Merge "Revert "Move RingtoneTest to media/tests/ringtone"" into main
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index 864caf4..605ce22 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}",
@@ -32,17 +33,23 @@
":android.companion.virtual.flags-aconfig-java{.generated_srcjars}",
":android.view.inputmethod.flags-aconfig-java{.generated_srcjars}",
":android.widget.flags-aconfig-java{.generated_srcjars}",
+ ":com.android.media.audio.flags-aconfig-java{.generated_srcjars}",
":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}",
+ ":aconfig_midi_flags_java_lib{.generated_srcjars}",
":android.service.autofill.flags-aconfig-java{.generated_srcjars}",
+ ":com.android.net.flags-aconfig-java{.generated_srcjars}",
]
filegroup {
@@ -292,6 +299,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",
@@ -305,6 +319,13 @@
defaults: ["framework-minus-apex-aconfig-java-defaults"],
}
+// Media Audio
+java_aconfig_library {
+ name: "com.android.media.audio.flags-aconfig-java",
+ aconfig_declarations: "aconfig_audio_flags",
+ defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
+
// Permissions
aconfig_declarations {
name: "android.permission.flags-aconfig",
@@ -316,6 +337,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
@@ -338,6 +377,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",
@@ -345,6 +390,12 @@
defaults: ["framework-minus-apex-aconfig-java-defaults"],
}
+java_aconfig_library {
+ name: "com.android.internal.foldables.flags-aconfig-java",
+ aconfig_declarations: "fold_lock_setting_flags",
+ defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
+
// Multi user
aconfig_declarations {
name: "android.multiuser.flags-aconfig",
@@ -429,7 +480,7 @@
package: "android.service.autofill",
srcs: [
"services/autofill/bugfixes.aconfig",
- "services/autofill/features.aconfig"
+ "services/autofill/features.aconfig",
],
}
@@ -438,3 +489,23 @@
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..0c199a6 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",
@@ -226,7 +227,6 @@
"android.hardware.radio.messaging-V3-java",
"android.hardware.radio.modem-V3-java",
"android.hardware.radio.network-V3-java",
- "android.hardware.radio.satellite-V1-java",
"android.hardware.radio.sim-V3-java",
"android.hardware.radio.voice-V3-java",
"android.hardware.thermal-V1.0-java-constants",
@@ -284,6 +284,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 +410,6 @@
"audiopolicy-aidl-java",
"sounddose-aidl-java",
"modules-utils-expresslog",
- "hoststubgen-annotations",
],
}
@@ -838,4 +838,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..da02298
--- /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",
+ defaults: ["hoststubgen-for-prototype-only-genrule"],
+ cmd: "cp $(in) $(out)",
+ srcs: [
+ ":framework-minus-apex.ravenwood-base{ravenwood.jar}",
+ ],
+ out: [
+ "framework-minus-apex.ravenwood.jar",
+ ],
+}
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobParameters.java b/apex/jobscheduler/framework/java/android/app/job/JobParameters.java
index f1403bd5..e833bb9 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobParameters.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobParameters.java
@@ -439,7 +439,7 @@
* provides an easy way to tell whether the job is being executed due to the deadline
* expiring. Note: If the job is running because its deadline expired, it implies that its
* constraints will not be met. However,
- * {@link android.app.job.JobInfo.Builder#setPeriodic(boolean) periodic jobs} will only ever
+ * {@link android.app.job.JobInfo.Builder#setPeriodic(long) periodic jobs} will only ever
* run when their constraints are satisfied, therefore, the constraints will still be satisfied
* for a periodic job even if the deadline has expired.
*/
diff --git a/apex/jobscheduler/service/aconfig/job.aconfig b/apex/jobscheduler/service/aconfig/job.aconfig
index 4e3cb7d..3e835b8 100644
--- a/apex/jobscheduler/service/aconfig/job.aconfig
+++ b/apex/jobscheduler/service/aconfig/job.aconfig
@@ -2,7 +2,7 @@
flag {
name: "relax_prefetch_connectivity_constraint_only_on_charger"
- namespace: "backstagepower"
+ 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/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/controllers/PrefetchController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/PrefetchController.java
index 2b7438c..fdeb072 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/PrefetchController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/PrefetchController.java
@@ -536,10 +536,13 @@
static final String KEY_LAUNCH_TIME_ALLOWANCE_MS =
PC_CONSTANT_PREFIX + "launch_time_allowance_ms";
- private static final long DEFAULT_LAUNCH_TIME_THRESHOLD_MS = 7 * HOUR_IN_MILLIS;
- private static final long DEFAULT_LAUNCH_TIME_ALLOWANCE_MS = 20 * MINUTE_IN_MILLIS;
+ private static final long DEFAULT_LAUNCH_TIME_THRESHOLD_MS = HOUR_IN_MILLIS;
+ private static final long DEFAULT_LAUNCH_TIME_ALLOWANCE_MS = 30 * MINUTE_IN_MILLIS;
- /** How much time each app will have to run jobs within their standby bucket window. */
+ /**
+ * The earliest amount of time before the next estimated app launch time that we may choose
+ * to run a prefetch job for the app.
+ */
public long LAUNCH_TIME_THRESHOLD_MS = DEFAULT_LAUNCH_TIME_THRESHOLD_MS;
/**
diff --git a/api/ApiDocs.bp b/api/ApiDocs.bp
index 8d8fc12..e162100 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,
}
@@ -160,7 +124,6 @@
"packages/modules/Media/apex/aidl/stable",
],
},
- extensions_info_file: ":sdk-extensions-info",
}
droidstubs {
@@ -168,13 +131,7 @@
defaults: ["framework-doc-stubs-sources-default"],
args: metalava_framework_docs_args +
" --show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.PRIVILEGED_APPS\\) ",
- api_levels_annotations_enabled: true,
- api_levels_annotations_dirs: [
- "sdk-dir",
- "api-versions-jars-dir",
- ],
- api_levels_sdk_type: "system",
- extensions_info_file: ":sdk-extensions-info",
+ api_levels_module: "api_versions_system",
}
/////////////////////////////////////////////////////////////////////
@@ -197,7 +154,7 @@
name: "framework-docs-default",
sdk_version: "none",
system_modules: "none",
- libs: framework_docs_only_libs + [
+ libs: [
"stub-annotations",
"unsupportedappusage",
],
@@ -236,20 +193,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 +246,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 +276,6 @@
droiddoc {
name: "ds-docs-kt",
- defaults: ["framework-dokka-docs-default"],
srcs: [
":framework-doc-stubs",
],
@@ -476,44 +354,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..7e41660 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: {
@@ -675,6 +695,7 @@
"api-stubs-docs-non-updatable.api.contribution",
],
visibility: ["//visibility:public"],
+ enable_validation: false,
}
java_api_library {
@@ -690,6 +711,7 @@
"system-api-stubs-docs-non-updatable.api.contribution",
],
visibility: ["//visibility:public"],
+ enable_validation: false,
}
java_api_library {
@@ -707,6 +729,7 @@
"test-api-stubs-docs-non-updatable.api.contribution",
],
visibility: ["//visibility:public"],
+ enable_validation: false,
}
java_api_library {
@@ -722,6 +745,7 @@
"api-stubs-docs-non-updatable.api.contribution",
"system-api-stubs-docs-non-updatable.api.contribution",
],
+ enable_validation: false,
}
java_api_library {
@@ -741,6 +765,7 @@
"module-lib-api-stubs-docs-non-updatable.api.contribution",
],
visibility: ["//visibility:public"],
+ enable_validation: false,
}
java_api_library {
@@ -754,6 +779,32 @@
"stub-annotations",
],
visibility: ["//visibility:public"],
+ enable_validation: false,
+}
+
+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",
+ ],
+ enable_validation: false,
}
java_api_library {
@@ -770,6 +821,7 @@
"android_module_lib_stubs_current.from-text",
],
visibility: ["//visibility:public"],
+ enable_validation: false,
}
////////////////////////////////////////////////////////////////////////
diff --git a/api/javadoc-lint-baseline b/api/javadoc-lint-baseline
index 2cc5078..d9e72b8 100644
--- a/api/javadoc-lint-baseline
+++ b/api/javadoc-lint-baseline
@@ -53,16 +53,6 @@
android/adservices/ondevicepersonalization/WebViewEventInput.java:30: lint: Unresolved link/see tag "android.adservices.ondevicepersonalization.IsolatedWorker#onExecute() IsolatedWorker#onExecute()" in android.adservices.ondevicepersonalization.WebViewEventInput [101]
android/adservices/ondevicepersonalization/WebViewEventInput.java:41: lint: Unresolved link/see tag "android.adservices.ondevicepersonalization.EventUrlProvider#createEventTrackingUrlWithResponse() EventUrlProvider#createEventTrackingUrlWithResponse()" in android.adservices.ondevicepersonalization.WebViewEventInput [101]
android/adservices/ondevicepersonalization/WebViewEventOutput.java:21: lint: Unresolved link/see tag "android.adservices.ondevicepersonalization.IsolatedWorker#onWebViewEvent() IsolatedWorker#onWebViewEvent()" in android.adservices.ondevicepersonalization.WebViewEventOutput [101]
-android/app/ActivityOptions.java:366: lint: Unresolved link/see tag "android.app.ComponentOptions.BackgroundActivityStartMode" in android.app.ActivityOptions [101]
-android/app/ActivityOptions.java:370: lint: Unresolved link/see tag "android.app.ComponentOptions.BackgroundActivityStartMode" in android.app.ActivityOptions [101]
-android/app/ActivityOptions.java:384: lint: Unresolved link/see tag "android.app.ComponentOptions.BackgroundActivityStartMode" in android.app.ActivityOptions [101]
-android/app/ApplicationStartInfo.java:96: lint: Unresolved link/see tag "#START_TIMESTAMP_JAVA_CLASSLOADING_COMPLETE" in android.app.ApplicationStartInfo [101]
-android/app/BroadcastOptions.java:132: lint: Unresolved link/see tag "#setDeliveryGroupMatchingFilter(android.content.IntentFilter)" in android.app.BroadcastOptions [101]
-android/app/GrammaticalInflectionManager.java:60: lint: Unresolved link/see tag "android.os.Environment#getDataSystemCeDirectory(int)" in android.app.GrammaticalInflectionManager [101]
-android/app/Notification.java:509: lint: Unresolved link/see tag "android.annotation.ColorInt ColorInt" in android.app.Notification [101]
-android/app/Notification.java:650: lint: Unresolved link/see tag "android.annotation.ColorInt ColorInt" in android.app.Notification [101]
-android/app/Notification.java:1866: lint: Unresolved link/see tag "/*missing*/" in android.app.Notification.Action [101]
-android/app/Notification.java:4796: lint: Unresolved link/see tag "android.content.pm.ShortcutInfo#setLongLived() ShortcutInfo#setLongLived()" in android.app.Notification.MessagingStyle [101]
android/app/admin/DevicePolicyManager.java:2670: lint: Unresolved link/see tag "android.os.UserManager#DISALLOW_CAMERA UserManager#DISALLOW_CAMERA" in android.app.admin.DevicePolicyManager [101]
android/app/admin/DevicePolicyManager.java:7257: lint: Unresolved link/see tag "android.app.admin.DevicePolicyIdentifiers#USB_DATA_SIGNALING_POLICY DevicePolicyIdentifiers#USB_DATA_SIGNALING_POLICY" in android.app.admin.DevicePolicyManager [101]
android/app/admin/DevicePolicyManager.java:7425: lint: Unresolved link/see tag "ACTION_DEVICE_FINANCING_STATE_CHANGED" in android.app.admin.DevicePolicyManager [101]
@@ -83,40 +73,6 @@
android/app/appsearch/SearchSpec.java:913: lint: Unresolved link/see tag "Features#NUMERIC_SEARCH" in android.app.appsearch.SearchSpec.Builder [101]
android/app/appsearch/SearchSpec.java:925: lint: Unresolved link/see tag "Features#VERBATIM_SEARCH" in android.app.appsearch.SearchSpec.Builder [101]
android/app/appsearch/SearchSpec.java:929: lint: Unresolved link/see tag "Features#LIST_FILTER_QUERY_LANGUAGE" in android.app.appsearch.SearchSpec.Builder [101]
-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]
android/hardware/camera2/CameraCharacteristics.java:2344: lint: Unresolved link/see tag "android.hardware.camera2.CameraDevice#concurrent-stream-guaranteed-configurations guideline" in android.hardware.camera2.CameraCharacteristics [101]
android/hardware/camera2/CameraCharacteristics.java:2344: lint: Unresolved link/see tag "android.hardware.camera2.CameraDevice#concurrent-stream-guaranteed-configurations tables" in android.hardware.camera2.CameraCharacteristics [101]
@@ -142,11 +98,6 @@
android/hardware/camera2/CaptureRequest.java:1501: lint: Unresolved link/see tag "SessionConfiguration#setSessionParameters" in android.hardware.camera2.CaptureRequest [101]
android/hardware/camera2/CaptureResult.java:923: lint: Unresolved link/see tag "SessionConfiguration#setSessionParameters" in android.hardware.camera2.CaptureResult [101]
android/hardware/camera2/CaptureResult.java:2337: lint: Unresolved link/see tag "SessionConfiguration#setSessionParameters" in android.hardware.camera2.CaptureResult [101]
-android/hardware/input/InputManager.java:215: lint: Unresolved link/see tag "android.hardware.input.InputManagerGlobal#getInputDevice InputManagerGlobal#getInputDevice" in android.hardware.input.InputManager.InputDeviceListener [101]
-android/inputmethodservice/AbstractInputMethodService.java:155: lint: Unresolved link/see tag "android.app.ActivityThread ActivityThread" in android.inputmethodservice.AbstractInputMethodService [101]
-android/inputmethodservice/InputMethodService.java:1078: lint: Unresolved link/see tag "android.widget.Editor" in android.inputmethodservice.InputMethodService [101]
-android/location/GnssSignalType.java:14: lint: Unresolved link/see tag "android.location.GnssStatus.ConstellationType GnssStatus.ConstellationType" in android.location.GnssSignalType [101]
-android/location/GnssSignalType.java:48: lint: Unresolved link/see tag "android.location.GnssStatus.ConstellationType GnssStatus.ConstellationType" in android.location.GnssSignalType [101]
android/media/AudioAttributes.java:443: lint: Unresolved link/see tag "android.media.AudioAttributes.AttributeSdkUsage#USAGE_ALARM AttributeSdkUsage#USAGE_ALARM" in android.media.AudioAttributes.Builder [101]
android/media/AudioAttributes.java:443: lint: Unresolved link/see tag "android.media.AudioAttributes.AttributeSdkUsage#USAGE_ASSISTANCE_ACCESSIBILITY AttributeSdkUsage#USAGE_ASSISTANCE_ACCESSIBILITY" in android.media.AudioAttributes.Builder [101]
android/media/AudioAttributes.java:443: lint: Unresolved link/see tag "android.media.AudioAttributes.AttributeSdkUsage#USAGE_ASSISTANCE_NAVIGATION_GUIDANCE AttributeSdkUsage#USAGE_ASSISTANCE_NAVIGATION_GUIDANCE" in android.media.AudioAttributes.Builder [101]
@@ -165,8 +116,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]
@@ -202,66 +151,8 @@
android/net/wifi/aware/SubscribeConfig.java:51: lint: Unresolved link/see tag "android.net.wifi.WifiScanner#WIFI_BAND_5_GHZ WifiScanner#WIFI_BAND_5_GHZ" in android.net.wifi.aware.SubscribeConfig [101]
android/net/wifi/aware/SubscribeConfig.java:276: lint: Unresolved link/see tag "android.net.wifi.WifiScanner#WIFI_BAND_24_GHZ WifiScanner#WIFI_BAND_24_GHZ" in android.net.wifi.aware.SubscribeConfig.Builder [101]
android/net/wifi/aware/SubscribeConfig.java:276: lint: Unresolved link/see tag "android.net.wifi.WifiScanner#WIFI_BAND_5_GHZ WifiScanner#WIFI_BAND_5_GHZ" in android.net.wifi.aware.SubscribeConfig.Builder [101]
-android/os/BugreportManager.java:146: lint: Unresolved link/see tag "android.os.BugreportParams#BUGREPORT_FLAG_DEFER_CONSENT BugreportParams#BUGREPORT_FLAG_DEFER_CONSENT" in android.os.BugreportManager.BugreportCallback [101]
-android/os/PowerManager.java:796: lint: Unresolved link/see tag "android.os.Temperature" in android.os.PowerManager.OnThermalStatusChangedListener [101]
-android/os/RemoteException.java:49: lint: Unresolved link/see tag "android.os.DeadSystemRuntimeException DeadSystemRuntimeException" in android.os.RemoteException [101]
-android/provider/Settings.java:374: lint: Unresolved link/see tag "android.credentials.CredentialManager#isEnabledCredentialProviderService()" in android.provider.Settings [101]
-android/provider/Settings.java:908: lint: Unresolved link/see tag "android.service.notification.NotificationAssistantService" in android.provider.Settings [101]
-android/provider/Settings.java:2181: lint: Unresolved link/see tag "android.app.time.TimeManager" in android.provider.Settings.Global [101]
-android/provider/Settings.java:2195: lint: Unresolved link/see tag "android.app.time.TimeManager" in android.provider.Settings.Global [101]
-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/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]
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]
@@ -269,21 +160,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/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]
@@ -291,13 +172,3 @@
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/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 8974daa..f6564ec 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -3286,10 +3286,10 @@
public abstract class AccessibilityService extends android.app.Service {
ctor public AccessibilityService();
- method @Deprecated public void attachAccessibilityOverlayToDisplay(int, @NonNull android.view.SurfaceControl);
- method public void attachAccessibilityOverlayToDisplay(int, @NonNull android.view.SurfaceControl, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.IntConsumer);
- method @Deprecated public void attachAccessibilityOverlayToWindow(int, @NonNull android.view.SurfaceControl);
- method public void attachAccessibilityOverlayToWindow(int, @NonNull android.view.SurfaceControl, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.IntConsumer);
+ method public void attachAccessibilityOverlayToDisplay(int, @NonNull android.view.SurfaceControl);
+ method @FlaggedApi("android.view.accessibility.a11y_overlay_callbacks") public void attachAccessibilityOverlayToDisplay(int, @NonNull android.view.SurfaceControl, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.IntConsumer);
+ method public void attachAccessibilityOverlayToWindow(int, @NonNull android.view.SurfaceControl);
+ method @FlaggedApi("android.view.accessibility.a11y_overlay_callbacks") public void attachAccessibilityOverlayToWindow(int, @NonNull android.view.SurfaceControl, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.IntConsumer);
method public boolean clearCache();
method public boolean clearCachedSubtree(@NonNull android.view.accessibility.AccessibilityNodeInfo);
method public final void disableSelf();
@@ -3401,9 +3401,9 @@
field public static final int GLOBAL_ACTION_RECENTS = 3; // 0x3
field public static final int GLOBAL_ACTION_TAKE_SCREENSHOT = 9; // 0x9
field public static final int GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN = 7; // 0x7
- field public static final int OVERLAY_RESULT_INTERNAL_ERROR = 1; // 0x1
- field public static final int OVERLAY_RESULT_INVALID = 2; // 0x2
- field public static final int OVERLAY_RESULT_SUCCESS = 0; // 0x0
+ field @FlaggedApi("android.view.accessibility.a11y_overlay_callbacks") public static final int OVERLAY_RESULT_INTERNAL_ERROR = 1; // 0x1
+ field @FlaggedApi("android.view.accessibility.a11y_overlay_callbacks") public static final int OVERLAY_RESULT_INVALID = 2; // 0x2
+ field @FlaggedApi("android.view.accessibility.a11y_overlay_callbacks") public static final int OVERLAY_RESULT_SUCCESS = 0; // 0x0
field public static final String SERVICE_INTERFACE = "android.accessibilityservice.AccessibilityService";
field public static final String SERVICE_META_DATA = "android.accessibilityservice";
field public static final int SHOW_MODE_AUTO = 0; // 0x0
@@ -5739,6 +5739,7 @@
ctor @Deprecated public FragmentBreadCrumbs(android.content.Context, android.util.AttributeSet);
ctor @Deprecated public FragmentBreadCrumbs(android.content.Context, android.util.AttributeSet, int);
method @Deprecated public void onBackStackChanged();
+ method @Deprecated protected void onLayout(boolean, int, int, int, int);
method @Deprecated public void setActivity(android.app.Activity);
method @Deprecated public void setMaxVisible(int);
method @Deprecated public void setOnBreadCrumbClickListener(android.app.FragmentBreadCrumbs.OnBreadCrumbClickListener);
@@ -9683,7 +9684,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;
@@ -9787,7 +9788,7 @@
method @NonNull public android.content.AttributionSource.Builder setAttributionTag(@Nullable String);
method @FlaggedApi("android.permission.flags.device_aware_permission_apis") @NonNull public android.content.AttributionSource.Builder setDeviceId(int);
method @Deprecated @NonNull public android.content.AttributionSource.Builder setNext(@Nullable android.content.AttributionSource);
- method @NonNull public android.content.AttributionSource.Builder setNextAttributionSource(@NonNull android.content.AttributionSource);
+ method @FlaggedApi("android.permission.flags.set_next_attribution_source") @NonNull public android.content.AttributionSource.Builder setNextAttributionSource(@NonNull android.content.AttributionSource);
method @NonNull public android.content.AttributionSource.Builder setPackageName(@Nullable String);
method @NonNull public android.content.AttributionSource.Builder setPid(int);
}
@@ -10992,6 +10993,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 +12675,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);
@@ -13649,6 +13652,7 @@
public interface XmlResourceParser extends org.xmlpull.v1.XmlPullParser android.util.AttributeSet java.lang.AutoCloseable {
method public void close();
+ method public String getAttributeNamespace(int);
}
}
@@ -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();
@@ -20399,7 +20405,7 @@
method @Deprecated public boolean isPreviewEnabled();
method @Deprecated public boolean isProximityCorrectionEnabled();
method @Deprecated public boolean isShifted();
- method public void onClick(android.view.View);
+ method @Deprecated public void onClick(android.view.View);
method @Deprecated public void onDetachedFromWindow();
method @Deprecated public void onDraw(android.graphics.Canvas);
method @Deprecated protected boolean onLongPress(android.inputmethodservice.Keyboard.Key);
@@ -24226,7 +24232,7 @@
@Deprecated public class RemoteControlClient.MetadataEditor extends android.media.MediaMetadataEditor {
method @Deprecated public void apply();
- method public Object clone() throws java.lang.CloneNotSupportedException;
+ method @Deprecated public Object clone() throws java.lang.CloneNotSupportedException;
method @Deprecated public android.media.RemoteControlClient.MetadataEditor putBitmap(int, android.graphics.Bitmap) throws java.lang.IllegalArgumentException;
method @Deprecated public android.media.RemoteControlClient.MetadataEditor putLong(int, long) throws java.lang.IllegalArgumentException;
method @Deprecated public android.media.RemoteControlClient.MetadataEditor putObject(int, Object) throws java.lang.IllegalArgumentException;
@@ -25781,15 +25787,15 @@
method public abstract void onDisconnect(android.media.midi.MidiReceiver);
}
- public abstract class MidiUmpDeviceService extends android.app.Service {
+ @FlaggedApi("com.android.media.midi.flags.virtual_ump") public abstract class MidiUmpDeviceService extends android.app.Service {
ctor public MidiUmpDeviceService();
- method @Nullable public final android.media.midi.MidiDeviceInfo getDeviceInfo();
- method @NonNull public final java.util.List<android.media.midi.MidiReceiver> getOutputPortReceivers();
- method @Nullable public android.os.IBinder onBind(@NonNull android.content.Intent);
- method public void onClose();
- method public void onDeviceStatusChanged(@NonNull android.media.midi.MidiDeviceStatus);
- method @NonNull public abstract java.util.List<android.media.midi.MidiReceiver> onGetInputPortReceivers();
- field public static final String SERVICE_INTERFACE = "android.media.midi.MidiUmpDeviceService";
+ method @FlaggedApi("com.android.media.midi.flags.virtual_ump") @Nullable public final android.media.midi.MidiDeviceInfo getDeviceInfo();
+ method @FlaggedApi("com.android.media.midi.flags.virtual_ump") @NonNull public final java.util.List<android.media.midi.MidiReceiver> getOutputPortReceivers();
+ method @FlaggedApi("com.android.media.midi.flags.virtual_ump") @Nullable public android.os.IBinder onBind(@NonNull android.content.Intent);
+ method @FlaggedApi("com.android.media.midi.flags.virtual_ump") public void onClose();
+ method @FlaggedApi("com.android.media.midi.flags.virtual_ump") public void onDeviceStatusChanged(@NonNull android.media.midi.MidiDeviceStatus);
+ method @FlaggedApi("com.android.media.midi.flags.virtual_ump") @NonNull public abstract java.util.List<android.media.midi.MidiReceiver> onGetInputPortReceivers();
+ field @FlaggedApi("com.android.media.midi.flags.virtual_ump") public static final String SERVICE_INTERFACE = "android.media.midi.MidiUmpDeviceService";
}
}
@@ -27090,6 +27096,7 @@
method @NonNull public java.util.List<android.media.AudioPresentation> getAudioPresentations();
method public String getSelectedTrack(int);
method public java.util.List<android.media.tv.TvTrackInfo> getTracks(int);
+ method protected void onLayout(boolean, int, int, int, int);
method public boolean onUnhandledInputEvent(android.view.InputEvent);
method public void overrideTvAppAttributionSource(@NonNull android.content.AttributionSource);
method public void reset();
@@ -28563,6 +28570,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";
@@ -31972,6 +31981,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
@@ -33085,6 +33095,22 @@
method public void onStateChanged(boolean);
}
+ @FlaggedApi("com.android.server.power.optimization.power_monitor_api") public final class PowerMonitor implements android.os.Parcelable {
+ method @FlaggedApi("com.android.server.power.optimization.power_monitor_api") public int describeContents();
+ method @FlaggedApi("com.android.server.power.optimization.power_monitor_api") @NonNull public String getName();
+ method @FlaggedApi("com.android.server.power.optimization.power_monitor_api") public int getType();
+ method @FlaggedApi("com.android.server.power.optimization.power_monitor_api") public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @FlaggedApi("com.android.server.power.optimization.power_monitor_api") @NonNull public static final android.os.Parcelable.Creator<android.os.PowerMonitor> CREATOR;
+ field @FlaggedApi("com.android.server.power.optimization.power_monitor_api") public static final int POWER_MONITOR_TYPE_CONSUMER = 0; // 0x0
+ field @FlaggedApi("com.android.server.power.optimization.power_monitor_api") public static final int POWER_MONITOR_TYPE_MEASUREMENT = 1; // 0x1
+ }
+
+ @FlaggedApi("com.android.server.power.optimization.power_monitor_api") public final class PowerMonitorReadings {
+ method @FlaggedApi("com.android.server.power.optimization.power_monitor_api") public long getConsumedEnergy(@NonNull android.os.PowerMonitor);
+ method @FlaggedApi("com.android.server.power.optimization.power_monitor_api") public long getTimestamp(@NonNull android.os.PowerMonitor);
+ field @FlaggedApi("com.android.server.power.optimization.power_monitor_api") public static final int ENERGY_UNAVAILABLE = -1; // 0xffffffff
+ }
+
public class Process {
ctor public Process();
method public static final long getElapsedCpuTime();
@@ -33647,6 +33673,8 @@
}
public class SystemHealthManager {
+ method @FlaggedApi("com.android.server.power.optimization.power_monitor_api") public void getPowerMonitorReadings(@NonNull java.util.List<android.os.PowerMonitor>, @Nullable android.os.Handler, @NonNull java.util.function.Consumer<android.os.PowerMonitorReadings>, @NonNull java.util.function.Consumer<java.lang.RuntimeException>);
+ method @FlaggedApi("com.android.server.power.optimization.power_monitor_api") public void getSupportedPowerMonitors(@Nullable android.os.Handler, @NonNull java.util.function.Consumer<java.util.List<android.os.PowerMonitor>>);
method public android.os.health.HealthStats takeMyUidSnapshot();
method public android.os.health.HealthStats takeUidSnapshot(int);
method public android.os.health.HealthStats[] takeUidSnapshots(int[]);
@@ -41555,7 +41583,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();
@@ -43522,7 +43550,6 @@
method public int getLongitude();
method public int getNetworkId();
method public int getSystemId();
- method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.CellIdentityCdma> CREATOR;
}
@@ -43538,7 +43565,6 @@
method @Nullable public String getMncString();
method @Nullable public String getMobileNetworkOperator();
method @Deprecated public int getPsc();
- method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.CellIdentityGsm> CREATOR;
}
@@ -43556,7 +43582,6 @@
method @Nullable public String getMobileNetworkOperator();
method public int getPci();
method public int getTac();
- method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.CellIdentityLte> CREATOR;
}
@@ -43569,7 +43594,6 @@
method @IntRange(from=0, to=3279165) public int getNrarfcn();
method @IntRange(from=0, to=1007) public int getPci();
method @IntRange(from=0, to=16777215) public int getTac();
- method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.CellIdentityNr> CREATOR;
}
@@ -43583,7 +43607,6 @@
method @Nullable public String getMncString();
method @Nullable public String getMobileNetworkOperator();
method public int getUarfcn();
- method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.CellIdentityTdscdma> CREATOR;
}
@@ -43599,7 +43622,6 @@
method @Nullable public String getMobileNetworkOperator();
method public int getPsc();
method public int getUarfcn();
- method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.CellIdentityWcdma> CREATOR;
}
@@ -43683,6 +43705,7 @@
public final class CellSignalStrengthCdma extends android.telephony.CellSignalStrength implements android.os.Parcelable {
method public int describeContents();
+ method public boolean equals(Object);
method public int getAsuLevel();
method public int getCdmaDbm();
method public int getCdmaEcio();
@@ -43693,24 +43716,28 @@
method public int getEvdoLevel();
method public int getEvdoSnr();
method @IntRange(from=android.telephony.CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN, to=android.telephony.CellSignalStrength.SIGNAL_STRENGTH_GREAT) public int getLevel();
+ method public int hashCode();
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.CellSignalStrengthCdma> CREATOR;
}
public final class CellSignalStrengthGsm extends android.telephony.CellSignalStrength implements android.os.Parcelable {
method public int describeContents();
+ method public boolean equals(Object);
method public int getAsuLevel();
method public int getBitErrorRate();
method public int getDbm();
method @IntRange(from=android.telephony.CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN, to=android.telephony.CellSignalStrength.SIGNAL_STRENGTH_GREAT) public int getLevel();
method public int getRssi();
method public int getTimingAdvance();
+ method public int hashCode();
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.CellSignalStrengthGsm> CREATOR;
}
public final class CellSignalStrengthLte extends android.telephony.CellSignalStrength implements android.os.Parcelable {
method public int describeContents();
+ method public boolean equals(Object);
method public int getAsuLevel();
method @IntRange(from=0, to=15) public int getCqi();
method @IntRange(from=1, to=6) public int getCqiTableIndex();
@@ -43721,12 +43748,14 @@
method public int getRssi();
method public int getRssnr();
method public int getTimingAdvance();
+ method public int hashCode();
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.CellSignalStrengthLte> CREATOR;
}
public final class CellSignalStrengthNr extends android.telephony.CellSignalStrength implements android.os.Parcelable {
method public int describeContents();
+ method public boolean equals(Object);
method public int getAsuLevel();
method @IntRange(from=0, to=15) @NonNull public java.util.List<java.lang.Integer> getCsiCqiReport();
method @IntRange(from=1, to=3) public int getCsiCqiTableIndex();
@@ -43739,26 +43768,31 @@
method public int getSsRsrq();
method public int getSsSinr();
method @IntRange(from=0, to=1282) public int getTimingAdvanceMicros();
+ method public int hashCode();
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.CellSignalStrengthNr> CREATOR;
}
public final class CellSignalStrengthTdscdma extends android.telephony.CellSignalStrength implements android.os.Parcelable {
method public int describeContents();
+ method public boolean equals(Object);
method public int getAsuLevel();
method public int getDbm();
method @IntRange(from=0, to=4) public int getLevel();
method public int getRscp();
+ method public int hashCode();
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.CellSignalStrengthTdscdma> CREATOR;
}
public final class CellSignalStrengthWcdma extends android.telephony.CellSignalStrength implements android.os.Parcelable {
method public int describeContents();
+ method public boolean equals(Object);
method public int getAsuLevel();
method public int getDbm();
method public int getEcNo();
method @IntRange(from=android.telephony.CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN, to=android.telephony.CellSignalStrength.SIGNAL_STRENGTH_GREAT) public int getLevel();
+ method public int hashCode();
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.CellSignalStrengthWcdma> CREATOR;
}
@@ -44214,7 +44248,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
@@ -44323,7 +44357,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();
@@ -44339,7 +44373,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
@@ -44554,7 +44588,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);
@@ -45340,7 +45374,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";
@@ -46546,6 +46580,7 @@
method @Deprecated public int length();
method @Deprecated public static android.text.AlteredCharSequence make(CharSequence, char[], int, int);
method @Deprecated public CharSequence subSequence(int, int);
+ method @Deprecated public String toString();
}
@Deprecated public class AndroidCharacter {
@@ -46673,10 +46708,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);
}
@@ -46997,6 +47032,7 @@
method public void removeSpan(Object);
method public void setSpan(Object, int, int, int);
method public CharSequence subSequence(int, int);
+ method public String toString();
}
public static final class PrecomputedText.Params {
@@ -47126,6 +47162,7 @@
method public void setFilters(android.text.InputFilter[]);
method public void setSpan(Object, int, int, int);
method public CharSequence subSequence(int, int);
+ method public String toString();
method public static android.text.SpannableStringBuilder valueOf(CharSequence);
}
@@ -47200,7 +47237,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);
}
@@ -48766,7 +48803,9 @@
method public boolean containsValue(Object);
method public void ensureCapacity(int);
method public java.util.Set<java.util.Map.Entry<K,V>> entrySet();
+ method public boolean equals(@Nullable Object);
method public V get(Object);
+ method public int hashCode();
method public int indexOfKey(Object);
method public int indexOfValue(Object);
method public boolean isEmpty();
@@ -48798,7 +48837,9 @@
method public boolean contains(Object);
method public boolean containsAll(java.util.Collection<?>);
method public void ensureCapacity(int);
+ method public boolean equals(@Nullable Object);
method public void forEach(java.util.function.Consumer<? super E>);
+ method public int hashCode();
method public int indexOf(Object);
method public boolean isEmpty();
method public java.util.Iterator<E> iterator();
@@ -53120,6 +53161,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();
@@ -53189,6 +53231,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);
@@ -53538,6 +53581,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();
@@ -53547,6 +53591,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);
@@ -54657,7 +54702,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();
@@ -54684,7 +54728,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);
@@ -54705,10 +54748,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);
@@ -55160,12 +55199,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);
}
@@ -57541,6 +57578,7 @@
ctor @Deprecated public AbsoluteLayout(android.content.Context, android.util.AttributeSet);
ctor @Deprecated public AbsoluteLayout(android.content.Context, android.util.AttributeSet, int);
ctor @Deprecated public AbsoluteLayout(android.content.Context, android.util.AttributeSet, int, int);
+ method @Deprecated protected void onLayout(boolean, int, int, int, int);
}
@Deprecated public static class AbsoluteLayout.LayoutParams extends android.view.ViewGroup.LayoutParams {
@@ -57619,6 +57657,7 @@
method public long getSelectedItemId();
method public int getSelectedItemPosition();
method public abstract android.view.View getSelectedView();
+ method protected void onLayout(boolean, int, int, int, int);
method public boolean performItemClick(android.view.View, int, long);
method public abstract void setAdapter(T);
method public void setEmptyView(android.view.View);
@@ -58265,6 +58304,7 @@
method public android.widget.FrameLayout.LayoutParams generateLayoutParams(android.util.AttributeSet);
method @Deprecated public boolean getConsiderGoneChildrenWhenMeasuring();
method public boolean getMeasureAllChildren();
+ method protected void onLayout(boolean, int, int, int, int);
method public void setMeasureAllChildren(boolean);
}
@@ -58318,6 +58358,7 @@
method public boolean getUseDefaultMargins();
method public boolean isColumnOrderPreserved();
method public boolean isRowOrderPreserved();
+ method protected void onLayout(boolean, int, int, int, int);
method public void setAlignmentMode(int);
method public void setColumnCount(int);
method public void setColumnOrderPreserved(boolean);
@@ -58540,6 +58581,7 @@
method public float getWeightSum();
method public boolean isBaselineAligned();
method public boolean isMeasureWithLargestChildEnabled();
+ method protected void onLayout(boolean, int, int, int, int);
method public void setBaselineAligned(boolean);
method public void setBaselineAlignedChildIndex(int);
method public void setDividerDrawable(android.graphics.drawable.Drawable);
@@ -59088,6 +59130,7 @@
method public android.widget.RelativeLayout.LayoutParams generateLayoutParams(android.util.AttributeSet);
method public int getGravity();
method public int getIgnoreGravity();
+ method protected void onLayout(boolean, int, int, int, int);
method public void setGravity(int);
method public void setHorizontalGravity(int);
method public void setIgnoreGravity(int);
@@ -59542,6 +59585,7 @@
method @Deprecated public boolean isMoving();
method @Deprecated public boolean isOpened();
method @Deprecated public void lock();
+ method @Deprecated protected void onLayout(boolean, int, int, int, int);
method @Deprecated public void open();
method @Deprecated public void setOnDrawerCloseListener(android.widget.SlidingDrawer.OnDrawerCloseListener);
method @Deprecated public void setOnDrawerOpenListener(android.widget.SlidingDrawer.OnDrawerOpenListener);
@@ -60188,6 +60232,7 @@
method public boolean hideOverflowMenu();
method public void inflateMenu(@MenuRes int);
method public boolean isOverflowMenuShowing();
+ method protected void onLayout(boolean, int, int, int, int);
method public void setCollapseContentDescription(@StringRes int);
method public void setCollapseContentDescription(@Nullable CharSequence);
method public void setCollapseIcon(@DrawableRes int);
@@ -60342,7 +60387,7 @@
method @Deprecated public android.view.View getZoomControls();
method @Deprecated public boolean isAutoDismissed();
method @Deprecated public boolean isVisible();
- method public boolean onTouch(android.view.View, android.view.MotionEvent);
+ method @Deprecated public boolean onTouch(android.view.View, android.view.MotionEvent);
method @Deprecated public void setAutoDismissed(boolean);
method @Deprecated public void setFocusable(boolean);
method @Deprecated public void setOnZoomListener(android.widget.ZoomButtonsController.OnZoomListener);
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index 052d614..b5d3ed7 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -6,9 +6,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/system-current.txt b/core/api/system-current.txt
index a99eeb0..c72d09d 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -297,6 +297,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";
@@ -310,7 +312,7 @@
field public static final String REMOVE_DRM_CERTIFICATES = "android.permission.REMOVE_DRM_CERTIFICATES";
field public static final String REMOVE_TASKS = "android.permission.REMOVE_TASKS";
field public static final String RENOUNCE_PERMISSIONS = "android.permission.RENOUNCE_PERMISSIONS";
- field public static final String REPORT_USAGE_STATS = "android.permission.REPORT_USAGE_STATS";
+ field @FlaggedApi("backstage_power.report_usage_stats_permission") public static final String REPORT_USAGE_STATS = "android.permission.REPORT_USAGE_STATS";
field @Deprecated public static final String REQUEST_NETWORK_SCORES = "android.permission.REQUEST_NETWORK_SCORES";
field public static final String REQUEST_NOTIFICATION_ASSISTANT_SERVICE = "android.permission.REQUEST_NOTIFICATION_ASSISTANT_SERVICE";
field public static final String RESET_PASSWORD = "android.permission.RESET_PASSWORD";
@@ -652,7 +654,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 +3210,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);
@@ -3554,7 +3555,7 @@
field public static final String ACTION_SHOW_SUSPENDED_APP_DETAILS = "android.intent.action.SHOW_SUSPENDED_APP_DETAILS";
field @Deprecated public static final String ACTION_SIM_STATE_CHANGED = "android.intent.action.SIM_STATE_CHANGED";
field public static final String ACTION_SPLIT_CONFIGURATION_CHANGED = "android.intent.action.SPLIT_CONFIGURATION_CHANGED";
- field public static final String ACTION_UNARCHIVE_PACKAGE = "android.intent.action.UNARCHIVE_PACKAGE";
+ field @FlaggedApi("android.content.pm.archiving") public static final String ACTION_UNARCHIVE_PACKAGE = "android.intent.action.UNARCHIVE_PACKAGE";
field public static final String ACTION_UPGRADE_SETUP = "android.intent.action.UPGRADE_SETUP";
field public static final String ACTION_USER_ADDED = "android.intent.action.USER_ADDED";
field public static final String ACTION_USER_REMOVED = "android.intent.action.USER_REMOVED";
@@ -4027,7 +4028,7 @@
field @Deprecated public static final int INTENT_FILTER_VERIFICATION_SUCCESS = 1; // 0x1
field @Deprecated public static final int MASK_PERMISSION_FLAGS = 255; // 0xff
field public static final int MATCH_ANY_USER = 4194304; // 0x400000
- field public static final long MATCH_ARCHIVED_PACKAGES = 4294967296L; // 0x100000000L
+ field @FlaggedApi("android.content.pm.archiving") public static final long MATCH_ARCHIVED_PACKAGES = 4294967296L; // 0x100000000L
field public static final int MATCH_CLONE_PROFILE = 536870912; // 0x20000000
field public static final int MATCH_FACTORY_ONLY = 2097152; // 0x200000
field public static final int MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS = 536870912; // 0x20000000
@@ -4058,7 +4059,9 @@
}
public static final class PackageManager.UninstallCompleteCallback implements android.os.Parcelable {
+ method public int describeContents();
method public void onUninstallComplete(@NonNull String, int, @Nullable String);
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.PackageManager.UninstallCompleteCallback> CREATOR;
}
@@ -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
}
@@ -5958,6 +5961,7 @@
public static final class SoundTrigger.Keyphrase implements android.os.Parcelable {
ctor public SoundTrigger.Keyphrase(int, int, @NonNull java.util.Locale, @NonNull String, @Nullable int[]);
+ method public int describeContents();
method public int getId();
method @NonNull public java.util.Locale getLocale();
method public int getRecognitionModes();
@@ -5980,6 +5984,7 @@
public static final class SoundTrigger.KeyphraseSoundModel extends android.hardware.soundtrigger.SoundTrigger.SoundModel implements android.os.Parcelable {
ctor public SoundTrigger.KeyphraseSoundModel(@NonNull java.util.UUID, @NonNull java.util.UUID, @Nullable byte[], @Nullable android.hardware.soundtrigger.SoundTrigger.Keyphrase[], int);
ctor public SoundTrigger.KeyphraseSoundModel(@NonNull java.util.UUID, @NonNull java.util.UUID, @Nullable byte[], @Nullable android.hardware.soundtrigger.SoundTrigger.Keyphrase[]);
+ method public int describeContents();
method @NonNull public android.hardware.soundtrigger.SoundTrigger.Keyphrase[] getKeyphrases();
method @NonNull public static android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel readFromParcel(@NonNull android.os.Parcel);
method public void writeToParcel(@NonNull android.os.Parcel, int);
@@ -5987,6 +5992,7 @@
}
public static final class SoundTrigger.ModelParamRange implements android.os.Parcelable {
+ method public int describeContents();
method public int getEnd();
method public int getStart();
method public void writeToParcel(@NonNull android.os.Parcel, int);
@@ -6714,7 +6720,7 @@
method public boolean setUidDeviceAffinity(int, @NonNull java.util.List<android.media.AudioDeviceInfo>);
method public boolean setUserIdDeviceAffinity(int, @NonNull java.util.List<android.media.AudioDeviceInfo>);
method public String toLogFriendlyString();
- method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int updateMixingRules(@NonNull java.util.List<android.util.Pair<android.media.audiopolicy.AudioMix,android.media.audiopolicy.AudioMixingRule>>);
+ method @FlaggedApi("com.android.media.audio.flags.audio_policy_update_mixing_rules_api") @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int updateMixingRules(@NonNull java.util.List<android.util.Pair<android.media.audiopolicy.AudioMix,android.media.audiopolicy.AudioMixingRule>>);
field public static final int FOCUS_POLICY_DUCKING_DEFAULT = 0; // 0x0
field public static final int FOCUS_POLICY_DUCKING_IN_APP = 0; // 0x0
field public static final int FOCUS_POLICY_DUCKING_IN_POLICY = 1; // 0x1
@@ -6797,6 +6803,7 @@
public abstract class MusicRecognitionService extends android.app.Service {
ctor public MusicRecognitionService();
+ method @Nullable public android.os.IBinder onBind(@NonNull android.content.Intent);
method public abstract void onRecognize(@NonNull android.os.ParcelFileDescriptor, @NonNull android.media.AudioFormat, @NonNull android.media.musicrecognition.MusicRecognitionService.Callback);
}
@@ -6855,6 +6862,7 @@
public abstract class SoundTriggerDetectionService extends android.app.Service {
ctor public SoundTriggerDetectionService();
+ method public final android.os.IBinder onBind(android.content.Intent);
method @MainThread public void onConnected(@NonNull java.util.UUID, @Nullable android.os.Bundle);
method @MainThread public void onDisconnected(@NonNull java.util.UUID, @Nullable android.os.Bundle);
method @MainThread public void onError(@NonNull java.util.UUID, @Nullable android.os.Bundle, int, int);
@@ -7568,6 +7576,7 @@
}
public class MediaEvent extends android.media.tv.tuner.filter.FilterEvent {
+ method protected void finalize();
method public long getAudioHandle();
method @NonNull public java.util.List<android.media.AudioPresentation> getAudioPresentations();
method public long getAvDataId();
@@ -8962,6 +8971,8 @@
package android.net.metrics {
@Deprecated public final class ApfProgramEvent implements android.net.metrics.IpConnectivityLog.Event {
+ method @Deprecated public int describeContents();
+ method @Deprecated public void writeToParcel(android.os.Parcel, int);
}
@Deprecated public static final class ApfProgramEvent.Builder {
@@ -8976,6 +8987,8 @@
}
@Deprecated public final class ApfStats implements android.net.metrics.IpConnectivityLog.Event {
+ method @Deprecated public int describeContents();
+ method @Deprecated public void writeToParcel(android.os.Parcel, int);
}
@Deprecated public static final class ApfStats.Builder {
@@ -8994,6 +9007,8 @@
}
@Deprecated public final class DhcpClientEvent implements android.net.metrics.IpConnectivityLog.Event {
+ method @Deprecated public int describeContents();
+ method @Deprecated public void writeToParcel(android.os.Parcel, int);
}
@Deprecated public static final class DhcpClientEvent.Builder {
@@ -9005,7 +9020,9 @@
@Deprecated public final class DhcpErrorEvent implements android.net.metrics.IpConnectivityLog.Event {
ctor @Deprecated public DhcpErrorEvent(int);
+ method @Deprecated public int describeContents();
method @Deprecated public static int errorCodeWithOption(int, int);
+ method @Deprecated public void writeToParcel(android.os.Parcel, int);
field @Deprecated public static final int BOOTP_TOO_SHORT = 67174400; // 0x4010000
field @Deprecated public static final int BUFFER_UNDERFLOW = 83951616; // 0x5010000
field @Deprecated public static final int DHCP_BAD_MAGIC_COOKIE = 67239936; // 0x4020000
@@ -9043,6 +9060,8 @@
@Deprecated public final class IpManagerEvent implements android.net.metrics.IpConnectivityLog.Event {
ctor @Deprecated public IpManagerEvent(int, long);
+ method @Deprecated public int describeContents();
+ method @Deprecated public void writeToParcel(android.os.Parcel, int);
field @Deprecated public static final int COMPLETE_LIFECYCLE = 3; // 0x3
field @Deprecated public static final int ERROR_INTERFACE_NOT_FOUND = 8; // 0x8
field @Deprecated public static final int ERROR_INVALID_PROVISIONING = 7; // 0x7
@@ -9055,6 +9074,8 @@
@Deprecated public final class IpReachabilityEvent implements android.net.metrics.IpConnectivityLog.Event {
ctor @Deprecated public IpReachabilityEvent(int);
+ method @Deprecated public int describeContents();
+ method @Deprecated public void writeToParcel(android.os.Parcel, int);
field @Deprecated public static final int NUD_FAILED = 512; // 0x200
field @Deprecated public static final int NUD_FAILED_ORGANIC = 1024; // 0x400
field @Deprecated public static final int PROBE = 256; // 0x100
@@ -9065,6 +9086,8 @@
@Deprecated public final class NetworkEvent implements android.net.metrics.IpConnectivityLog.Event {
ctor @Deprecated public NetworkEvent(int, long);
ctor @Deprecated public NetworkEvent(int);
+ method @Deprecated public int describeContents();
+ method @Deprecated public void writeToParcel(android.os.Parcel, int);
field @Deprecated public static final int NETWORK_CAPTIVE_PORTAL_FOUND = 4; // 0x4
field @Deprecated public static final int NETWORK_CONNECTED = 1; // 0x1
field @Deprecated public static final int NETWORK_CONSECUTIVE_DNS_TIMEOUT_FOUND = 12; // 0xc
@@ -9081,6 +9104,8 @@
}
@Deprecated public final class RaEvent implements android.net.metrics.IpConnectivityLog.Event {
+ method @Deprecated public int describeContents();
+ method @Deprecated public void writeToParcel(android.os.Parcel, int);
}
@Deprecated public static final class RaEvent.Builder {
@@ -9095,7 +9120,9 @@
}
@Deprecated public final class ValidationProbeEvent implements android.net.metrics.IpConnectivityLog.Event {
+ method @Deprecated public int describeContents();
method @Deprecated @NonNull public static String getProbeName(int);
+ method @Deprecated public void writeToParcel(android.os.Parcel, int);
field @Deprecated public static final int DNS_FAILURE = 0; // 0x0
field @Deprecated public static final int DNS_SUCCESS = 1; // 0x1
field @Deprecated public static final int PROBE_DNS = 0; // 0x0
@@ -9605,6 +9632,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 +9732,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 +9799,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);
@@ -11500,6 +11527,7 @@
public abstract class FieldClassificationService extends android.app.Service {
ctor public FieldClassificationService();
+ method public final android.os.IBinder onBind(android.content.Intent);
method public abstract void onClassificationRequest(@NonNull android.service.assist.classification.FieldClassificationRequest, @NonNull android.os.CancellationSignal, @NonNull android.os.OutcomeReceiver<android.service.assist.classification.FieldClassificationResponse,java.lang.Exception>);
method public void onConnected();
method public void onDisconnected();
@@ -11578,6 +11606,7 @@
method protected final void dump(java.io.FileDescriptor, java.io.PrintWriter, String[]);
method protected void dump(@NonNull java.io.PrintWriter, @NonNull String[]);
method @Nullable public final android.service.autofill.FillEventHistory getFillEventHistory();
+ method public final android.os.IBinder onBind(android.content.Intent);
method public void onConnected();
method public void onDisconnected();
method public void onFillRequest(@NonNull android.service.autofill.augmented.FillRequest, @NonNull android.os.CancellationSignal, @NonNull android.service.autofill.augmented.FillController, @NonNull android.service.autofill.augmented.FillCallback);
@@ -11616,6 +11645,7 @@
public final class FillWindow implements java.lang.AutoCloseable {
ctor public FillWindow();
+ method public void close();
method public void destroy();
method public boolean update(@NonNull android.service.autofill.augmented.PresentationParams.Area, @NonNull android.view.View, long);
}
@@ -11641,6 +11671,7 @@
public final class CarrierMessagingServiceWrapper implements java.lang.AutoCloseable {
ctor public CarrierMessagingServiceWrapper();
method public boolean bindToCarrierMessagingService(@NonNull android.content.Context, @NonNull String, @NonNull java.util.concurrent.Executor, @NonNull Runnable);
+ method public void close();
method public void disconnect();
method public void downloadMms(@NonNull android.net.Uri, int, @NonNull android.net.Uri, @NonNull java.util.concurrent.Executor, @NonNull android.service.carrier.CarrierMessagingServiceWrapper.CarrierMessagingCallback);
method public void receiveSms(@NonNull android.service.carrier.MessagePdu, @NonNull String, int, int, @NonNull java.util.concurrent.Executor, @NonNull android.service.carrier.CarrierMessagingServiceWrapper.CarrierMessagingCallback);
@@ -11691,6 +11722,7 @@
method public final void disableSelf();
method public void onActivityEvent(@NonNull android.service.contentcapture.ActivityEvent);
method public void onActivitySnapshot(@NonNull android.view.contentcapture.ContentCaptureSessionId, @NonNull android.service.contentcapture.SnapshotData);
+ method public final android.os.IBinder onBind(android.content.Intent);
method public void onConnected();
method public void onContentCaptureEvent(@NonNull android.view.contentcapture.ContentCaptureSessionId, @NonNull android.view.contentcapture.ContentCaptureEvent);
method public void onCreateContentCaptureSession(@NonNull android.view.contentcapture.ContentCaptureContext, @NonNull android.view.contentcapture.ContentCaptureSessionId);
@@ -11729,6 +11761,7 @@
public abstract class ContentSuggestionsService extends android.app.Service {
ctor public ContentSuggestionsService();
+ method public final android.os.IBinder onBind(android.content.Intent);
method public abstract void onClassifyContentSelections(@NonNull android.app.contentsuggestions.ClassificationsRequest, @NonNull android.app.contentsuggestions.ContentSuggestionsManager.ClassificationsCallback);
method public abstract void onNotifyInteraction(@NonNull String, @NonNull android.os.Bundle);
method public abstract void onProcessContextImage(int, @Nullable android.graphics.Bitmap, @NonNull android.os.Bundle);
@@ -11742,6 +11775,7 @@
public abstract class DataLoaderService extends android.app.Service {
ctor public DataLoaderService();
+ method @NonNull public final android.os.IBinder onBind(@NonNull android.content.Intent);
method @Nullable public android.service.dataloader.DataLoaderService.DataLoader onCreateDataLoader(@NonNull android.content.pm.DataLoaderParams);
}
@@ -12414,6 +12448,7 @@
public class TraceReportService extends android.app.Service {
ctor public TraceReportService();
+ method @Nullable public final android.os.IBinder onBind(@NonNull android.content.Intent);
method public void onReportTrace(@NonNull android.service.tracing.TraceReportService.TraceParams);
}
@@ -13039,6 +13074,7 @@
method @Nullable public android.content.ComponentName getCallScreeningComponent();
method public boolean isBlocked();
method public boolean isInContacts();
+ method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.telecom.Connection.CallFilteringCompletionInfo> CREATOR;
}
@@ -13228,7 +13264,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
@@ -13348,6 +13384,7 @@
method public int getReason();
method public int getTimeoutSeconds();
method public boolean isEnabled();
+ method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.CallForwardingInfo> CREATOR;
field public static final int REASON_ALL = 4; // 0x4
field public static final int REASON_ALL_CONDITIONAL = 5; // 0x5
@@ -13632,6 +13669,7 @@
method public int getDownlinkCapacityKbps();
method public int getType();
method public int getUplinkCapacityKbps();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.LinkCapacityEstimate> CREATOR;
field public static final int INVALID = -1; // 0xffffffff
field public static final int LCE_TYPE_COMBINED = 2; // 0x2
@@ -13641,8 +13679,10 @@
public final class LteVopsSupportInfo extends android.telephony.VopsSupportInfo {
ctor public LteVopsSupportInfo(int, int);
+ method public boolean equals(@Nullable Object);
method public int getEmcBearerSupport();
method public int getVopsSupport();
+ method public int hashCode();
method public boolean isEmergencyServiceFallbackSupported();
method public boolean isEmergencyServiceSupported();
method public boolean isVopsSupported();
@@ -13709,7 +13749,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);
@@ -13743,9 +13783,11 @@
public final class NrVopsSupportInfo extends android.telephony.VopsSupportInfo {
ctor public NrVopsSupportInfo(int, int, int);
+ method public boolean equals(@Nullable Object);
method public int getEmcSupport();
method public int getEmfSupport();
method public int getVopsSupport();
+ method public int hashCode();
method public boolean isEmergencyServiceFallbackSupported();
method public boolean isEmergencyServiceSupported();
method public boolean isVopsSupported();
@@ -14858,6 +14900,7 @@
public abstract class QualifiedNetworksService extends android.app.Service {
ctor public QualifiedNetworksService();
+ method public android.os.IBinder onBind(android.content.Intent);
method @NonNull public abstract android.telephony.data.QualifiedNetworksService.NetworkAvailabilityProvider onCreateNetworkAvailabilityProvider(int);
field public static final String QUALIFIED_NETWORKS_SERVICE_INTERFACE = "android.telephony.data.QualifiedNetworksService";
}
@@ -15049,6 +15092,7 @@
public class GbaService extends android.app.Service {
ctor public GbaService();
method public void onAuthenticationRequest(int, int, int, @NonNull android.net.Uri, @NonNull byte[], boolean);
+ method public android.os.IBinder onBind(android.content.Intent);
method public final void reportAuthenticationFailure(int, int) throws java.lang.RuntimeException;
method public final void reportKeysAvailable(int, @NonNull byte[], @NonNull String) throws java.lang.RuntimeException;
field public static final String SERVICE_INTERFACE = "android.telephony.gba.GbaService";
@@ -15551,6 +15595,7 @@
method @Deprecated public android.telephony.ims.stub.ImsRegistrationImplBase getRegistration(int);
method @NonNull public android.telephony.ims.stub.ImsRegistrationImplBase getRegistrationForSubscription(int, int);
method @Nullable public android.telephony.ims.stub.SipTransportImplBase getSipTransport(int);
+ method public android.os.IBinder onBind(android.content.Intent);
method public final void onUpdateSupportedImsFeatures(android.telephony.ims.stub.ImsFeatureConfiguration) throws android.os.RemoteException;
method public android.telephony.ims.stub.ImsFeatureConfiguration querySupportedImsFeatures();
method public void readyForFeatureCreation();
@@ -16679,157 +16724,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/test-current.txt b/core/api/test-current.txt
index aa48451..b905287 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -28,7 +28,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 +55,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 +838,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 +1179,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();
@@ -2957,10 +2956,6 @@
method @Deprecated public boolean isBound();
}
- public class NotificationRankingUpdate implements android.os.Parcelable {
- method public final boolean isFdNotNullAndClosed();
- }
-
}
package android.service.quickaccesswallet {
@@ -3190,7 +3185,6 @@
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 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;
@@ -3283,12 +3277,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 {
@@ -3410,7 +3404,7 @@
public final class Choreographer {
method public static long getFrameDelay();
- method public long getFrameTimeNanos();
+ method @FlaggedApi("android.view.flags.expected_presentation_time_api") public long getFrameTimeNanos();
method public void postCallback(int, Runnable, Object);
method public void postCallbackDelayed(int, Runnable, Object, long);
method public void removeCallbacks(int, Runnable, Object);
@@ -3571,8 +3565,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);
@@ -3590,8 +3584,8 @@
field public static final int ACCESSIBILITY_TITLE_CHANGED = 33554432; // 0x2000000
field public static final int FLAG_SLIPPERY = 536870912; // 0x20000000
field public CharSequence accessibilityTitle;
- field public float preferredMaxDisplayRefreshRate;
- field public float preferredMinDisplayRefreshRate;
+ field @FlaggedApi("android.view.flags.wm_display_refresh_rate_test") public float preferredMaxDisplayRefreshRate;
+ field @FlaggedApi("android.view.flags.wm_display_refresh_rate_test") public float preferredMinDisplayRefreshRate;
field public int privateFlags;
}
@@ -3621,7 +3615,6 @@
public final class AccessibilityWindowInfo implements android.os.Parcelable {
method public static void setNumInstancesInUseCounter(java.util.concurrent.atomic.AtomicInteger);
- field public static final int UNDEFINED_WINDOW_ID = -1; // 0xffffffff
}
}
@@ -3629,7 +3622,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();
}
@@ -3949,7 +3942,9 @@
package android.window {
public final class BackNavigationInfo implements android.os.Parcelable {
+ method public int describeContents();
method @NonNull public static String typeToString(int);
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.window.BackNavigationInfo> CREATOR;
field public static final String KEY_TRIGGER_BACK = "TriggerBack";
field public static final int TYPE_CALLBACK = 4; // 0x4
@@ -4010,11 +4005,13 @@
}
public final class TaskFragmentCreationParams implements android.os.Parcelable {
+ method public int describeContents();
method @NonNull public android.os.IBinder getFragmentToken();
method @NonNull public android.graphics.Rect getInitialRelativeBounds();
method @NonNull public android.window.TaskFragmentOrganizerToken getOrganizer();
method @NonNull public android.os.IBinder getOwnerToken();
method public int getWindowingMode();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.window.TaskFragmentCreationParams> CREATOR;
}
@@ -4026,6 +4023,7 @@
}
public final class TaskFragmentInfo implements android.os.Parcelable {
+ method public int describeContents();
method public boolean equalsForTaskFragmentOrganizer(@Nullable android.window.TaskFragmentInfo);
method @NonNull public java.util.List<android.os.IBinder> getActivities();
method @NonNull public java.util.List<android.os.IBinder> getActivitiesRequestedInTaskFragment();
@@ -4039,6 +4037,7 @@
method public boolean isEmpty();
method public boolean isTaskClearedForReuse();
method public boolean isVisible();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.window.TaskFragmentInfo> CREATOR;
}
@@ -4061,6 +4060,8 @@
}
public final class TaskFragmentOrganizerToken implements android.os.Parcelable {
+ method public int describeContents();
+ method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.window.TaskFragmentOrganizerToken> CREATOR;
}
diff --git a/core/java/Android.bp b/core/java/Android.bp
index c3f3d87..0293f66 100644
--- a/core/java/Android.bp
+++ b/core/java/Android.bp
@@ -23,11 +23,6 @@
visibility: ["//frameworks/base"],
}
-filegroup {
- name: "IKeyAttestationApplicationIdProvider.aidl",
- srcs: ["android/security/keymaster/IKeyAttestationApplicationIdProvider.aidl"],
-}
-
aidl_library {
name: "IDropBoxManagerService_aidl",
srcs: [
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index 3370c12..1000612 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -23,6 +23,7 @@
import android.annotation.CallbackExecutor;
import android.annotation.CheckResult;
import android.annotation.ColorInt;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -793,6 +794,7 @@
* @hide
*/
@Retention(RetentionPolicy.SOURCE)
+ @FlaggedApi("android.view.accessibility.a11y_overlay_callbacks")
@IntDef(
prefix = {"OVERLAY_RESULT_"},
value = {
@@ -803,6 +805,7 @@
public @interface AttachOverlayResult {}
/** Result code indicating the overlay was successfully attached. */
+ @FlaggedApi("android.view.accessibility.a11y_overlay_callbacks")
public static final int OVERLAY_RESULT_SUCCESS = 0;
/**
@@ -810,6 +813,7 @@
* error and not
* because of problems with the input.
*/
+ @FlaggedApi("android.view.accessibility.a11y_overlay_callbacks")
public static final int OVERLAY_RESULT_INTERNAL_ERROR = 1;
/**
@@ -817,6 +821,7 @@
* specified display or
* window id was invalid.
*/
+ @FlaggedApi("android.view.accessibility.a11y_overlay_callbacks")
public static final int OVERLAY_RESULT_INVALID = 2;
private int mConnectionId = AccessibilityInteractionClient.NO_ID;
@@ -3506,11 +3511,7 @@
* @param displayId the display to which the SurfaceControl should be attached.
* @param sc the SurfaceControl containing the overlay content
*
- * @deprecated Use
- * {@link #attachAccessibilityOverlayToDisplay(int, SurfaceControl, Executor, IntConsumer)}
- * instead.
*/
- @Deprecated
public void attachAccessibilityOverlayToDisplay(int displayId, @NonNull SurfaceControl sc) {
Preconditions.checkNotNull(sc, "SurfaceControl cannot be null");
AccessibilityInteractionClient.getInstance(this)
@@ -3547,6 +3548,7 @@
* @see #OVERLAY_RESULT_INVALID
* @see #OVERLAY_RESULT_INTERNAL_ERROR
*/
+ @FlaggedApi("android.view.accessibility.a11y_overlay_callbacks")
public void attachAccessibilityOverlayToDisplay(
int displayId,
@NonNull SurfaceControl sc,
@@ -3581,11 +3583,7 @@
* @param accessibilityWindowId The window id, from {@link AccessibilityWindowInfo#getId()}.
* @param sc the SurfaceControl containing the overlay content
*
- * @deprecated Use
- * {@link #attachAccessibilityOverlayToWindow(int, SurfaceControl, Executor,IntConsumer)}
- * instead.
*/
- @Deprecated
public void attachAccessibilityOverlayToWindow(
int accessibilityWindowId, @NonNull SurfaceControl sc) {
Preconditions.checkNotNull(sc, "SurfaceControl cannot be null");
@@ -3623,6 +3621,7 @@
* @see #OVERLAY_RESULT_INVALID
* @see #OVERLAY_RESULT_INTERNAL_ERROR
*/
+ @FlaggedApi("android.view.accessibility.a11y_overlay_callbacks")
public void attachAccessibilityOverlayToWindow(
int accessibilityWindowId,
@NonNull SurfaceControl sc,
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index 895dde7..f2c0051 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -2073,9 +2073,7 @@
* Allow a {@link PendingIntent} to use the privilege of its creator to start background
* activities.
*
- * @param mode the {@link android.app.ComponentOptions.BackgroundActivityStartMode} being set
- * @throws IllegalArgumentException is the value is not a valid
- * {@link android.app.ComponentOptions.BackgroundActivityStartMode}
+ * @param mode the mode being set
*/
@NonNull
public ActivityOptions setPendingIntentCreatorBackgroundActivityStartMode(
@@ -2088,7 +2086,7 @@
* Returns the mode to start background activities granted by the creator of the
* {@link PendingIntent}.
*
- * @return the {@link android.app.ComponentOptions.BackgroundActivityStartMode} currently set
+ * @return the mode currently set
*/
public @BackgroundActivityStartMode int getPendingIntentCreatorBackgroundActivityStartMode() {
return mPendingIntentCreatorBackgroundActivityStartMode;
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index dd1e108..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 trusted process 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_TRUSTED_PROCESS_TRAINING_DATA =
- AppProtoEnums.APP_OP_RECEIVE_TRUSTED_PROCESS_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_TRUSTED_PROCESS_TRAINING_DATA
+ OPSTR_RECEIVE_SANDBOXED_DETECTION_TRAINING_DATA
})
public @interface AppOpString {}
@@ -2257,18 +2258,17 @@
*
* @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 trusted process 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_TRUSTED_PROCESS_TRAINING_DATA =
- "android:receive_trusted_process_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;
@@ -2381,7 +2381,8 @@
OP_FOREGROUND_SERVICE_SPECIAL_USE,
OP_CAPTURE_CONSENTLESS_BUGREPORT_ON_USERDEBUG_BUILD,
OP_USE_FULL_SCREEN_INTENT,
- OP_RECEIVE_SANDBOX_TRIGGER_AUDIO
+ OP_RECEIVE_SANDBOX_TRIGGER_AUDIO,
+ OP_RECEIVE_SANDBOXED_DETECTION_TRAINING_DATA
};
static final AppOpInfo[] sAppOpInfos = new AppOpInfo[]{
@@ -2814,9 +2815,11 @@
"RECEIVE_SANDBOX_TRIGGER_AUDIO")
.setPermission(Manifest.permission.RECEIVE_SANDBOX_TRIGGER_AUDIO)
.setDefaultMode(AppOpsManager.MODE_DEFAULT).build(),
- new AppOpInfo.Builder(OP_RECEIVE_TRUSTED_PROCESS_TRAINING_DATA,
- OPSTR_RECEIVE_TRUSTED_PROCESS_TRAINING_DATA,
- "RECEIVE_TRUSTED_PROCESS_TRAINING_DATA").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/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/BroadcastOptions.java b/core/java/android/app/BroadcastOptions.java
index 9549ebf..41b4004 100644
--- a/core/java/android/app/BroadcastOptions.java
+++ b/core/java/android/app/BroadcastOptions.java
@@ -843,8 +843,7 @@
* considered to be in the same delivery group as this iff it has the same {@code namespace}
* and {@code key}.
*
- * <p> If neither matching key using this API nor matching filter using
- * {@link #setDeliveryGroupMatchingFilter(IntentFilter)} is specified, then by default
+ * <p> If not matching key using this API then by default
* {@link Intent#filterEquals(Intent)} will be used to identify the delivery group.
*/
@NonNull
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index 8fea03b..ebf183f 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -50,7 +50,6 @@
import android.os.UserHandle;
import android.provider.Settings;
import android.security.net.config.NetworkSecurityConfigProvider;
-import android.sysprop.VndkProperties;
import android.text.TextUtils;
import android.util.AndroidRuntimeException;
import android.util.ArrayMap;
@@ -901,14 +900,10 @@
}
// Similar to vendor apks, we should add /product/lib for apks from product partition
- // when product apps are marked as unbundled. We cannot use the same way from vendor
- // to check if lib path exists because there is possibility that /product/lib would not
- // exist from legacy device while product apks are bundled. To make this clear, we use
- // "ro.product.vndk.version" property. If the property is defined, we regard all product
- // apks as unbundled.
+ // when product apps are marked as unbundled. Product is separated as long as the
+ // partition exists, so it can be handled with same approach from the vendor partition.
if (mApplicationInfo.getCodePath() != null
- && mApplicationInfo.isProduct()
- && VndkProperties.product_vndk_version().isPresent()) {
+ && mApplicationInfo.isProduct()) {
isBundledApp = false;
}
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index dd7db23..94e1292 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -1514,13 +1514,13 @@
/**
* {@link #extras} key: the color used as a hint for the Answer action button of a
- * {@link android.app.Notification.CallStyle} notification. This extra is a {@link ColorInt}.
+ * {@link android.app.Notification.CallStyle} notification. This extra is a {@code ColorInt}.
*/
public static final String EXTRA_ANSWER_COLOR = "android.answerColor";
/**
* {@link #extras} key: the color used as a hint for the Decline or Hang Up action button of a
- * {@link android.app.Notification.CallStyle} notification. This extra is a {@link ColorInt}.
+ * {@link android.app.Notification.CallStyle} notification. This extra is a {@code ColorInt}.
*/
public static final String EXTRA_DECLINE_COLOR = "android.declineColor";
@@ -1704,7 +1704,7 @@
private static final String EXTRA_DATA_ONLY_INPUTS = "android.extra.DATA_ONLY_INPUTS";
/**
- * {@link }: No semantic action defined.
+ * No semantic action defined.
*/
public static final int SEMANTIC_ACTION_NONE = 0;
@@ -7923,7 +7923,7 @@
* (via {@link Notification.Builder#setShortcutId(String)}) are displayed in a dedicated
* conversation section in the shade above non-conversation alerting and silence notifications.
* To be a valid conversation shortcut, the shortcut must be a
- * {@link ShortcutInfo#setLongLived()} dynamic or cached sharing shortcut.
+ * {@link ShortcutInfo.Builder#setLongLived(boolean)} dynamic or cached sharing shortcut.
*
* <p>
* This class is a "rebuilder": It attaches to a Builder object and modifies its behavior.
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/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java
index 15bd1dc..e268968 100644
--- a/core/java/android/app/assist/AssistStructure.java
+++ b/core/java/android/app/assist/AssistStructure.java
@@ -651,6 +651,7 @@
// POJO used to override some autofill-related values when the node is parcelized.
// Not written to parcel.
AutofillOverlay mAutofillOverlay;
+ boolean mIsCredential;
int mX;
int mY;
@@ -799,6 +800,7 @@
if (autofillFlags != 0) {
mSanitized = in.readInt() == 1;
+ mIsCredential = in.readInt() == 1;
mImportantForAutofill = in.readInt();
if ((autofillFlags & AUTOFILL_FLAGS_HAS_AUTOFILL_VIEW_ID) != 0) {
@@ -1033,6 +1035,7 @@
if (autofillFlags != 0) {
out.writeInt(mSanitized ? 1 : 0);
+ out.writeInt(mIsCredential ? 1 : 0);
out.writeInt(mImportantForAutofill);
writeSensitive = mSanitized || !sanitizeOnWrite;
if ((autofillFlags & AUTOFILL_FLAGS_HAS_AUTOFILL_VIEW_ID) != 0) {
@@ -1246,6 +1249,19 @@
}
/**
+ * @return whether the node is a credential.
+ *
+ * <p>It's only relevant when the {@link AssistStructure} is used for autofill purposes,
+ * not for assist purposes.
+ * TODO(b/303677885): add TestApi
+ *
+ * @hide
+ */
+ public boolean isCredential() {
+ return mIsCredential;
+ }
+
+ /**
* Gets the {@link android.text.InputType} bits of this structure.
*
* @return bits as defined by {@link android.text.InputType}.
@@ -2183,6 +2199,11 @@
}
@Override
+ public void setIsCredential(boolean isCredential) {
+ mNode.mIsCredential = isCredential;
+ }
+
+ @Override
public void setReceiveContentMimeTypes(@Nullable String[] mimeTypes) {
mNode.mReceiveContentMimeTypes = mimeTypes;
}
@@ -2498,7 +2519,9 @@
+ ", value=" + node.getAutofillValue()
+ ", sanitized=" + node.isSanitized()
+ ", important=" + node.getImportantForAutofill()
- + ", visibility=" + node.getVisibility());
+ + ", visibility=" + node.getVisibility()
+ + ", isCredential=" + node.isCredential()
+ );
}
final int NCHILDREN = node.getChildCount();
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/app/usage/flags.aconfig b/core/java/android/app/usage/flags.aconfig
index afe87de..d1f9067 100644
--- a/core/java/android/app/usage/flags.aconfig
+++ b/core/java/android/app/usage/flags.aconfig
@@ -7,3 +7,9 @@
bug: "296061232"
}
+flag {
+ name: "report_usage_stats_permission"
+ namespace: "backstage_power"
+ description: "Feature flag for the new REPORT_USAGE_STATS permission."
+ bug: "296056771"
+}
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/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..93a3e78 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/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/content/AttributionSource.java b/core/java/android/content/AttributionSource.java
index bfc1eec..62fbcaf 100644
--- a/core/java/android/content/AttributionSource.java
+++ b/core/java/android/content/AttributionSource.java
@@ -730,7 +730,7 @@
/**
* The next app to receive the permission protected data.
*
- * @deprecated Use {@link setNextAttributionSource} instead.
+ * @deprecated Use {@link #setNextAttributionSource} instead.
*/
@Deprecated
public @NonNull Builder setNext(@Nullable AttributionSource value) {
@@ -744,6 +744,7 @@
/**
* The next app to receive the permission protected data.
*/
+ @FlaggedApi(Flags.FLAG_SET_NEXT_ATTRIBUTION_SOURCE)
public @NonNull Builder setNextAttributionSource(@NonNull AttributionSource value) {
checkNotUsed();
mBuilderFieldsSet |= 0x20;
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/Context.java b/core/java/android/content/Context.java
index b6a98a5..59bb73b 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -323,7 +323,7 @@
// Make sure no flag uses the sign bit (most significant bit) of the long integer,
// to avoid future confusion.
BIND_BYPASS_USER_NETWORK_RESTRICTIONS,
- BIND_FILTER_OUT_QUARANTINED_COMPONENTS,
+ BIND_MATCH_QUARANTINED_COMPONENTS,
})
@Retention(RetentionPolicy.SOURCE)
public @interface BindServiceFlagsLongBits {}
@@ -703,7 +703,7 @@
*
* @hide
*/
- public static final long BIND_FILTER_OUT_QUARANTINED_COMPONENTS = 0x2_0000_0000L;
+ public static final long BIND_MATCH_QUARANTINED_COMPONENTS = 0x2_0000_0000L;
/**
@@ -4357,7 +4357,6 @@
* @see android.telephony.CarrierConfigManager
* @see #EUICC_SERVICE
* @see android.telephony.euicc.EuiccManager
- * @see android.telephony.MmsManager
* @see #INPUT_METHOD_SERVICE
* @see android.view.inputmethod.InputMethodManager
* @see #UI_MODE_SERVICE
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 44a9acd..b765562 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.
@@ -3915,6 +3929,8 @@
* {@link #ACTION_BOOT_COMPLETED} is sent. This is sent as a foreground
* broadcast, since it is part of a visible user interaction; be as quick
* as possible when handling it.
+ *
+ * <p><b>Note:</b> This broadcast is not sent to the system user.
*/
public static final String ACTION_USER_INITIALIZE =
"android.intent.action.USER_INITIALIZE";
@@ -4166,7 +4182,7 @@
* new state of quiet mode. This is only sent to registered receivers, not manifest receivers.
*
* <p>This broadcast is similar to {@link #ACTION_MANAGED_PROFILE_AVAILABLE} but functions as a
- * generic broadcast for all users of type {@link android.content.pm.UserInfo#isProfile()}}.
+ * generic broadcast for all profile users.
*/
@FlaggedApi(FLAG_ALLOW_PRIVATE_PROFILE)
public static final String ACTION_PROFILE_AVAILABLE =
@@ -4180,7 +4196,7 @@
* new state of quiet mode. This is only sent to registered receivers, not manifest receivers.
*
* <p>This broadcast is similar to {@link #ACTION_MANAGED_PROFILE_UNAVAILABLE} but functions as
- * a generic broadcast for all users of type {@link android.content.pm.UserInfo#isProfile()}}.
+ * a generic broadcast for all profile users.
*/
@FlaggedApi(FLAG_ALLOW_PRIVATE_PROFILE)
public static final String ACTION_PROFILE_UNAVAILABLE =
@@ -4208,7 +4224,7 @@
* that was removed.
*
* <p>This broadcast is similar to {@link #ACTION_MANAGED_PROFILE_REMOVED} but functions as a
- * generic broadcast for all users of type {@link android.content.pm.UserInfo#isProfile()}}.
+ * generic broadcast for all profile users.
* It is sent in addition to the {@link #ACTION_MANAGED_PROFILE_REMOVED} broadcast when a
* managed user is removed.
*
@@ -4228,7 +4244,7 @@
* that was added.
*
* <p>This broadcast is similar to {@link #ACTION_MANAGED_PROFILE_ADDED} but functions as a
- * generic broadcast for all users of type {@link android.content.pm.UserInfo#isProfile()}}.
+ * generic broadcast for all profile users.
* It is sent in addition to the {@link #ACTION_MANAGED_PROFILE_ADDED} broadcast when a
* managed user is added.
*
@@ -5309,6 +5325,7 @@
* @hide
*/
@SystemApi
+ @FlaggedApi(android.content.pm.Flags.FLAG_ARCHIVING)
public static final String ACTION_UNARCHIVE_PACKAGE = "android.intent.action.UNARCHIVE_PACKAGE";
// ---------------------------------------------------------------------
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 673a8a5..d837aae 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -371,6 +371,13 @@
"android.content.pm.extra.UNARCHIVE_ALL_USERS";
/**
+ * A list of warnings that occurred during installation.
+ *
+ * @hide
+ */
+ public static final String EXTRA_WARNINGS = "android.content.pm.extra.WARNINGS";
+
+ /**
* Streaming installation pending.
* Caller should make sure DataLoader is able to prepare image and reinitiate the operation.
*
@@ -2723,8 +2730,8 @@
* Sets the state of permissions for the package at installation.
* <p/>
* Granting any runtime permissions require the
- * {@link android.Manifest.permission#INSTALL_GRANT_RUNTIME_PERMISSIONS
- * INSTALL_GRANT_RUNTIME_PERMISSIONS} permission to be held by the caller. Revoking runtime
+ * {@code android.Manifest.permission#INSTALL_GRANT_RUNTIME_PERMISSIONS}
+ * permission to be held by the caller. Revoking runtime
* permissions is not allowed, even during app update sessions.
* <p/>
* Holders without the permission are allowed to change the following special permissions:
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 8fbe50c3..1b60f8e 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -838,7 +838,7 @@
GET_DISABLED_COMPONENTS,
GET_DISABLED_UNTIL_USED_COMPONENTS,
GET_UNINSTALLED_PACKAGES,
- FILTER_OUT_QUARANTINED_COMPONENTS,
+ MATCH_QUARANTINED_COMPONENTS,
})
@Retention(RetentionPolicy.SOURCE)
public @interface ComponentInfoFlagsBits {}
@@ -863,7 +863,7 @@
GET_DISABLED_UNTIL_USED_COMPONENTS,
GET_UNINSTALLED_PACKAGES,
MATCH_CLONE_PROFILE,
- FILTER_OUT_QUARANTINED_COMPONENTS,
+ MATCH_QUARANTINED_COMPONENTS,
})
@Retention(RetentionPolicy.SOURCE)
public @interface ResolveInfoFlagsBits {}
@@ -1252,12 +1252,13 @@
*/
// TODO(b/278553670) Unhide and update @links before launch.
@SystemApi
+ @FlaggedApi(android.content.pm.Flags.FLAG_ARCHIVING)
public static final long MATCH_ARCHIVED_PACKAGES = 1L << 32;
/**
* @hide
*/
- public static final long FILTER_OUT_QUARANTINED_COMPONENTS = 0x100000000L;
+ public static final long MATCH_QUARANTINED_COMPONENTS = 0x100000000L;
/**
* Flag for {@link #addCrossProfileIntentFilter}: if this flag is set: when
@@ -4605,6 +4606,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;
@@ -9878,6 +9889,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
@@ -9888,7 +9911,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..db12728 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,18 @@
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"
+}
+
+flag {
+ name: "sdk_lib_independence"
+ namespace: "package_manager_service"
+ description: "Feature flag to keep app working even if its declared sdk-library dependency is unavailable."
+ bug: "295827951"
+ is_fixed_read_only: true
+}
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..38fbd72 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,23 @@
return mSettingsSubtitle;
}
+ /**
+ * Returns the settings activity.
+ *
+ * @hide
+ */
+ @Nullable
+ @TestApi
+ @FlaggedApi(Flags.FLAG_SETTINGS_ACTIVITY_ENABLED)
+ public CharSequence getSettingsActivity() {
+ // Add a manual check to make sure this returns null if
+ // the flag is not enabled.
+ if (!Flags.settingsActivityEnabled()) {
+ return null;
+ }
+ return mSettingsActivity;
+ }
+
/** Returns the component name for the service. */
@NonNull
public ComponentName getComponentName() {
@@ -133,6 +151,7 @@
dest.writeBoolean(mIsPrimary);
TextUtils.writeToParcel(mOverrideLabel, dest, flags);
TextUtils.writeToParcel(mSettingsSubtitle, dest, flags);
+ TextUtils.writeToParcel(mSettingsActivity, dest, flags);
}
@Override
@@ -161,6 +180,9 @@
+ "settingsSubtitle="
+ mSettingsSubtitle
+ ", "
+ + "settingsActivity="
+ + mSettingsActivity
+ + ", "
+ "capabilities="
+ String.join(",", mCapabilities)
+ "}";
@@ -174,6 +196,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 +219,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 +255,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/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/CryptoObject.java b/core/java/android/hardware/biometrics/CryptoObject.java
index 151f819..6ac1efb 100644
--- a/core/java/android/hardware/biometrics/CryptoObject.java
+++ b/core/java/android/hardware/biometrics/CryptoObject.java
@@ -114,8 +114,8 @@
}
/**
- * Get {@link PresentationSession} object.
- * @return {@link PresentationSession} object or null if this doesn't contain one.
+ * Get {@link KeyAgreement} object.
+ * @return {@link KeyAgreement} object or null if this doesn't contain one.
*/
@FlaggedApi(FLAG_ADD_KEY_AGREEMENT_CRYPTO_OBJECT)
public KeyAgreement getKeyAgreement() {
diff --git a/core/java/android/hardware/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/BrightnessInfo.java b/core/java/android/hardware/display/BrightnessInfo.java
index 99f3d15..53a9a75 100644
--- a/core/java/android/hardware/display/BrightnessInfo.java
+++ b/core/java/android/hardware/display/BrightnessInfo.java
@@ -59,7 +59,8 @@
@IntDef(prefix = {"BRIGHTNESS_MAX_REASON_"}, value = {
BRIGHTNESS_MAX_REASON_NONE,
- BRIGHTNESS_MAX_REASON_THERMAL
+ BRIGHTNESS_MAX_REASON_THERMAL,
+ BRIGHTNESS_MAX_REASON_POWER_IC
})
@Retention(RetentionPolicy.SOURCE)
public @interface BrightnessMaxReason {}
@@ -74,6 +75,11 @@
*/
public static final int BRIGHTNESS_MAX_REASON_THERMAL = 1;
+ /**
+ * Maximum brightness is restricted due to power throttling.
+ */
+ public static final int BRIGHTNESS_MAX_REASON_POWER_IC = 2;
+
/** Brightness */
public final float brightness;
@@ -144,6 +150,8 @@
return "none";
case BRIGHTNESS_MAX_REASON_THERMAL:
return "thermal";
+ case BRIGHTNESS_MAX_REASON_POWER_IC:
+ return "power IC";
}
return "invalid";
}
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 990ebc5..aeddd0c 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,10 +28,10 @@
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;
+import android.app.ActivityThread;
import android.app.KeyguardManager;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
@@ -377,7 +378,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;
@@ -775,7 +776,8 @@
*/
public void registerDisplayListener(@NonNull DisplayListener listener,
@Nullable Handler handler, @EventsMask long eventsMask) {
- mGlobal.registerDisplayListener(listener, handler, eventsMask);
+ mGlobal.registerDisplayListener(listener, handler, eventsMask,
+ ActivityThread.currentPackageName());
}
/**
@@ -1819,6 +1821,12 @@
String KEY_BRIGHTNESS_THROTTLING_DATA = "brightness_throttling_data";
/**
+ * Key for the power throttling data as a String formatted, from the display
+ * device config.
+ */
+ String KEY_POWER_THROTTLING_DATA = "power_throttling_data";
+
+ /**
* Key for new power controller feature flag. If enabled new DisplayPowerController will
* be used.
* Read value via {@link android.provider.DeviceConfig#getBoolean(String, String, boolean)}
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index 6d6085b..8decd50 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -25,6 +25,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
+import android.app.ActivityThread;
import android.app.PropertyInvalidatedCache;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
@@ -45,8 +46,11 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.Trace;
+import android.sysprop.DisplayProperties;
+import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;
+import android.util.Slog;
import android.util.SparseArray;
import android.view.Display;
import android.view.DisplayAdjustments;
@@ -72,7 +76,13 @@
*/
public final class DisplayManagerGlobal {
private static final String TAG = "DisplayManager";
- private static final boolean DEBUG = false;
+
+ private static final String EXTRA_LOGGING_PACKAGE_NAME =
+ DisplayProperties.debug_vri_package().orElse(null);
+ private static String sCurrentPackageName = ActivityThread.currentPackageName();
+ private static boolean sExtraDisplayListenerLogging = initExtraLogging();
+
+ private static final boolean DEBUG = false || sExtraDisplayListenerLogging;
// True if display info and display ids should be cached.
//
@@ -130,6 +140,8 @@
@VisibleForTesting
public DisplayManagerGlobal(IDisplayManager dm) {
mDm = dm;
+ initExtraLogging();
+
try {
mWideColorSpace =
ColorSpace.get(
@@ -208,7 +220,7 @@
registerCallbackIfNeededLocked();
- if (DEBUG) {
+ if (DEBUG || extraLogging()) {
Log.d(TAG, "getDisplayInfo: displayId=" + displayId + ", info=" + info);
}
return info;
@@ -321,12 +333,14 @@
* If null, listener will use the handler for the current thread, and if still null,
* the handler for the main thread.
* If that is still null, a runtime exception will be thrown.
+ * @param packageName of the calling package.
*/
public void registerDisplayListener(@NonNull DisplayListener listener,
- @Nullable Handler handler, @EventsMask long eventsMask) {
+ @Nullable Handler handler, @EventsMask long eventsMask, String packageName) {
Looper looper = getLooperForHandler(handler);
Handler springBoard = new Handler(looper);
- registerDisplayListener(listener, new HandlerExecutor(springBoard), eventsMask);
+ registerDisplayListener(listener, new HandlerExecutor(springBoard), eventsMask,
+ packageName);
}
/**
@@ -334,9 +348,11 @@
*
* @param listener The listener that will be called when display changes occur.
* @param executor Executor for the thread that will be receiving the callbacks. Cannot be null.
+ * @param eventsMask Mask of events to be listened to.
+ * @param packageName of the calling package.
*/
public void registerDisplayListener(@NonNull DisplayListener listener,
- @NonNull Executor executor, @EventsMask long eventsMask) {
+ @NonNull Executor executor, @EventsMask long eventsMask, String packageName) {
if (listener == null) {
throw new IllegalArgumentException("listener must not be null");
}
@@ -345,15 +361,22 @@
throw new IllegalArgumentException("The set of events to listen to must not be empty.");
}
+ if (extraLogging()) {
+ Slog.i(TAG, "Registering Display Listener: "
+ + Long.toBinaryString(eventsMask) + ", packageName: " + packageName);
+ }
+
synchronized (mLock) {
int index = findDisplayListenerLocked(listener);
if (index < 0) {
- mDisplayListeners.add(new DisplayListenerDelegate(listener, executor, eventsMask));
+ mDisplayListeners.add(new DisplayListenerDelegate(listener, executor, eventsMask,
+ packageName));
registerCallbackIfNeededLocked();
} else {
mDisplayListeners.get(index).setEventsMask(eventsMask);
}
updateCallbackIfNeededLocked();
+ maybeLogAllDisplayListeners();
}
}
@@ -362,6 +385,10 @@
throw new IllegalArgumentException("listener must not be null");
}
+ if (extraLogging()) {
+ Slog.i(TAG, "Unregistering Display Listener: " + listener);
+ }
+
synchronized (mLock) {
int index = findDisplayListenerLocked(listener);
if (index >= 0) {
@@ -371,6 +398,18 @@
updateCallbackIfNeededLocked();
}
}
+ maybeLogAllDisplayListeners();
+ }
+
+ private void maybeLogAllDisplayListeners() {
+ if (!sExtraDisplayListenerLogging) {
+ return;
+ }
+
+ Slog.i(TAG, "Currently Registered Display Listeners:");
+ for (int i = 0; i < mDisplayListeners.size(); i++) {
+ Slog.i(TAG, i + ": " + mDisplayListeners.get(i));
+ }
}
private static Looper getLooperForHandler(@Nullable Handler handler) {
@@ -1148,15 +1187,20 @@
private final DisplayInfo mDisplayInfo = new DisplayInfo();
private final Executor mExecutor;
private AtomicLong mGenerationId = new AtomicLong(1);
+ private final String mPackageName;
DisplayListenerDelegate(DisplayListener listener, @NonNull Executor executor,
- @EventsMask long eventsMask) {
+ @EventsMask long eventsMask, String packageName) {
mExecutor = executor;
mListener = listener;
mEventsMask = eventsMask;
+ mPackageName = packageName;
}
public void sendDisplayEvent(int displayId, @DisplayEvent int event, DisplayInfo info) {
+ if (extraLogging()) {
+ Slog.i(TAG, "Sending Display Event: " + eventToString(event));
+ }
long generationId = mGenerationId.get();
Message msg = Message.obtain(null, event, displayId, 0, info);
mExecutor.execute(() -> {
@@ -1177,6 +1221,14 @@
}
private void handleMessage(Message msg) {
+ if (extraLogging()) {
+ Slog.i(TAG, "DisplayListenerDelegate(" + eventToString(msg.what)
+ + ", display=" + msg.arg1
+ + ", mEventsMask=" + Long.toBinaryString(mEventsMask)
+ + ", mPackageName=" + mPackageName
+ + ", msg.obj=" + msg.obj
+ + ", listener=" + mListener.getClass() + ")");
+ }
if (DEBUG) {
Trace.beginSection(
"DisplayListenerDelegate(" + eventToString(msg.what)
@@ -1193,6 +1245,10 @@
if ((mEventsMask & DisplayManager.EVENT_FLAG_DISPLAY_CHANGED) != 0) {
DisplayInfo newInfo = (DisplayInfo) msg.obj;
if (newInfo != null && !newInfo.equals(mDisplayInfo)) {
+ if (extraLogging()) {
+ Slog.i(TAG, "Sending onDisplayChanged: Display Changed. Info: "
+ + newInfo);
+ }
mDisplayInfo.copyFrom(newInfo);
mListener.onDisplayChanged(msg.arg1);
}
@@ -1228,6 +1284,11 @@
Trace.endSection();
}
}
+
+ @Override
+ public String toString() {
+ return "mask: {" + mEventsMask + "}, for " + mListener.getClass();
+ }
}
/**
@@ -1353,4 +1414,19 @@
}
return "UNKNOWN";
}
+
+
+ private static boolean initExtraLogging() {
+ if (sCurrentPackageName == null) {
+ sCurrentPackageName = ActivityThread.currentPackageName();
+ sExtraDisplayListenerLogging = !TextUtils.isEmpty(EXTRA_LOGGING_PACKAGE_NAME)
+ && EXTRA_LOGGING_PACKAGE_NAME.equals(sCurrentPackageName);
+ }
+ return sExtraDisplayListenerLogging;
+ }
+
+ private static boolean extraLogging() {
+ return sExtraDisplayListenerLogging && EXTRA_LOGGING_PACKAGE_NAME.equals(
+ sCurrentPackageName);
+ }
}
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index ff1a6ac..7b8419e 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -1373,7 +1373,7 @@
public interface InputDeviceListener {
/**
* Called whenever an input device has been added to the system.
- * Use {@link InputManagerGlobal#getInputDevice} to get more information about the device.
+ * Use {@link #getInputDevice(int)} to get more information about the device.
*
* @param deviceId The id of the input device that was added.
*/
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 582c5c0..4c2bbc1 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -3231,7 +3231,7 @@
}
/**
- * Called when the user tapped or clicked an {@link android.widget.Editor}.
+ * Called when the user tapped or clicked an editor.
* This can be useful when IME makes a decision of showing Virtual keyboard based on what
* {@link MotionEvent#getToolType(int)} was used to click the editor.
* e.g. when toolType is {@link MotionEvent#TOOL_TYPE_STYLUS}, IME may choose to show a
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/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 8482945..e85b7bf 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -1854,7 +1854,7 @@
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
public HistoryItem next;
- // The time of this event in milliseconds, as per SystemClock.elapsedRealtime().
+ // The time of this event in milliseconds, as per MonotonicClock.monotonicTime().
@UnsupportedAppUsage
public long time;
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/BatteryUsageStatsQuery.java b/core/java/android/os/BatteryUsageStatsQuery.java
index 49d7e8b..32840d4 100644
--- a/core/java/android/os/BatteryUsageStatsQuery.java
+++ b/core/java/android/os/BatteryUsageStatsQuery.java
@@ -300,6 +300,7 @@
* @param fromTimestamp Exclusive starting timestamp, as per System.currentTimeMillis()
* @param toTimestamp Inclusive ending timestamp, as per System.currentTimeMillis()
*/
+ // TODO(b/298459065): switch to monotonic clock
public Builder aggregateSnapshots(long fromTimestamp, long toTimestamp) {
mFromTimestamp = fromTimestamp;
mToTimestamp = toTimestamp;
diff --git a/core/java/android/os/BugreportManager.java b/core/java/android/os/BugreportManager.java
index 086b0e5..58def6e 100644
--- a/core/java/android/os/BugreportManager.java
+++ b/core/java/android/os/BugreportManager.java
@@ -143,10 +143,11 @@
*/
public void onError(@BugreportErrorCode int errorCode) {}
- /** Called when taking bugreport finishes successfully.
+ /**
+ * Called when taking bugreport finishes successfully.
*
* <p>This callback will be invoked if the
- * {@link BugreportParams#BUGREPORT_FLAG_DEFER_CONSENT} flag is not set.
+ * {@code BugreportParams#BUGREPORT_FLAG_DEFER_CONSENT} flag is not set.
*/
public void onFinished() {}
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 4174c1c..fce715a 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -2670,7 +2670,7 @@
/**
* Called when overall thermal throttling status changed.
- * @param status defined in {@link android.os.Temperature}.
+ * @param status the status
*/
void onThermalStatusChanged(@ThermalStatus int status);
}
diff --git a/core/java/android/os/PowerMonitor.java b/core/java/android/os/PowerMonitor.java
index 5fb0df7..8c5f2cd 100644
--- a/core/java/android/os/PowerMonitor.java
+++ b/core/java/android/os/PowerMonitor.java
@@ -16,6 +16,7 @@
package android.os;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -23,12 +24,19 @@
import java.lang.annotation.RetentionPolicy;
/**
- * A PowerMonitor represents either a Channel aka ODPM rail (on-device power monitor) or an
- * EnergyConsumer, as defined in
- * <a href="https://android.googlesource.com/platform/hardware/interfaces/+/refs/heads/main/power/stats/aidl/aidl_api/android.hardware.power.stats/current/android/hardware/power/stats">android.hardware.power.stats</a>
- *
- * @hide
+ * A PowerMonitor represents either an ODPM rail (on-device power rail monitor) or a modeled
+ * energy consumer.
+ * <p/>
+ * ODPM rail names are device-specific. No assumptions should be made about the names and
+ * exact purpose of ODPM rails across different device models. A rail name may be something
+ * like "S2S_VDD_G3D"; specific knowledge of the device hardware is required to interpret
+ * the corresponding power monitor data.
+ * <p/>
+ * Energy consumer have more human-readable names, e.g. "GPU", "MODEM" etc. However, developers
+ * must be extra cautious about using energy consumers across different device models,
+ * as their exact implementations are also hardware dependent and are customized by OEMs.
*/
+@FlaggedApi("com.android.server.power.optimization.power_monitor_api")
public final class PowerMonitor implements Parcelable {
/**
@@ -36,9 +44,9 @@
* power rail measurement, or modeled in some fashion. For example, an energy consumer may
* represent a combination of multiple rails or a portion of a rail shared between subsystems,
* e.g. WiFi and Bluetooth are often handled by the same chip, powered by a shared rail.
- * Some consumer names are standardized (see android.hardware.power.stats.EnergyConsumerType),
- * others are not.
+ * Some consumer names are standardized, others are not.
*/
+ @FlaggedApi("com.android.server.power.optimization.power_monitor_api")
public static final int POWER_MONITOR_TYPE_CONSUMER = 0;
/**
@@ -46,6 +54,7 @@
* no assumptions can be made about the source of those measurements across different devices,
* even if they have the same name.
*/
+ @FlaggedApi("com.android.server.power.optimization.power_monitor_api")
public static final int POWER_MONITOR_TYPE_MEASUREMENT = 1;
/** @hide */
@@ -64,38 +73,60 @@
* @hide
*/
public final int index;
+
@PowerMonitorType
- public final int type;
+ private final int mType;
@NonNull
- public final String name;
+ private final String mName;
/**
* @hide
*/
public PowerMonitor(int index, int type, @NonNull String name) {
this.index = index;
- this.type = type;
- this.name = name;
+ this.mType = type;
+ this.mName = name;
+ }
+
+ /**
+ * Returns the type of the power monitor.
+ */
+ @FlaggedApi("com.android.server.power.optimization.power_monitor_api")
+ @PowerMonitorType
+ public int getType() {
+ return mType;
+ }
+
+ /**
+ * Returns the name of the power monitor, either a power rail or an energy consumer.
+ */
+ @FlaggedApi("com.android.server.power.optimization.power_monitor_api")
+ @NonNull
+ public String getName() {
+ return mName;
}
private PowerMonitor(Parcel in) {
index = in.readInt();
- type = in.readInt();
- name = in.readString();
+ mType = in.readInt();
+ mName = in.readString8();
}
+ @FlaggedApi("com.android.server.power.optimization.power_monitor_api")
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeInt(index);
- dest.writeInt(type);
- dest.writeString(name);
+ dest.writeInt(mType);
+ dest.writeString8(mName);
}
+ @FlaggedApi("com.android.server.power.optimization.power_monitor_api")
@Override
public int describeContents() {
return 0;
}
+ @FlaggedApi("com.android.server.power.optimization.power_monitor_api")
@NonNull
public static final Creator<PowerMonitor> CREATOR = new Creator<>() {
@Override
diff --git a/core/java/android/os/PowerMonitorReadings.java b/core/java/android/os/PowerMonitorReadings.java
index e767059..bb677d5 100644
--- a/core/java/android/os/PowerMonitorReadings.java
+++ b/core/java/android/os/PowerMonitorReadings.java
@@ -17,6 +17,7 @@
package android.os;
import android.annotation.ElapsedRealtimeLong;
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import java.util.Arrays;
@@ -24,10 +25,10 @@
/**
* A collection of energy measurements from Power Monitors.
- *
- * @hide
*/
+@FlaggedApi("com.android.server.power.optimization.power_monitor_api")
public final class PowerMonitorReadings {
+ @FlaggedApi("com.android.server.power.optimization.power_monitor_api")
public static final int ENERGY_UNAVAILABLE = -1;
@NonNull
@@ -41,7 +42,7 @@
Comparator.comparingInt(pm -> pm.index);
/**
- * @param powerMonitors array of power monitor (ODPM) rails, sorted by PowerMonitor.index
+ * @param powerMonitors array of power monitor, sorted by PowerMonitor.index
* @hide
*/
public PowerMonitorReadings(@NonNull PowerMonitor[] powerMonitors,
@@ -56,7 +57,8 @@
* Does not persist across reboots.
* Represents total energy: both on-battery and plugged-in.
*/
- public long getConsumedEnergyUws(@NonNull PowerMonitor powerMonitor) {
+ @FlaggedApi("com.android.server.power.optimization.power_monitor_api")
+ public long getConsumedEnergy(@NonNull PowerMonitor powerMonitor) {
int offset = Arrays.binarySearch(mPowerMonitors, powerMonitor, POWER_MONITOR_COMPARATOR);
if (offset >= 0) {
return mEnergyUws[offset];
@@ -67,6 +69,7 @@
/**
* Elapsed realtime, in milliseconds, when the snapshot was taken.
*/
+ @FlaggedApi("com.android.server.power.optimization.power_monitor_api")
@ElapsedRealtimeLong
public long getTimestamp(@NonNull PowerMonitor powerMonitor) {
int offset = Arrays.binarySearch(mPowerMonitors, powerMonitor, POWER_MONITOR_COMPARATOR);
@@ -84,7 +87,7 @@
if (i != 0) {
sb.append(", ");
}
- sb.append(mPowerMonitors[i].name)
+ sb.append(mPowerMonitors[i].getName())
.append(" = ").append(mEnergyUws[i])
.append(" (").append(mTimestampsMs[i]).append(')');
}
diff --git a/core/java/android/os/RemoteException.java b/core/java/android/os/RemoteException.java
index 970f419..ace5f7e 100644
--- a/core/java/android/os/RemoteException.java
+++ b/core/java/android/os/RemoteException.java
@@ -66,7 +66,7 @@
/**
* Rethrow this exception when we know it came from the system server. This
* gives us an opportunity to throw a nice clean
- * {@link DeadSystemRuntimeException} signal to avoid spamming logs with
+ * {@code DeadSystemRuntimeException} signal to avoid spamming logs with
* misleading stack traces.
* <p>
* Apps making calls into the system server may end up persisting internal
diff --git a/core/java/android/os/flags.aconfig b/core/java/android/os/flags.aconfig
index a95e66d..7fceda4 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."
@@ -9,7 +16,7 @@
flag {
name: "remove_app_profiler_pss_collection"
- namespace: "android_platform_power_optimization"
+ namespace: "power_optimization"
description: "Replaces background PSS collection in AppProfiler with RSS"
bug: "297542292"
}
diff --git a/core/java/android/os/health/SystemHealthManager.java b/core/java/android/os/health/SystemHealthManager.java
index dfc43f4..2d53341 100644
--- a/core/java/android/os/health/SystemHealthManager.java
+++ b/core/java/android/os/health/SystemHealthManager.java
@@ -16,6 +16,7 @@
package android.os.health;
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemService;
@@ -24,7 +25,6 @@
import android.os.BatteryStats;
import android.os.Build;
import android.os.Bundle;
-import android.os.ConditionVariable;
import android.os.Handler;
import android.os.IPowerStatsService;
import android.os.PowerMonitor;
@@ -157,39 +157,15 @@
}
/**
- * Returns a list of supported power monitors, which include raw ODPM rails and
- * modeled energy consumers. If ODPM is unsupported by PowerStats HAL, this method returns
- * an empty array.
+ * Asynchronously retrieves a list of supported {@link PowerMonitor}'s, which include raw ODPM
+ * (on-device power rail monitor) rails and modeled energy consumers. If ODPM is unsupported
+ * on this device this method delivers an empty list.
*
- * @hide
- */
- @NonNull
- public List<PowerMonitor> getSupportedPowerMonitors() {
- synchronized (mPowerMonitorsLock) {
- if (mPowerMonitorsInfo != null) {
- return mPowerMonitorsInfo;
- }
- }
- ConditionVariable lock = new ConditionVariable();
- // Populate mPowerMonitorsInfo by side-effect
- getSupportedPowerMonitors(null, unused -> lock.open());
- lock.block();
-
- synchronized (mPowerMonitorsLock) {
- return mPowerMonitorsInfo;
- }
- }
-
- /**
- * Asynchronously retrieves a list of supported power monitors, see
- * {@link #getSupportedPowerMonitors()}
- *
- * @param handler optional Handler to deliver the callback. If not supplied, the callback
- * may be invoked on an arbitrary thread.
+ * @param handler optional Handler to deliver the callback. If not supplied, the callback
+ * may be invoked on an arbitrary thread.
* @param onResult callback for the result
- *
- * @hide
*/
+ @FlaggedApi("com.android.server.power.optimization.power_monitor_api")
public void getSupportedPowerMonitors(@Nullable Handler handler,
@NonNull Consumer<List<PowerMonitor>> onResult) {
final List<PowerMonitor> result;
@@ -229,35 +205,6 @@
}
}
- /**
- * Retrieves the accumulated power consumption reported by the specified power monitors.
- *
- * @param powerMonitors power monitors to be returned.
- *
- * @hide
- */
- @NonNull
- public PowerMonitorReadings getPowerMonitorReadings(@NonNull List<PowerMonitor> powerMonitors) {
- PowerMonitorReadings[] outReadings = new PowerMonitorReadings[1];
- RuntimeException[] outException = new RuntimeException[1];
- ConditionVariable lock = new ConditionVariable();
- getPowerMonitorReadings(powerMonitors, null,
- pms -> {
- outReadings[0] = pms;
- lock.open();
- },
- error -> {
- outException[0] = error;
- lock.open();
- }
- );
- lock.block();
- if (outException[0] != null) {
- throw outException[0];
- }
- return outReadings[0];
- }
-
private static final Comparator<PowerMonitor> POWER_MONITOR_COMPARATOR =
Comparator.comparingInt(pm -> pm.index);
@@ -270,9 +217,8 @@
* may be invoked on an arbitrary thread.
* @param onSuccess callback for the result
* @param onError callback invoked in case of an error
- *
- * @hide
*/
+ @FlaggedApi("com.android.server.power.optimization.power_monitor_api")
public void getPowerMonitorReadings(@NonNull List<PowerMonitor> powerMonitors,
@Nullable Handler handler, @NonNull Consumer<PowerMonitorReadings> onSuccess,
@NonNull Consumer<RuntimeException> onError) {
diff --git a/core/java/android/os/vibrator/flags.aconfig b/core/java/android/os/vibrator/flags.aconfig
index 88f62f3..66ad12c 100644
--- a/core/java/android/os/vibrator/flags.aconfig
+++ b/core/java/android/os/vibrator/flags.aconfig
@@ -27,3 +27,13 @@
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/permission/flags.aconfig b/core/java/android/permission/flags.aconfig
index d8534dd..d60d4c6 100644
--- a/core/java/android/permission/flags.aconfig
+++ b/core/java/android/permission/flags.aconfig
@@ -21,3 +21,10 @@
description: "enable role controller in system server"
bug: "302562590"
}
+
+flag {
+ name: "set_next_attribution_source"
+ namespace: "permissions"
+ description: "enable AttributionSource.setNextAttributionSource"
+ bug: "304478648"
+}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 5c64389..b34e09f 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1800,7 +1800,6 @@
* Input: Nothing.
* <p>
* Output: Nothing.
- * @see android.service.notification.NotificationAssistantService
*/
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
public static final String ACTION_NOTIFICATION_ASSISTANT_SETTINGS =
@@ -2507,8 +2506,8 @@
* to the caller package.
* <p>
* <b>NOTE: </b> Applications should call
- * {@link android.credentials.CredentialManager#isEnabledCredentialProviderService()}
- * and only use this action to start an activity if they return {@code false}.
+ * {@link android.credentials.CredentialManager#isEnabledCredentialProviderService(
+ * ComponentName)} and only use this action to start an activity if they return {@code false}.
*/
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
public static final String ACTION_CREDENTIAL_PROVIDER =
@@ -5354,6 +5353,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
@@ -11180,6 +11210,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.
*
@@ -12338,7 +12374,7 @@
* Value to specify if the device's UTC system clock should be set automatically, e.g. using
* telephony signals like NITZ, or other sources like GNSS or NTP.
*
- * <p>Prefer {@link android.app.time.TimeManager} API calls to determine the state of
+ * <p>Prefer {@code android.app.time.TimeManager} API calls to determine the state of
* automatic time detection instead of directly observing this setting as it may be ignored
* by the time_detector service under various conditions.
*
@@ -12351,7 +12387,7 @@
* Value to specify if the device's time zone system property should be set automatically,
* e.g. using telephony signals like MCC and NITZ, or other mechanisms like the location.
*
- * <p>Prefer {@link android.app.time.TimeManager} API calls to determine the state of
+ * <p>Prefer {@code android.app.time.TimeManager} API calls to determine the state of
* automatic time zone detection instead of directly observing this setting as it may be
* ignored by the time_zone_detector service under various conditions.
*
@@ -12465,6 +12501,13 @@
"wireless_charging_started_sound";
/**
+ * Whether to auto enable reverse charging once plugged-in.
+ * @hide
+ */
+ public static final String REVERSE_CHARGING_AUTO_ON =
+ "settings_key_reverse_charging_auto_turn_on";
+
+ /**
* URI for "wired charging started" sound.
* @hide
*/
diff --git a/core/java/android/security/keymaster/IKeyAttestationApplicationIdProvider.aidl b/core/java/android/security/keymaster/IKeyAttestationApplicationIdProvider.aidl
deleted file mode 100644
index dbffd5f..0000000
--- a/core/java/android/security/keymaster/IKeyAttestationApplicationIdProvider.aidl
+++ /dev/null
@@ -1,32 +0,0 @@
-/**
- * Copyright (c) 2016, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.security.keymaster;
-
-import android.security.keymaster.KeyAttestationApplicationId;
-import android.security.keymaster.KeyAttestationPackageInfo;
-import android.content.pm.Signature;
-
-/**
- * This must be kept manually in sync with system/security/keystore until AIDL
- * can generate both Java and C++ bindings.
- *
- * @hide
- */
-interface IKeyAttestationApplicationIdProvider {
- /* keep in sync with /system/security/keystore/keystore_attestation_id.cpp */
- KeyAttestationApplicationId getKeyAttestationApplicationId(int uid);
-}
diff --git a/core/java/android/security/keymaster/KeyAttestationApplicationId.aidl b/core/java/android/security/keymaster/KeyAttestationApplicationId.aidl
deleted file mode 100644
index 9f6ff58..0000000
--- a/core/java/android/security/keymaster/KeyAttestationApplicationId.aidl
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Copyright (c) 2016, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.security.keymaster;
-
-/* The cpp_header is relative to system/security/keystore/include
- * Link against libkeystore_binder to make use of the native implementation of this Parcelable.
- */
-parcelable KeyAttestationApplicationId cpp_header "keystore/KeyAttestationApplicationId.h";
diff --git a/core/java/android/security/keymaster/KeyAttestationApplicationId.java b/core/java/android/security/keymaster/KeyAttestationApplicationId.java
deleted file mode 100644
index 670f30e1b..0000000
--- a/core/java/android/security/keymaster/KeyAttestationApplicationId.java
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.security.keymaster;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * @hide
- * The information aggregated by this class is used by keystore to identify a caller of the
- * keystore API toward a remote party. It aggregates multiple PackageInfos because keystore
- * can only determine a caller by uid granularity, and a uid can be shared by multiple packages.
- * The remote party must decide if it trusts all of the packages enough to consider the
- * confidentiality of the key material in question intact.
- */
-public class KeyAttestationApplicationId implements Parcelable {
- private final KeyAttestationPackageInfo[] mAttestationPackageInfos;
-
- /**
- * @param mAttestationPackageInfos
- */
- public KeyAttestationApplicationId(KeyAttestationPackageInfo[] mAttestationPackageInfos) {
- super();
- this.mAttestationPackageInfos = mAttestationPackageInfos;
- }
-
- /**
- * @return the mAttestationPackageInfos
- */
- public KeyAttestationPackageInfo[] getAttestationPackageInfos() {
- return mAttestationPackageInfos;
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeTypedArray(mAttestationPackageInfos, flags);
- }
-
- public static final @android.annotation.NonNull Parcelable.Creator<KeyAttestationApplicationId> CREATOR
- = new Parcelable.Creator<KeyAttestationApplicationId>() {
- @Override
- public KeyAttestationApplicationId createFromParcel(Parcel source) {
- return new KeyAttestationApplicationId(source);
- }
-
- @Override
- public KeyAttestationApplicationId[] newArray(int size) {
- return new KeyAttestationApplicationId[size];
- }
- };
-
- KeyAttestationApplicationId(Parcel source) {
- mAttestationPackageInfos = source.createTypedArray(KeyAttestationPackageInfo.CREATOR);
- }
-}
diff --git a/core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl b/core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl
deleted file mode 100644
index f8b843b..0000000
--- a/core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Copyright (c) 2016, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.security.keymaster;
-
-/* The cpp_header is relative to system/security/keystore/include
- * Link against libkeystore_binder to make use of the native implementation of this Parcelable.
- */
-parcelable KeyAttestationPackageInfo cpp_header "keystore/KeyAttestationPackageInfo.h";
diff --git a/core/java/android/security/keymaster/KeyAttestationPackageInfo.java b/core/java/android/security/keymaster/KeyAttestationPackageInfo.java
deleted file mode 100644
index c0b8d8d..0000000
--- a/core/java/android/security/keymaster/KeyAttestationPackageInfo.java
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.security.keymaster;
-
-import android.content.pm.Signature;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * @hide
- * This class constitutes and excerpt from the PackageManager's PackageInfo for the purpose of
- * key attestation. It is part of the KeyAttestationApplicationId, which is used by
- * keystore to identify the caller of the keystore API towards a remote party.
- */
-public class KeyAttestationPackageInfo implements Parcelable {
- private final String mPackageName;
- private final long mPackageVersionCode;
- private final Signature[] mPackageSignatures;
-
- /**
- * @param mPackageName
- * @param mPackageVersionCode
- * @param mPackageSignatures
- */
- public KeyAttestationPackageInfo(
- String mPackageName, long mPackageVersionCode, Signature[] mPackageSignatures) {
- super();
- this.mPackageName = mPackageName;
- this.mPackageVersionCode = mPackageVersionCode;
- this.mPackageSignatures = mPackageSignatures;
- }
- /**
- * @return the mPackageName
- */
- public String getPackageName() {
- return mPackageName;
- }
- /**
- * @return the mPackageVersionCode
- */
- public long getPackageVersionCode() {
- return mPackageVersionCode;
- }
- /**
- * @return the mPackageSignatures
- */
- public Signature[] getPackageSignatures() {
- return mPackageSignatures;
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeString(mPackageName);
- dest.writeLong(mPackageVersionCode);
- dest.writeTypedArray(mPackageSignatures, flags);
- }
-
- public static final @android.annotation.NonNull Parcelable.Creator<KeyAttestationPackageInfo> CREATOR
- = new Parcelable.Creator<KeyAttestationPackageInfo>() {
- @Override
- public KeyAttestationPackageInfo createFromParcel(Parcel source) {
- return new KeyAttestationPackageInfo(source);
- }
-
- @Override
- public KeyAttestationPackageInfo[] newArray(int size) {
- return new KeyAttestationPackageInfo[size];
- }
- };
-
- private KeyAttestationPackageInfo(Parcel source) {
- mPackageName = source.readString();
- mPackageVersionCode = source.readLong();
- mPackageSignatures = source.createTypedArray(Signature.CREATOR);
- }
-}
diff --git a/core/java/android/service/autofill/FillRequest.java b/core/java/android/service/autofill/FillRequest.java
index 8cf2ce4..7ec1483 100644
--- a/core/java/android/service/autofill/FillRequest.java
+++ b/core/java/android/service/autofill/FillRequest.java
@@ -97,8 +97,6 @@
*/
public static final @RequestFlags int FLAG_VIEW_NOT_FOCUSED = 0x10;
- // The flag value 0x20 has been defined in AutofillManager.
-
/**
* Indicates the request supports fill dialog presentation for the fields, the
* system will send the request when the activity just started.
diff --git a/core/java/android/service/autofill/FillResponse.java b/core/java/android/service/autofill/FillResponse.java
index 3fb94ee..7ea74d3 100644
--- a/core/java/android/service/autofill/FillResponse.java
+++ b/core/java/android/service/autofill/FillResponse.java
@@ -406,7 +406,7 @@
*
* Call this when a field has been detected with a type.
*
- * Altough similiarly named with {@link setFieldClassificationIds},
+ * Altough similiarly named with {@link #setFieldClassificationIds},
* it provides a different functionality - setFieldClassificationIds should
* be used when a field is only suspected to be Autofillable.
* This method should be used when a field is certainly Autofillable
diff --git a/core/java/android/service/autofill/SaveInfo.java b/core/java/android/service/autofill/SaveInfo.java
index 954cc96..53706cd3 100644
--- a/core/java/android/service/autofill/SaveInfo.java
+++ b/core/java/android/service/autofill/SaveInfo.java
@@ -813,7 +813,7 @@
* If no {@link #Builder(int, AutofillId[]) required ids},
* or {@link #setOptionalIds(AutofillId[]) optional ids}, or {@link #FLAG_DELAY_SAVE}
* were set, Save Dialog will only be triggered if platform detection is enabled, which
- * is indicated when {@link FillRequest.getHints()} is not empty.
+ * is indicated when {@link FillRequest#getHints()} is not empty.
*/
public SaveInfo build() {
throwIfDestroyed();
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/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index 07f8aac..759953e 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -896,8 +896,7 @@
* <p>This method will throw a security exception if you don't have access to notifications
* for the given user.</p>
* <p>The caller must have {@link CompanionDeviceManager#getAssociations() an associated
- * device} or be the {@link NotificationAssistantService notification assistant} in order to
- * use this method.
+ * device} or be the notification assistant in order to use this method.
*
* @param pkg The package to retrieve channels for.
*/
@@ -920,8 +919,7 @@
* <p>This method will throw a security exception if you don't have access to notifications
* for the given user.</p>
* <p>The caller must have {@link CompanionDeviceManager#getAssociations() an associated
- * device} or be the {@link NotificationAssistantService notification assistant} in order to
- * use this method.
+ * device} or be the notification assistant in order to use this method.
*
* @param pkg The package to retrieve channel groups for.
*/
@@ -2001,15 +1999,28 @@
/**
* Returns a list of smart {@link Notification.Action} that can be added by the
- * {@link NotificationAssistantService}
+ * notification assistant.
*/
public @NonNull List<Notification.Action> getSmartActions() {
return mSmartActions == null ? Collections.emptyList() : mSmartActions;
}
+
/**
- * Returns a list of smart replies that can be added by the
- * {@link NotificationAssistantService}
+ * 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 notification assistant.
*/
public @NonNull List<CharSequence> getSmartReplies() {
return mSmartReplies == null ? Collections.emptyList() : mSmartReplies;
@@ -2353,11 +2364,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/service/quickaccesswallet/WalletCard.java b/core/java/android/service/quickaccesswallet/WalletCard.java
index 4a4fd04..bf129c5 100644
--- a/core/java/android/service/quickaccesswallet/WalletCard.java
+++ b/core/java/android/service/quickaccesswallet/WalletCard.java
@@ -372,9 +372,9 @@
}
/**
- * Set of locations this card might be useful at. If {@link
- * PackageManager.FEATURE_WALLET_LOCATION_BASED_SUGGESTIONS} is enabled, the card might be
- * shown to the user when a user is near one of these locations.
+ * Set of locations this card might be useful at. If
+ * {@link android.content.pm.PackageManager#FEATURE_WALLET_LOCATION_BASED_SUGGESTIONS} is
+ * enabled, the card might be shown to the user when a user is near one of these locations.
*/
@NonNull
public Builder setCardLocations(@NonNull List<Location> cardLocations) {
diff --git a/core/java/android/service/voice/VoiceInteractionSession.java b/core/java/android/service/voice/VoiceInteractionSession.java
index cabcae3..d40b39e 100644
--- a/core/java/android/service/voice/VoiceInteractionSession.java
+++ b/core/java/android/service/voice/VoiceInteractionSession.java
@@ -1809,7 +1809,7 @@
* in milliseconds of the KeyEvent that triggered Assistant and
* Intent.EXTRA_ASSIST_INPUT_DEVICE_ID (android.intent.extra.ASSIST_INPUT_DEVICE_ID)
* referring to the device that sent the request. Starting from Android 14, the system will
- * add {@link VoiceInteractionService#KEY_SHOW_SESSION_ID}, the Bundle is not null. But the
+ * add {@link #KEY_SHOW_SESSION_ID}, the Bundle is not null. But the
* application should handle null case before Android 14.
* @param showFlags The show flags originally provided to
* {@link VoiceInteractionService#showSession VoiceInteractionService.showSession}.
diff --git a/core/java/android/speech/SpeechRecognizer.java b/core/java/android/speech/SpeechRecognizer.java
index bb5dd7f..3ed13bb 100644
--- a/core/java/android/speech/SpeechRecognizer.java
+++ b/core/java/android/speech/SpeechRecognizer.java
@@ -536,7 +536,16 @@
@MainThread
public void setRecognitionListener(RecognitionListener listener) {
checkIsCalledFromMainThread();
- putMessage(Message.obtain(mHandler, MSG_CHANGE_LISTENER, listener));
+ if (mListener.mInternalListener == null) {
+ // This shortcut is needed because otherwise, if there's an error connecting, it never
+ // gets delivered. I.e., the onSuccess callback set up in connectToSystemService does
+ // not get called, MSG_CHANGE_LISTENER does not get executed, so the onError in the same
+ // place does not get forwarded anywhere.
+ // Thread-wise, this is safe as both this method and the handler are on the UI thread.
+ handleChangeListener(listener);
+ } else {
+ putMessage(Message.obtain(mHandler, MSG_CHANGE_LISTENER, listener));
+ }
}
/**
diff --git a/core/java/android/text/DynamicLayout.java b/core/java/android/text/DynamicLayout.java
index 2b3a081..a0cd074 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;
@@ -1268,7 +1274,7 @@
}
/**
- * Gets the {@link LineBreakconfig} used in this DynamicLayout.
+ * Gets the {@link LineBreakConfig} used in this DynamicLayout.
* Use this only to consult the LineBreakConfig's properties and not
* to change them.
*
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/WordSegmentFinder.java b/core/java/android/text/WordSegmentFinder.java
index be002f3..b0a70ea 100644
--- a/core/java/android/text/WordSegmentFinder.java
+++ b/core/java/android/text/WordSegmentFinder.java
@@ -24,7 +24,7 @@
/**
* Implementation of {@link SegmentFinder} using words as the text segment. Word boundaries are
- * found using {@link WordIterator}. Whitespace characters are excluded, so they are not included in
+ * found using {@code WordIterator}. Whitespace characters are excluded, so they are not included in
* any text segments.
*
* <p>For example, the text "Hello, World!" would be subdivided into four text segments: "Hello",
diff --git a/core/java/android/text/flags/custom_locale_fallback.aconfig b/core/java/android/text/flags/custom_locale_fallback.aconfig
index 04e64f9..52fe883 100644
--- a/core/java/android/text/flags/custom_locale_fallback.aconfig
+++ b/core/java/android/text/flags/custom_locale_fallback.aconfig
@@ -4,5 +4,6 @@
name: "custom_locale_fallback"
namespace: "text"
description: "A feature flag that adds custom locale fallback to the vendor customization XML. This enables vendors to add their locale specific fonts, e.g. Japanese font."
- bug: ""
+ is_fixed_read_only: true
+ bug: "278768958"
}
diff --git a/core/java/android/text/flags/deprecate_fonts_xml.aconfig b/core/java/android/text/flags/deprecate_fonts_xml.aconfig
index 58dc210..5362138 100644
--- a/core/java/android/text/flags/deprecate_fonts_xml.aconfig
+++ b/core/java/android/text/flags/deprecate_fonts_xml.aconfig
@@ -4,5 +4,7 @@
name: "deprecate_fonts_xml"
namespace: "text"
description: "Feature flag for deprecating fonts.xml. By setting true for this feature flag, the new font configuration XML, /system/etc/font_fallback.xml is used. The new XML has a new syntax and flexibility of variable font declarations, but it is not compatible with the apps that reads fonts.xml. So, fonts.xml is maintained as a subset of the font_fallback.xml"
+ # Make read only, as it could be used before the Settings provider is initialized.
+ is_fixed_read_only: true
bug: "281769620"
}
diff --git a/core/java/android/text/flags/fix_line_height_for_locale.aconfig b/core/java/android/text/flags/fix_line_height_for_locale.aconfig
new file mode 100644
index 0000000..8696bfa
--- /dev/null
+++ b/core/java/android/text/flags/fix_line_height_for_locale.aconfig
@@ -0,0 +1,8 @@
+package: "com.android.text.flags"
+
+flag {
+ name: "fix_line_height_for_locale"
+ namespace: "text"
+ description: "Feature flag that preserve the line height of the TextView and EditText even if the the locale is different from Latin"
+ bug: "303326708"
+}
diff --git a/core/java/android/text/flags/optimized_font_loading.aconfig b/core/java/android/text/flags/optimized_font_loading.aconfig
new file mode 100644
index 0000000..0e4a69f
--- /dev/null
+++ b/core/java/android/text/flags/optimized_font_loading.aconfig
@@ -0,0 +1,11 @@
+package: "com.android.text.flags"
+
+flag {
+ name: "use_optimized_boottime_font_loading"
+ namespace: "text"
+ description: "Feature flag ensuring that font is loaded once and asynchronously."
+ # Make read only, as font loading is in the critical boot path which happens before the read-write
+ # flags propagate to the device.
+ is_fixed_read_only: true
+ bug: "304406888"
+}
diff --git a/core/java/android/util/Base64.java b/core/java/android/util/Base64.java
index 92abd7c..33cc5e3 100644
--- a/core/java/android/util/Base64.java
+++ b/core/java/android/util/Base64.java
@@ -26,6 +26,7 @@
* href="http://www.ietf.org/rfc/rfc2045.txt">2045</a> and <a
* href="http://www.ietf.org/rfc/rfc3548.txt">3548</a>.
*/
+@android.ravenwood.annotations.RavenwoodWholeClassKeep
public class Base64 {
/**
* Default values for encoder/decoder flags.
diff --git a/core/java/android/view/Choreographer.java b/core/java/android/view/Choreographer.java
index 1ab055a..a74cbe4 100644
--- a/core/java/android/view/Choreographer.java
+++ b/core/java/android/view/Choreographer.java
@@ -16,9 +16,11 @@
package android.view;
+import static android.view.flags.Flags.FLAG_EXPECTED_PRESENTATION_TIME_API;
import static android.view.DisplayEventReceiver.VSYNC_SOURCE_APP;
import static android.view.DisplayEventReceiver.VSYNC_SOURCE_SURFACE_FLINGER;
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
@@ -695,6 +697,7 @@
*/
@TestApi
@UnsupportedAppUsage
+ @FlaggedApi(FLAG_EXPECTED_PRESENTATION_TIME_API)
public long getFrameTimeNanos() {
synchronized (mLock) {
if (!mCallbacksRunning) {
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index 0b2b6ce..5069455 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -26,6 +26,7 @@
import android.annotation.RequiresPermission;
import android.annotation.SuppressLint;
import android.annotation.TestApi;
+import android.app.ActivityThread;
import android.app.KeyguardManager;
import android.app.WindowConfiguration;
import android.compat.annotation.UnsupportedAppUsage;
@@ -1366,7 +1367,8 @@
// form of the larger DISPLAY_CHANGED event
mGlobal.registerDisplayListener(toRegister, executor,
DisplayManager.EVENT_FLAG_HDR_SDR_RATIO_CHANGED
- | DisplayManagerGlobal.EVENT_DISPLAY_CHANGED);
+ | DisplayManagerGlobal.EVENT_DISPLAY_CHANGED,
+ ActivityThread.currentPackageName());
}
}
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/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/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 139c0be..2f3d73a 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -263,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);
@@ -1790,7 +1790,12 @@
public float xDpi;
public float yDpi;
+ // Some modes have peak refresh rate lower than the panel vsync rate.
public float refreshRate;
+ // Fixed rate of vsync deadlines for the panel.
+ // This can be higher then the peak refresh rate for some panel technologies
+ // See: VrrConfig.aidl
+ public float vsyncRate;
public long appVsyncOffsetNanos;
public long presentationDeadlineNanos;
public int[] supportedHdrTypes;
@@ -1811,6 +1816,7 @@
+ ", xDpi=" + xDpi
+ ", yDpi=" + yDpi
+ ", refreshRate=" + refreshRate
+ + ", vsyncRate=" + vsyncRate
+ ", appVsyncOffsetNanos=" + appVsyncOffsetNanos
+ ", presentationDeadlineNanos=" + presentationDeadlineNanos
+ ", supportedHdrTypes=" + Arrays.toString(supportedHdrTypes)
@@ -1828,6 +1834,7 @@
&& Float.compare(that.xDpi, xDpi) == 0
&& Float.compare(that.yDpi, yDpi) == 0
&& Float.compare(that.refreshRate, refreshRate) == 0
+ && Float.compare(that.vsyncRate, vsyncRate) == 0
&& appVsyncOffsetNanos == that.appVsyncOffsetNanos
&& presentationDeadlineNanos == that.presentationDeadlineNanos
&& Arrays.equals(supportedHdrTypes, that.supportedHdrTypes)
@@ -1836,8 +1843,9 @@
@Override
public int hashCode() {
- return Objects.hash(id, width, height, xDpi, yDpi, refreshRate, appVsyncOffsetNanos,
- presentationDeadlineNanos, group, Arrays.hashCode(supportedHdrTypes));
+ return Objects.hash(id, width, height, xDpi, yDpi, refreshRate, vsyncRate,
+ appVsyncOffsetNanos, presentationDeadlineNanos, group,
+ Arrays.hashCode(supportedHdrTypes));
}
}
@@ -3701,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.
*
@@ -3709,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;
}
@@ -4258,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.
@@ -4292,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
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index cfde400..e9d0e4c 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -9308,6 +9308,7 @@
structure.setAutofillType(autofillType);
structure.setAutofillHints(getAutofillHints());
structure.setAutofillValue(getAutofillValue());
+ structure.setIsCredential(isCredential());
}
structure.setImportantForAutofill(getImportantForAutofill());
structure.setReceiveContentMimeTypes(getReceiveContentMimeTypes());
@@ -12161,12 +12162,6 @@
* a precision touch gesture in a small area in either the X or Y dimension, such as
* an edge swipe or dragging a <code>SeekBar</code> thumb.</p>
*
- * <p>On Wear OS, these rects control where system-level swipe-to-dismiss gesture can start. If
- * the attribute {@code android:windowSwipeToDismiss} has been set to {@code false}, the system
- * will create an exclusion rect with size equal to the window frame size. In order words, the
- * system swipe-to-dismiss will not apply, and the app must handle gestural input within itself.
- * </p>
- *
* <p>Note: the system will put a limit of <code>200dp</code> on the vertical extent of the
* exclusions it takes into account. The limit does not apply while the navigation
* bar is {@link #SYSTEM_UI_FLAG_IMMERSIVE_STICKY stickily} hidden, nor to the
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index aa47f07..9392783 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -1549,7 +1549,8 @@
mHandler,
DisplayManager.EVENT_FLAG_DISPLAY_ADDED
| DisplayManager.EVENT_FLAG_DISPLAY_CHANGED
- | DisplayManager.EVENT_FLAG_DISPLAY_REMOVED);
+ | DisplayManager.EVENT_FLAG_DISPLAY_REMOVED,
+ mBasePackageName);
}
/**
@@ -1730,7 +1731,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 +2233,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 +3350,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 +3878,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 +3894,8 @@
mPendingTransitions.clear();
}
if (!performDraw(mActiveSurfaceSyncGroup)) {
- handleSyncRequestWhenNoAsyncDraw(mActiveSurfaceSyncGroup, mPendingTransaction,
- mLastPerformDrawSkippedReason);
+ handleSyncRequestWhenNoAsyncDraw(mActiveSurfaceSyncGroup, mHasPendingTransactions,
+ mPendingTransaction, mLastPerformDrawSkippedReason);
}
}
@@ -4774,8 +4773,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 +4788,8 @@
}
if (!usingAsyncReport) {
- handleSyncRequestWhenNoAsyncDraw(surfaceSyncGroup, pendingTransaction,
- "no async report");
+ handleSyncRequestWhenNoAsyncDraw(surfaceSyncGroup, pendingTransaction != null,
+ pendingTransaction, "no async report");
}
if (mPerformContentCapture) {
@@ -4800,13 +4799,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) {
@@ -5653,7 +5653,8 @@
mUpdateHdrSdrRatioInfo = true;
}
- private void updateColorModeIfNeeded(@ActivityInfo.ColorMode int colorMode) {
+ private void updateColorModeIfNeeded(@ActivityInfo.ColorMode int colorMode,
+ float desiredRatio) {
if (mAttachInfo.mThreadedRenderer == null) {
return;
}
@@ -5667,7 +5668,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();
@@ -8606,10 +8610,6 @@
mLastLayoutFrame.set(frame);
}
- if (mOnBackInvokedDispatcher.isSystemGestureExclusionNeeded()) {
- setRootSystemGestureExclusionRects(List.of(frame));
- }
-
final WindowConfiguration winConfig = getCompatWindowConfiguration();
mPendingBackDropFrame.set(mPendingDragResizing && !winConfig.useWindowFrameForBackdrop()
? winConfig.getMaxBounds()
@@ -9054,8 +9054,8 @@
mAdded = false;
AnimationHandler.removeRequestor(this);
}
- handleSyncRequestWhenNoAsyncDraw(mActiveSurfaceSyncGroup, mPendingTransaction,
- "shutting down VRI");
+ handleSyncRequestWhenNoAsyncDraw(mActiveSurfaceSyncGroup, mHasPendingTransactions,
+ mPendingTransaction, "shutting down VRI");
WindowManagerGlobal.getInstance().doRemoveView(this);
}
@@ -10548,6 +10548,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/ViewStructure.java b/core/java/android/view/ViewStructure.java
index 2c2ae06..bb2c7c8 100644
--- a/core/java/android/view/ViewStructure.java
+++ b/core/java/android/view/ViewStructure.java
@@ -397,6 +397,13 @@
public void setImportantForAutofill(@AutofillImportance int mode) {}
/**
+ * Sets whether the node is a credential. See {@link View#isCredential}.
+ *
+ * @hide
+ */
+ public void setIsCredential(boolean isCredential) {}
+
+ /**
* Sets the MIME types accepted by this view. See {@link View#getReceiveContentMimeTypes()}.
*
* <p>Should only be set when the node is used for Autofill or Content Capture purposes - it
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 2f4bea0..4f03ce9 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -17,6 +17,7 @@
package android.view;
import static android.content.pm.ActivityInfo.COLOR_MODE_DEFAULT;
+import static android.view.flags.Flags.FLAG_WM_DISPLAY_REFRESH_RATE_TEST;
import static android.view.View.STATUS_BAR_DISABLE_BACK;
import static android.view.View.STATUS_BAR_DISABLE_CLOCK;
import static android.view.View.STATUS_BAR_DISABLE_EXPAND;
@@ -82,6 +83,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 +1686,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 +1694,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) {
@@ -3113,7 +3116,7 @@
/**
* Never animate position changes of the window.
*
- * @see android.R.attr#Window_windowNoMoveAnimation
+ * @see android.R.styleable#Window_windowNoMoveAnimation
* {@hide}
*/
@UnsupportedAppUsage
@@ -3903,6 +3906,7 @@
* This value is ignored if {@link #preferredDisplayModeId} is set.
* @hide
*/
+ @FlaggedApi(FLAG_WM_DISPLAY_REFRESH_RATE_TEST)
@TestApi
public float preferredMinDisplayRefreshRate;
@@ -3912,6 +3916,7 @@
* This value is ignored if {@link #preferredDisplayModeId} is set.
* @hide
*/
+ @FlaggedApi(FLAG_WM_DISPLAY_REFRESH_RATE_TEST)
@TestApi
public float preferredMaxDisplayRefreshRate;
@@ -4314,6 +4319,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.
@@ -4526,7 +4534,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) {
@@ -4541,7 +4549,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;
@@ -4716,6 +4724,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
@@ -4865,6 +4906,7 @@
checkNonRecursiveParams();
out.writeTypedArray(paramsForRotation, 0 /* parcelableFlags */);
out.writeInt(mDisplayFlags);
+ out.writeFloat(mDesiredHdrHeadroom);
}
public static final @android.annotation.NonNull Parcelable.Creator<LayoutParams> CREATOR
@@ -4936,6 +4978,7 @@
forciblyShownTypes = in.readInt();
paramsForRotation = in.createTypedArray(LayoutParams.CREATOR);
mDisplayFlags = in.readInt();
+ mDesiredHdrHeadroom = in.readFloat();
}
@SuppressWarnings({"PointlessBitwiseExpression"})
@@ -5196,6 +5239,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;
@@ -5423,6 +5471,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);
@@ -5821,6 +5872,7 @@
*
* @hide
*/
+ @FlaggedApi("REPLACE_CONTENT_WITH_MIRROR")
@TestApi
@RequiresPermission(permission.ACCESS_SURFACE_FLINGER)
default boolean replaceContentOnDisplayWithMirror(int displayId, @NonNull Window window) {
@@ -5836,6 +5888,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/accessibility/AccessibilityInteractionClient.java b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
index 60f46e6..12ce0f4 100644
--- a/core/java/android/view/accessibility/AccessibilityInteractionClient.java
+++ b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
@@ -1731,6 +1731,9 @@
@Override
public void sendAttachOverlayResult(
@AccessibilityService.AttachOverlayResult int result, int interactionId) {
+ if (!Flags.a11yOverlayCallbacks()) {
+ return;
+ }
synchronized (mInstanceLock) {
if (mAttachAccessibilityOverlayCallbacks.contains(interactionId)) {
final Pair<Executor, IntConsumer> pair =
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/AccessibilityWindowInfo.java b/core/java/android/view/accessibility/AccessibilityWindowInfo.java
index 09b2f9c..fa0052c 100644
--- a/core/java/android/view/accessibility/AccessibilityWindowInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityWindowInfo.java
@@ -99,7 +99,6 @@
/** @hide */
public static final int UNDEFINED_CONNECTION_ID = -1;
/** @hide */
- @TestApi
public static final int UNDEFINED_WINDOW_ID = -1;
/** @hide */
public static final int ANY_WINDOW_ID = -2;
diff --git a/core/java/android/view/accessibility/flags/accessibility_flags.aconfig b/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
index e31ad82..cc612ed 100644
--- a/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
+++ b/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
@@ -5,4 +5,18 @@
name: "force_invert_color"
description: "Enable force force-dark for smart inversion and dark theme everywhere"
bug: "282821643"
-}
\ No newline at end of file
+}
+
+flag {
+ namespace: "accessibility"
+ name: "allow_shortcut_chooser_on_lockscreen"
+ description: "Allows the a11y shortcut disambig dialog to appear on the lockscreen"
+ bug: "303871725"
+}
+
+flag {
+ name: "a11y_overlay_callbacks"
+ namespace: "accessibility"
+ description: "Whether to allow the passing of result callbacks when attaching a11y overlays."
+ bug: "304478691"
+}
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 34e4c37..a40ff64 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -16,7 +16,6 @@
package android.view.autofill;
-import static android.Manifest.permission.PROVIDE_OWN_AUTOFILL_SUGGESTIONS;
import static android.service.autofill.FillRequest.FLAG_IME_SHOWING;
import static android.service.autofill.FillRequest.FLAG_MANUAL_REQUEST;
import static android.service.autofill.FillRequest.FLAG_PASSWORD_INPUT_TYPE;
@@ -31,12 +30,10 @@
import static android.view.autofill.Helper.toList;
import android.accessibilityservice.AccessibilityServiceInfo;
-import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresFeature;
-import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.TestApi;
@@ -53,19 +50,15 @@
import android.content.pm.ResolveInfo;
import android.graphics.Rect;
import android.metrics.LogMaker;
-import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
-import android.os.CancellationSignal;
import android.os.Handler;
import android.os.IBinder;
-import android.os.ICancellationSignal;
import android.os.Looper;
import android.os.Parcelable;
import android.os.RemoteException;
import android.os.SystemClock;
import android.service.autofill.AutofillService;
-import android.service.autofill.FillCallback;
import android.service.autofill.FillEventHistory;
import android.service.autofill.Flags;
import android.service.autofill.IFillCallback;
@@ -89,7 +82,6 @@
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityNodeProvider;
import android.view.accessibility.AccessibilityWindowInfo;
-import android.view.inputmethod.InlineSuggestionsRequest;
import android.view.inputmethod.InputMethodManager;
import android.widget.CheckBox;
import android.widget.DatePicker;
@@ -119,7 +111,6 @@
import java.util.List;
import java.util.Objects;
import java.util.Set;
-import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;
import sun.misc.Cleaner;
@@ -189,12 +180,6 @@
* shows an autofill save UI if the value of savable views have changed. If the user selects the
* option to Save, the current value of the views is then sent to the autofill service.
*
- * <p>There is another choice for the application to provide it's datasets to the Autofill framework
- * by setting an {@link AutofillRequestCallback} through
- * {@link #setAutofillRequestCallback(Executor, AutofillRequestCallback)}. The application can use
- * its callback instead of the default {@link AutofillService}. See
- * {@link AutofillRequestCallback} for more details.
- *
* <h3 id="additional-notes">Additional notes</h3>
*
* <p>It is safe to call <code>AutofillManager</code> methods from any thread.
@@ -334,7 +319,6 @@
/** @hide */ public static final int FLAG_ADD_CLIENT_DEBUG = 0x2;
/** @hide */ public static final int FLAG_ADD_CLIENT_VERBOSE = 0x4;
/** @hide */ public static final int FLAG_ADD_CLIENT_ENABLED_FOR_AUGMENTED_AUTOFILL_ONLY = 0x8;
- /** @hide */ public static final int FLAG_ENABLED_CLIENT_SUGGESTIONS = 0x20;
// NOTE: flag below is used by the session start receiver only, hence it can have values above
/** @hide */ public static final int RECEIVER_FLAG_SESSION_FOR_AUGMENTED_AUTOFILL_ONLY = 0x1;
@@ -676,11 +660,6 @@
@GuardedBy("mLock")
private boolean mEnabledForAugmentedAutofillOnly;
- @GuardedBy("mLock")
- @Nullable private AutofillRequestCallback mAutofillRequestCallback;
- @GuardedBy("mLock")
- @Nullable private Executor mRequestCallbackExecutor;
-
private boolean mScreenHasCredmanField;
/**
@@ -2434,38 +2413,6 @@
return new AutofillId(parent.getAutofillViewId(), virtualId);
}
- /**
- * Sets the client's suggestions callback for autofill.
- *
- * @see AutofillRequestCallback
- *
- * @param executor specifies the thread upon which the callbacks will be invoked.
- * @param callback which handles autofill request to provide client's suggestions.
- */
- @RequiresPermission(PROVIDE_OWN_AUTOFILL_SUGGESTIONS)
- public void setAutofillRequestCallback(@NonNull @CallbackExecutor Executor executor,
- @NonNull AutofillRequestCallback callback) {
- if (mContext.checkSelfPermission(PROVIDE_OWN_AUTOFILL_SUGGESTIONS)
- != PackageManager.PERMISSION_GRANTED) {
- throw new SecurityException("Requires PROVIDE_OWN_AUTOFILL_SUGGESTIONS permission!");
- }
-
- synchronized (mLock) {
- mRequestCallbackExecutor = executor;
- mAutofillRequestCallback = callback;
- }
- }
-
- /**
- * clears the client's suggestions callback for autofill.
- */
- public void clearAutofillRequestCallback() {
- synchronized (mLock) {
- mRequestCallbackExecutor = null;
- mAutofillRequestCallback = null;
- }
- }
-
@GuardedBy("mLock")
private void startSessionLocked(@NonNull AutofillId id, @NonNull Rect bounds,
@NonNull AutofillValue value, int flags) {
@@ -2526,13 +2473,6 @@
}
}
- if (mAutofillRequestCallback != null) {
- if (sDebug) {
- Log.d(TAG, "startSession with the client suggestions provider");
- }
- flags |= FLAG_ENABLED_CLIENT_SUGGESTIONS;
- }
-
mService.startSession(client.autofillClientGetActivityToken(),
mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(),
mCallback != null, flags, clientActivity,
@@ -2890,28 +2830,6 @@
}
}
- private void onFillRequest(InlineSuggestionsRequest request,
- CancellationSignal cancellationSignal, FillCallback callback) {
- final AutofillRequestCallback autofillRequestCallback;
- final Executor executor;
- synchronized (mLock) {
- autofillRequestCallback = mAutofillRequestCallback;
- executor = mRequestCallbackExecutor;
- }
- if (autofillRequestCallback != null && executor != null) {
- final long ident = Binder.clearCallingIdentity();
- try {
- executor.execute(() ->
- autofillRequestCallback.onFillRequest(
- request, cancellationSignal, callback));
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- } else {
- callback.onSuccess(null);
- }
- }
-
/** @hide */
public static final int SET_STATE_FLAG_ENABLED = 0x01;
/** @hide */
@@ -4495,23 +4413,6 @@
}
@Override
- public void requestFillFromClient(int id, InlineSuggestionsRequest request,
- IFillCallback callback) {
- final AutofillManager afm = mAfm.get();
- if (afm != null) {
- ICancellationSignal transport = CancellationSignal.createTransport();
- try {
- callback.onCancellable(transport);
- } catch (RemoteException e) {
- Slog.w(TAG, "Error requesting a cancellation", e);
- }
-
- afm.onFillRequest(request, CancellationSignal.fromTransport(transport),
- new FillCallback(callback, id));
- }
- }
-
- @Override
public void notifyFillDialogTriggerIds(List<AutofillId> ids) {
final AutofillManager afm = mAfm.get();
if (afm != null) {
diff --git a/core/java/android/view/autofill/AutofillRequestCallback.java b/core/java/android/view/autofill/AutofillRequestCallback.java
deleted file mode 100644
index e632a58..0000000
--- a/core/java/android/view/autofill/AutofillRequestCallback.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view.autofill;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.os.CancellationSignal;
-import android.service.autofill.FillCallback;
-import android.view.inputmethod.InlineSuggestionsRequest;
-
-/**
- * <p>This class is used to provide some input suggestions to the Autofill framework.
- *
- * <P>When the user is requested to input something, Autofill will try to query input suggestions
- * for the user choosing. If the application want to provide some internal input suggestions,
- * implements this callback and register via
- * {@link AutofillManager#setAutofillRequestCallback(java.util.concurrent.Executor,
- * AutofillRequestCallback)}. Autofill will callback the
- * {@link #onFillRequest(InlineSuggestionsRequest, CancellationSignal, FillCallback)} to request
- * input suggestions.
- *
- * <P>To make sure the callback to take effect, must register before the autofill session starts.
- * If the autofill session is started, calls {@link AutofillManager#cancel()} to finish current
- * session, and then the callback will be used at the next restarted session.
- *
- * <P>To create a {@link android.service.autofill.FillResponse}, application should fetch
- * {@link AutofillId}s from its view structure. Below is an example:
- * <pre class="prettyprint">
- * AutofillId usernameId = findViewById(R.id.username).getAutofillId();
- * AutofillId passwordId = findViewById(R.id.password).getAutofillId();
- * </pre>
- * To learn more about creating a {@link android.service.autofill.FillResponse}, read
- * <a href="/guide/topics/text/autofill-services#fill">Fill out client views</a>.
- *
- * <P>To fallback to the default {@link android.service.autofill.AutofillService}, just respond
- * a null of the {@link android.service.autofill.FillResponse}. And then Autofill will do a fill
- * request with the default {@link android.service.autofill.AutofillService}. Or clear the callback
- * from {@link AutofillManager} via {@link AutofillManager#clearAutofillRequestCallback()}. If the
- * client would like to keep no suggestions for the field, respond with an empty
- * {@link android.service.autofill.FillResponse} which has no dataset.
- *
- * <P>IMPORTANT: This should not be used for displaying anything other than input suggestions, or
- * the keyboard may choose to block your app from the inline strip.
- */
-public interface AutofillRequestCallback {
- /**
- * Called by the Android system to decide if a screen can be autofilled by the callback.
- *
- * @param inlineSuggestionsRequest the {@link InlineSuggestionsRequest request} to handle if
- * currently inline suggestions are supported and can be displayed.
- * @param cancellationSignal signal for observing cancellation requests. The system will use
- * this to notify you that the fill result is no longer needed and you should stop
- * handling this fill request in order to save resources.
- * @param callback object used to notify the result of the request.
- */
- void onFillRequest(@Nullable InlineSuggestionsRequest inlineSuggestionsRequest,
- @NonNull CancellationSignal cancellationSignal, @NonNull FillCallback callback);
-}
diff --git a/core/java/android/view/autofill/IAutoFillManagerClient.aidl b/core/java/android/view/autofill/IAutoFillManagerClient.aidl
index 6e13097..917a974 100644
--- a/core/java/android/view/autofill/IAutoFillManagerClient.aidl
+++ b/core/java/android/view/autofill/IAutoFillManagerClient.aidl
@@ -24,11 +24,9 @@
import android.content.IntentSender;
import android.graphics.Rect;
import android.os.IBinder;
-import android.service.autofill.IFillCallback;
import android.view.autofill.AutofillId;
import android.view.autofill.AutofillValue;
import android.view.autofill.IAutofillWindowPresenter;
-import android.view.inputmethod.InlineSuggestionsRequest;
import android.view.KeyEvent;
import com.android.internal.os.IResultReceiver;
@@ -149,12 +147,6 @@
void requestShowSoftInput(in AutofillId id);
/**
- * Requests to determine if a screen can be autofilled by the client app.
- */
- void requestFillFromClient(int id, in InlineSuggestionsRequest request,
- in IFillCallback callback);
-
- /**
* Notifies autofill ids that require to show the fill dialog.
*/
void notifyFillDialogTriggerIds(in List<AutofillId> ids);
diff --git a/core/java/android/view/contentcapture/ContentCaptureManager.java b/core/java/android/view/contentcapture/ContentCaptureManager.java
index 2c7d326..42b3e38 100644
--- a/core/java/android/view/contentcapture/ContentCaptureManager.java
+++ b/core/java/android/view/contentcapture/ContentCaptureManager.java
@@ -60,7 +60,9 @@
import java.lang.annotation.RetentionPolicy;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
@@ -377,6 +379,30 @@
public static final String DEVICE_CONFIG_PROPERTY_CONTENT_PROTECTION_BUFFER_SIZE =
"content_protection_buffer_size";
+ /**
+ * Sets the config for content protection required groups.
+ *
+ * @hide
+ */
+ public static final String DEVICE_CONFIG_PROPERTY_CONTENT_PROTECTION_REQUIRED_GROUPS_CONFIG =
+ "content_protection_required_groups_config";
+
+ /**
+ * Sets the config for content protection optional groups.
+ *
+ * @hide
+ */
+ public static final String DEVICE_CONFIG_PROPERTY_CONTENT_PROTECTION_OPTIONAL_GROUPS_CONFIG =
+ "content_protection_optional_groups_config";
+
+ /**
+ * Sets the threshold for content protection optional groups.
+ *
+ * @hide
+ */
+ public static final String DEVICE_CONFIG_PROPERTY_CONTENT_PROTECTION_OPTIONAL_GROUPS_THRESHOLD =
+ "content_protection_optional_groups_threshold";
+
/** @hide */
@TestApi
public static final int LOGGING_LEVEL_OFF = 0;
@@ -417,6 +443,18 @@
public static final int DEFAULT_CONTENT_PROTECTION_APPS_BLOCKLIST_SIZE = 5000;
/** @hide */
public static final int DEFAULT_CONTENT_PROTECTION_BUFFER_SIZE = 150;
+ /** @hide */
+ public static final List<List<String>> DEFAULT_CONTENT_PROTECTION_REQUIRED_GROUPS =
+ Collections.emptyList();
+ /** @hide */
+ public static final String DEFAULT_CONTENT_PROTECTION_REQUIRED_GROUPS_CONFIG = "";
+ /** @hide */
+ public static final List<List<String>> DEFAULT_CONTENT_PROTECTION_OPTIONAL_GROUPS =
+ Collections.emptyList();
+ /** @hide */
+ public static final String DEFAULT_CONTENT_PROTECTION_OPTIONAL_GROUPS_CONFIG = "";
+ /** @hide */
+ public static final int DEFAULT_CONTENT_PROTECTION_OPTIONAL_GROUPS_THRESHOLD = 0;
private final Object mLock = new Object();
diff --git a/core/java/android/view/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/contentcapture/MainContentCaptureSession.java b/core/java/android/view/contentcapture/MainContentCaptureSession.java
index 2241fd5..b44d6a4 100644
--- a/core/java/android/view/contentcapture/MainContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/MainContentCaptureSession.java
@@ -317,16 +317,14 @@
}
}
- // Should not be possible for mComponentName to be null here but check anyway
- if (mManager.mOptions.contentProtectionOptions.enableReceiver
- && mManager.getContentProtectionEventBuffer() != null
- && mComponentName != null) {
+ if (isContentProtectionEnabled()) {
mContentProtectionEventProcessor =
new ContentProtectionEventProcessor(
mManager.getContentProtectionEventBuffer(),
mHandler,
mSystemServerInterface,
- mComponentName.getPackageName());
+ mComponentName.getPackageName(),
+ mManager.mOptions.contentProtectionOptions);
} else {
mContentProtectionEventProcessor = null;
}
@@ -956,4 +954,15 @@
private boolean isContentCaptureReceiverEnabled() {
return mManager.mOptions.enableReceiver;
}
+
+ @UiThread
+ private boolean isContentProtectionEnabled() {
+ // Should not be possible for mComponentName to be null here but check anyway
+ // Should not be possible for groups to be empty if receiver is enabled but check anyway
+ return mManager.mOptions.contentProtectionOptions.enableReceiver
+ && mManager.getContentProtectionEventBuffer() != null
+ && mComponentName != null
+ && (!mManager.mOptions.contentProtectionOptions.requiredGroups.isEmpty()
+ || !mManager.mOptions.contentProtectionOptions.optionalGroups.isEmpty());
+ }
}
diff --git a/core/java/android/view/contentprotection/ContentProtectionEventProcessor.java b/core/java/android/view/contentprotection/ContentProtectionEventProcessor.java
index b44abf3..aaf90bd 100644
--- a/core/java/android/view/contentprotection/ContentProtectionEventProcessor.java
+++ b/core/java/android/view/contentprotection/ContentProtectionEventProcessor.java
@@ -19,9 +19,9 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UiThread;
+import android.content.ContentCaptureOptions;
import android.content.pm.ParceledListSlice;
import android.os.Handler;
-import android.text.InputType;
import android.util.Log;
import android.view.contentcapture.ContentCaptureEvent;
import android.view.contentcapture.IContentCaptureManager;
@@ -33,10 +33,10 @@
import java.time.Duration;
import java.time.Instant;
import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashSet;
+import java.util.Collection;
import java.util.List;
import java.util.Set;
+import java.util.stream.Stream;
/**
* Main entry point for processing {@link ContentCaptureEvent} for the content protection flow.
@@ -47,33 +47,13 @@
private static final String TAG = "ContentProtectionEventProcessor";
- private static final List<Integer> PASSWORD_FIELD_INPUT_TYPES =
- Collections.unmodifiableList(
- Arrays.asList(
- InputType.TYPE_NUMBER_VARIATION_PASSWORD,
- InputType.TYPE_TEXT_VARIATION_PASSWORD,
- InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD,
- InputType.TYPE_TEXT_VARIATION_WEB_PASSWORD));
-
- private static final List<String> PASSWORD_TEXTS =
- Collections.unmodifiableList(
- Arrays.asList("password", "pass word", "code", "pin", "credential"));
-
- private static final List<String> ADDITIONAL_SUSPICIOUS_TEXTS =
- Collections.unmodifiableList(
- Arrays.asList("user", "mail", "phone", "number", "login", "log in", "sign in"));
-
private static final Duration MIN_DURATION_BETWEEN_FLUSHING = Duration.ofSeconds(3);
- private static final String ANDROID_CLASS_NAME_PREFIX = "android.";
-
private static final Set<Integer> EVENT_TYPES_TO_STORE =
- Collections.unmodifiableSet(
- new HashSet<>(
- Arrays.asList(
- ContentCaptureEvent.TYPE_VIEW_APPEARED,
- ContentCaptureEvent.TYPE_VIEW_DISAPPEARED,
- ContentCaptureEvent.TYPE_VIEW_TEXT_CHANGED)));
+ Set.of(
+ ContentCaptureEvent.TYPE_VIEW_APPEARED,
+ ContentCaptureEvent.TYPE_VIEW_DISAPPEARED,
+ ContentCaptureEvent.TYPE_VIEW_TEXT_CHANGED);
private static final int RESET_LOGIN_TOTAL_EVENTS_TO_PROCESS = 150;
@@ -85,11 +65,7 @@
@NonNull private final String mPackageName;
- @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
- public boolean mPasswordFieldDetected = false;
-
- @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
- public boolean mSuspiciousTextDetected = false;
+ @NonNull private final ContentCaptureOptions.ContentProtectionOptions mOptions;
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
@Nullable
@@ -97,15 +73,32 @@
private int mResetLoginRemainingEventsToProcess;
+ private boolean mAnyGroupFound = false;
+
+ // Ordered by priority
+ private final List<SearchGroup> mGroupsRequired;
+
+ // Ordered by priority
+ private final List<SearchGroup> mGroupsOptional;
+
+ // Ordered by priority
+ private final List<SearchGroup> mGroupsAll;
+
public ContentProtectionEventProcessor(
@NonNull RingBuffer<ContentCaptureEvent> eventBuffer,
@NonNull Handler handler,
@NonNull IContentCaptureManager contentCaptureManager,
- @NonNull String packageName) {
+ @NonNull String packageName,
+ @NonNull ContentCaptureOptions.ContentProtectionOptions options) {
mEventBuffer = eventBuffer;
mHandler = handler;
mContentCaptureManager = contentCaptureManager;
mPackageName = packageName;
+ mOptions = options;
+ mGroupsRequired = options.requiredGroups.stream().map(SearchGroup::new).toList();
+ mGroupsOptional = options.optionalGroups.stream().map(SearchGroup::new).toList();
+ mGroupsAll =
+ Stream.of(mGroupsRequired, mGroupsOptional).flatMap(Collection::stream).toList();
}
/** Main entry point for {@link ContentCaptureEvent} processing. */
@@ -130,9 +123,31 @@
@UiThread
private void processViewAppearedEvent(@NonNull ContentCaptureEvent event) {
- mPasswordFieldDetected |= isPasswordField(event);
- mSuspiciousTextDetected |= isSuspiciousText(event);
- if (mPasswordFieldDetected && mSuspiciousTextDetected) {
+ ViewNode viewNode = event.getViewNode();
+ String eventText = ContentProtectionUtils.getEventTextLower(event);
+ String viewNodeText = ContentProtectionUtils.getViewNodeTextLower(viewNode);
+ String hintText = ContentProtectionUtils.getHintTextLower(viewNode);
+
+ mGroupsAll.stream()
+ .filter(group -> !group.mFound)
+ .filter(
+ group ->
+ group.matches(eventText)
+ || group.matches(viewNodeText)
+ || group.matches(hintText))
+ .findFirst()
+ .ifPresent(
+ group -> {
+ group.mFound = true;
+ mAnyGroupFound = true;
+ });
+
+ boolean loginDetected =
+ mGroupsRequired.stream().allMatch(group -> group.mFound)
+ && mGroupsOptional.stream().filter(group -> group.mFound).count()
+ >= mOptions.optionalGroupsThreshold;
+
+ if (loginDetected) {
loginDetected();
} else {
maybeResetLoginFlags();
@@ -150,14 +165,13 @@
@UiThread
private void resetLoginFlags() {
- mPasswordFieldDetected = false;
- mSuspiciousTextDetected = false;
- mResetLoginRemainingEventsToProcess = 0;
+ mGroupsAll.forEach(group -> group.mFound = false);
+ mAnyGroupFound = false;
}
@UiThread
private void maybeResetLoginFlags() {
- if (mPasswordFieldDetected || mSuspiciousTextDetected) {
+ if (mAnyGroupFound) {
if (mResetLoginRemainingEventsToProcess <= 0) {
mResetLoginRemainingEventsToProcess = RESET_LOGIN_TOTAL_EVENTS_TO_PROCESS;
} else {
@@ -194,61 +208,21 @@
}
}
- private boolean isPasswordField(@NonNull ContentCaptureEvent event) {
- return isPasswordField(event.getViewNode());
- }
+ private static final class SearchGroup {
- private boolean isPasswordField(@Nullable ViewNode viewNode) {
- if (viewNode == null) {
- return false;
+ @NonNull private final List<String> mSearchStrings;
+
+ public boolean mFound = false;
+
+ SearchGroup(@NonNull List<String> searchStrings) {
+ mSearchStrings = searchStrings;
}
- return isAndroidPasswordField(viewNode) || isWebViewPasswordField(viewNode);
- }
- private boolean isAndroidPasswordField(@NonNull ViewNode viewNode) {
- if (!isAndroidViewNode(viewNode)) {
- return false;
+ public boolean matches(@Nullable String text) {
+ if (text == null) {
+ return false;
+ }
+ return mSearchStrings.stream().anyMatch(text::contains);
}
- int inputType = viewNode.getInputType();
- return PASSWORD_FIELD_INPUT_TYPES.stream()
- .anyMatch(passwordInputType -> (inputType & passwordInputType) != 0);
- }
-
- private boolean isWebViewPasswordField(@NonNull ViewNode viewNode) {
- if (viewNode.getClassName() != null) {
- return false;
- }
- return isPasswordText(ContentProtectionUtils.getViewNodeText(viewNode));
- }
-
- private boolean isAndroidViewNode(@NonNull ViewNode viewNode) {
- String className = viewNode.getClassName();
- return className != null && className.startsWith(ANDROID_CLASS_NAME_PREFIX);
- }
-
- private boolean isSuspiciousText(@NonNull ContentCaptureEvent event) {
- return isSuspiciousText(ContentProtectionUtils.getEventText(event))
- || isSuspiciousText(ContentProtectionUtils.getViewNodeText(event));
- }
-
- private boolean isSuspiciousText(@Nullable String text) {
- if (text == null) {
- return false;
- }
- if (isPasswordText(text)) {
- return true;
- }
- String lowerCaseText = text.toLowerCase();
- return ADDITIONAL_SUSPICIOUS_TEXTS.stream()
- .anyMatch(suspiciousText -> lowerCaseText.contains(suspiciousText));
- }
-
- private boolean isPasswordText(@Nullable String text) {
- if (text == null) {
- return false;
- }
- String lowerCaseText = text.toLowerCase();
- return PASSWORD_TEXTS.stream()
- .anyMatch(passwordText -> lowerCaseText.contains(passwordText));
}
}
diff --git a/core/java/android/view/contentprotection/ContentProtectionUtils.java b/core/java/android/view/contentprotection/ContentProtectionUtils.java
index 9abf6f1..1ecac7f 100644
--- a/core/java/android/view/contentprotection/ContentProtectionUtils.java
+++ b/core/java/android/view/contentprotection/ContentProtectionUtils.java
@@ -28,33 +28,39 @@
*/
public final class ContentProtectionUtils {
- /** Returns the text extracted directly from the {@link ContentCaptureEvent}, if set. */
+ /** Returns the lowercase text extracted from the {@link ContentCaptureEvent}, if set. */
@Nullable
- public static String getEventText(@NonNull ContentCaptureEvent event) {
+ public static String getEventTextLower(@NonNull ContentCaptureEvent event) {
CharSequence text = event.getText();
if (text == null) {
return null;
}
- return text.toString();
+ return text.toString().toLowerCase();
}
- /** Returns the text extracted from the event's {@link ViewNode}, if set. */
+ /** Returns the lowercase text extracted from the {@link ViewNode}, if set. */
@Nullable
- public static String getViewNodeText(@NonNull ContentCaptureEvent event) {
- ViewNode viewNode = event.getViewNode();
+ public static String getViewNodeTextLower(@Nullable ViewNode viewNode) {
if (viewNode == null) {
return null;
}
- return getViewNodeText(viewNode);
- }
-
- /** Returns the text extracted directly from the {@link ViewNode}, if set. */
- @Nullable
- public static String getViewNodeText(@NonNull ViewNode viewNode) {
CharSequence text = viewNode.getText();
if (text == null) {
return null;
}
- return text.toString();
+ return text.toString().toLowerCase();
+ }
+
+ /** Returns the lowercase hint text extracted from the {@link ViewNode}, if set. */
+ @Nullable
+ public static String getHintTextLower(@Nullable ViewNode viewNode) {
+ if (viewNode == null) {
+ return null;
+ }
+ String text = viewNode.getHint();
+ if (text == null) {
+ return null;
+ }
+ return text.toLowerCase();
}
}
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/refresh_rate_flags.aconfig b/core/java/android/view/flags/refresh_rate_flags.aconfig
index 56b5fac..fd96890 100644
--- a/core/java/android/view/flags/refresh_rate_flags.aconfig
+++ b/core/java/android/view/flags/refresh_rate_flags.aconfig
@@ -26,4 +26,11 @@
namespace: "core_graphics"
description: "Enable the `setFrameRate` callback"
bug: "299946220"
+}
+
+flag {
+ name: "wm_display_refresh_rate_test"
+ namespace: "core_graphics"
+ description: "Adds WindowManager display refresh rate fields to test API"
+ bug: "304475199"
}
\ 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/window/BackEvent.java b/core/java/android/window/BackEvent.java
index f53737a..5562360 100644
--- a/core/java/android/window/BackEvent.java
+++ b/core/java/android/window/BackEvent.java
@@ -49,7 +49,7 @@
private final int mSwipeEdge;
/**
- * Creates a new {@link BackMotionEvent} instance.
+ * Creates a new {@link BackEvent} instance.
*
* @param touchX Absolute X location of the touch point of this event.
* @param touchY Absolute Y location of the touch point of this event.
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/SurfaceSyncGroup.java b/core/java/android/window/SurfaceSyncGroup.java
index 5d14698..eb9d62b 100644
--- a/core/java/android/window/SurfaceSyncGroup.java
+++ b/core/java/android/window/SurfaceSyncGroup.java
@@ -263,6 +263,7 @@
Trace.instantForTrack(Trace.TRACE_TAG_VIEW, mTrackName, "markSyncReady");
}
synchronized (mLock) {
+ toggleTimeout(false);
if (mHasWMSync) {
try {
WindowManagerGlobal.getWindowManagerService().markSurfaceSyncGroupReady(mToken);
diff --git a/core/java/android/window/SystemPerformanceHinter.java b/core/java/android/window/SystemPerformanceHinter.java
index e1a2b94..2736b68 100644
--- a/core/java/android/window/SystemPerformanceHinter.java
+++ b/core/java/android/window/SystemPerformanceHinter.java
@@ -211,7 +211,11 @@
session.displayId);
mTransaction.setFrameRateSelectionStrategy(displaySurfaceControl,
FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN);
- mTransaction.setFrameRateCategory(displaySurfaceControl, FRAME_RATE_CATEGORY_HIGH);
+ // 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.displayId + "-"
+ session.reason, session.traceCookie);
@@ -251,16 +255,21 @@
session.displayId);
mTransaction.setFrameRateSelectionStrategy(displaySurfaceControl,
FRAME_RATE_SELECTION_STRATEGY_SELF);
- mTransaction.setFrameRateCategory(displaySurfaceControl, FRAME_RATE_CATEGORY_DEFAULT);
+ // 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);
diff --git a/core/java/android/window/TaskFragmentOperation.java b/core/java/android/window/TaskFragmentOperation.java
index 43fa0be..4e0f9a5 100644
--- a/core/java/android/window/TaskFragmentOperation.java
+++ b/core/java/android/window/TaskFragmentOperation.java
@@ -88,6 +88,26 @@
*/
public static final int OP_TYPE_SET_ISOLATED_NAVIGATION = 11;
+ /**
+ * Reorders the TaskFragment to be the bottom-most in the Task. Note that this op will bring the
+ * TaskFragment to the bottom of the Task below all the other Activities and TaskFragments.
+ *
+ * This is only allowed for system organizers. See
+ * {@link com.android.server.wm.TaskFragmentOrganizerController#registerOrganizer(
+ * ITaskFragmentOrganizer, boolean)}
+ */
+ public static final int OP_TYPE_REORDER_TO_BOTTOM_OF_TASK = 12;
+
+ /**
+ * Reorders the TaskFragment to be the top-most in the Task. Note that this op will bring the
+ * TaskFragment to the top of the Task above all the other Activities and TaskFragments.
+ *
+ * This is only allowed for system organizers. See
+ * {@link com.android.server.wm.TaskFragmentOrganizerController#registerOrganizer(
+ * ITaskFragmentOrganizer, boolean)}
+ */
+ public static final int OP_TYPE_REORDER_TO_TOP_OF_TASK = 13;
+
@IntDef(prefix = { "OP_TYPE_" }, value = {
OP_TYPE_UNKNOWN,
OP_TYPE_CREATE_TASK_FRAGMENT,
@@ -101,7 +121,9 @@
OP_TYPE_SET_ANIMATION_PARAMS,
OP_TYPE_SET_RELATIVE_BOUNDS,
OP_TYPE_REORDER_TO_FRONT,
- OP_TYPE_SET_ISOLATED_NAVIGATION
+ OP_TYPE_SET_ISOLATED_NAVIGATION,
+ OP_TYPE_REORDER_TO_BOTTOM_OF_TASK,
+ OP_TYPE_REORDER_TO_TOP_OF_TASK,
})
@Retention(RetentionPolicy.SOURCE)
public @interface OperationType {}
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/WindowProviderService.java b/core/java/android/window/WindowProviderService.java
index 611da3c..0cbfcc5 100644
--- a/core/java/android/window/WindowProviderService.java
+++ b/core/java/android/window/WindowProviderService.java
@@ -155,7 +155,7 @@
}
/**
- * Override {@link Service}'s empty implementation and listen to {@link ActivityThread} for
+ * Override {@link Service}'s empty implementation and listen to {@code ActivityThread} for
* low memory and trim memory events.
*/
@Override
diff --git a/core/java/android/window/flags/window_surfaces.aconfig b/core/java/android/window/flags/window_surfaces.aconfig
index 1b98806..ccbf4a9 100644
--- a/core/java/android/window/flags/window_surfaces.aconfig
+++ b/core/java/android/window/flags/window_surfaces.aconfig
@@ -9,3 +9,11 @@
is_fixed_read_only: true
bug: "292032926"
}
+
+flag {
+ namespace: "window_surfaces"
+ name: "explicit_refresh_rate_hints"
+ description: "Performance related hints during transitions"
+ is_fixed_read_only: true
+ bug: "300019131"
+}
diff --git a/core/java/android/window/flags/windowing_frontend.aconfig b/core/java/android/window/flags/windowing_frontend.aconfig
index 7c931cd..392aa1b 100644
--- a/core/java/android/window/flags/windowing_frontend.aconfig
+++ b/core/java/android/window/flags/windowing_frontend.aconfig
@@ -13,3 +13,11 @@
description: "On close to square display, when necessary, configuration includes status bar"
bug: "291870756"
}
+
+flag {
+ name: "dimmer_refactor"
+ namespace: "windowing_frontend"
+ description: "Refactor dim to fix flickers"
+ bug: "281632483,295291019"
+ is_fixed_read_only: true
+}
\ No newline at end of file
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/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/DisplayResolutionTracker.java b/core/java/com/android/internal/jank/DisplayResolutionTracker.java
index 72a1bac..ca6c54d 100644
--- a/core/java/com/android/internal/jank/DisplayResolutionTracker.java
+++ b/core/java/com/android/internal/jank/DisplayResolutionTracker.java
@@ -24,6 +24,7 @@
import android.annotation.IntDef;
import android.annotation.Nullable;
+import android.app.ActivityThread;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManagerGlobal;
import android.os.Handler;
@@ -147,7 +148,8 @@
public void registerDisplayListener(DisplayManager.DisplayListener listener) {
manager.registerDisplayListener(listener, handler,
DisplayManager.EVENT_FLAG_DISPLAY_ADDED
- | DisplayManager.EVENT_FLAG_DISPLAY_CHANGED);
+ | DisplayManager.EVENT_FLAG_DISPLAY_CHANGED,
+ ActivityThread.currentPackageName());
}
@Override
diff --git a/core/java/com/android/internal/os/BatteryStatsHistory.java b/core/java/com/android/internal/os/BatteryStatsHistory.java
index d3103f1..0399430 100644
--- a/core/java/com/android/internal/os/BatteryStatsHistory.java
+++ b/core/java/com/android/internal/os/BatteryStatsHistory.java
@@ -37,7 +37,6 @@
import android.util.AtomicFile;
import android.util.Slog;
import android.util.SparseArray;
-import android.util.TimeUtils;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -78,7 +77,7 @@
private static final String TAG = "BatteryStatsHistory";
// Current on-disk Parcel version. Must be updated when the format of the parcelable changes
- private static final int VERSION = 209;
+ private static final int VERSION = 210;
private static final String HISTORY_DIR = "battery-history";
private static final String FILE_SUFFIX = ".bh";
@@ -194,10 +193,11 @@
private int mNextHistoryTagIdx = 0;
private int mNumHistoryTagChars = 0;
private int mHistoryBufferLastPos = -1;
- private long mLastHistoryElapsedRealtimeMs = 0;
private long mTrackRunningHistoryElapsedRealtimeMs = 0;
private long mTrackRunningHistoryUptimeMs = 0;
- private long mHistoryBaseTimeMs;
+ private final MonotonicClock mMonotonicClock;
+ // Monotonic time when we started writing to the history buffer
+ private long mHistoryBufferStartTime;
private final ArraySet<PowerStats.Descriptor> mWrittenPowerStatsDescriptors = new ArraySet<>();
private byte mLastHistoryStepLevel = 0;
private boolean mMutable = true;
@@ -307,23 +307,26 @@
* @param maxHistoryBufferSize the most amount of RAM to used for buffering of history steps
*/
public BatteryStatsHistory(File systemDir, int maxHistoryFiles, int maxHistoryBufferSize,
- HistoryStepDetailsCalculator stepDetailsCalculator, Clock clock) {
+ HistoryStepDetailsCalculator stepDetailsCalculator, Clock clock,
+ MonotonicClock monotonicClock) {
this(Parcel.obtain(), systemDir, maxHistoryFiles, maxHistoryBufferSize,
- stepDetailsCalculator, clock, new TraceDelegate());
+ stepDetailsCalculator, clock, monotonicClock, new TraceDelegate());
initHistoryBuffer();
}
@VisibleForTesting
public BatteryStatsHistory(Parcel historyBuffer, File systemDir,
int maxHistoryFiles, int maxHistoryBufferSize,
- HistoryStepDetailsCalculator stepDetailsCalculator, Clock clock, TraceDelegate tracer) {
+ HistoryStepDetailsCalculator stepDetailsCalculator, Clock clock,
+ MonotonicClock monotonicClock, TraceDelegate tracer) {
this(historyBuffer, systemDir, maxHistoryFiles, maxHistoryBufferSize, stepDetailsCalculator,
- clock, tracer, null);
+ clock, monotonicClock, tracer, null);
}
private BatteryStatsHistory(Parcel historyBuffer, File systemDir,
int maxHistoryFiles, int maxHistoryBufferSize,
- HistoryStepDetailsCalculator stepDetailsCalculator, Clock clock, TraceDelegate tracer,
+ HistoryStepDetailsCalculator stepDetailsCalculator, Clock clock,
+ MonotonicClock monotonicClock, TraceDelegate tracer,
BatteryStatsHistory writableHistory) {
mHistoryBuffer = historyBuffer;
mSystemDir = systemDir;
@@ -332,6 +335,7 @@
mStepDetailsCalculator = stepDetailsCalculator;
mTracer = tracer;
mClock = clock;
+ mMonotonicClock = monotonicClock;
mWritableHistory = writableHistory;
if (mWritableHistory != null) {
mMutable = false;
@@ -381,16 +385,18 @@
}
private BatteryHistoryFile makeBatteryHistoryFile() {
- return new BatteryHistoryFile(mHistoryDir, mClock.elapsedRealtime() + mHistoryBaseTimeMs);
+ return new BatteryHistoryFile(mHistoryDir, mMonotonicClock.monotonicTime());
}
public BatteryStatsHistory(int maxHistoryFiles, int maxHistoryBufferSize,
- HistoryStepDetailsCalculator stepDetailsCalculator, Clock clock) {
+ HistoryStepDetailsCalculator stepDetailsCalculator, Clock clock,
+ MonotonicClock monotonicClock) {
mMaxHistoryFiles = maxHistoryFiles;
mMaxHistoryBufferSize = maxHistoryBufferSize;
mStepDetailsCalculator = stepDetailsCalculator;
mTracer = new TraceDelegate();
mClock = clock;
+ mMonotonicClock = monotonicClock;
mHistoryBuffer = Parcel.obtain();
mSystemDir = null;
@@ -417,16 +423,16 @@
mHistoryBuffer = Parcel.obtain();
mHistoryBuffer.unmarshall(historyBlob, 0, historyBlob.length);
+ mMonotonicClock = null;
readFromParcel(parcel, true /* useBlobs */);
}
private void initHistoryBuffer() {
- mHistoryBaseTimeMs = 0;
- mLastHistoryElapsedRealtimeMs = 0;
mTrackRunningHistoryElapsedRealtimeMs = 0;
mTrackRunningHistoryUptimeMs = 0;
mWrittenPowerStatsDescriptors.clear();
+ mHistoryBufferStartTime = mMonotonicClock.monotonicTime();
mHistoryBuffer.setDataSize(0);
mHistoryBuffer.setDataPosition(0);
mHistoryBuffer.setDataCapacity(mMaxHistoryBufferSize / 2);
@@ -466,7 +472,7 @@
historyBufferCopy.appendFrom(mHistoryBuffer, 0, mHistoryBuffer.dataSize());
return new BatteryStatsHistory(historyBufferCopy, mSystemDir, 0, 0, null, null, null,
- this);
+ null, this);
}
}
@@ -491,7 +497,7 @@
* When {@link #mHistoryBuffer} reaches {@link BatteryStatsImpl.Constants#MAX_HISTORY_BUFFER},
* create next history file.
*/
- public void startNextFile() {
+ public void startNextFile(long elapsedRealtimeMs) {
if (mMaxHistoryFiles == 0) {
Slog.wtf(TAG, "mMaxHistoryFiles should not be zero when writing history");
return;
@@ -517,6 +523,7 @@
Slog.e(TAG, "Could not create history file: " + mActiveFile.getBaseFile());
}
+ mHistoryBufferStartTime = mMonotonicClock.monotonicTime(elapsedRealtimeMs);
mHistoryBuffer.setDataSize(0);
mHistoryBuffer.setDataPosition(0);
mHistoryBuffer.setDataCapacity(mMaxHistoryBufferSize / 2);
@@ -699,9 +706,12 @@
if (mHistoryParcels != null) {
while (mParcelIndex < mHistoryParcels.size()) {
final Parcel p = mHistoryParcels.get(mParcelIndex++);
- if (!skipHead(p)) {
+ if (!verifyVersion(p)) {
continue;
}
+ // skip monotonic time field.
+ p.readLong();
+
final int bufSize = p.readInt();
final int curPos = p.dataPosition();
mCurrentParcelEnd = curPos + bufSize;
@@ -745,24 +755,36 @@
}
out.unmarshall(raw, 0, raw.length);
out.setDataPosition(0);
- return skipHead(out);
+ if (!verifyVersion(out)) {
+ return false;
+ }
+ // skip monotonic time field.
+ out.readLong();
+ return true;
}
/**
- * Skip the header part of history parcel.
+ * Verify header part of history parcel.
*
- * @param p history parcel to skip head.
* @return true if version match, false if not.
*/
- private boolean skipHead(Parcel p) {
+ private boolean verifyVersion(Parcel p) {
p.setDataPosition(0);
final int version = p.readInt();
- if (version != VERSION) {
- return false;
- }
- // skip historyBaseTime field.
- p.readLong();
- return true;
+ return version == VERSION;
+ }
+
+ /**
+ * Extracts the monotonic time, as per {@link MonotonicClock}, from the supplied battery history
+ * buffer.
+ */
+ public long getHistoryBufferStartTime(Parcel p) {
+ int pos = p.dataPosition();
+ p.setDataPosition(0);
+ p.readInt(); // Skip the version field
+ long monotonicTime = p.readLong();
+ p.setDataPosition(pos);
+ return monotonicTime;
}
/**
@@ -1438,7 +1460,8 @@
throw new ConcurrentModificationException("Battery history is not writable");
}
- final long timeDiffMs = (mHistoryBaseTimeMs + elapsedRealtimeMs) - mHistoryLastWritten.time;
+ final long timeDiffMs = mMonotonicClock.monotonicTime(elapsedRealtimeMs)
+ - mHistoryLastWritten.time;
final int diffStates = mHistoryLastWritten.states ^ cur.states;
final int diffStates2 = mHistoryLastWritten.states2 ^ cur.states2;
final int lastDiffStates = mHistoryLastWritten.states ^ mHistoryLastLastWritten.states;
@@ -1476,7 +1499,9 @@
mHistoryBuffer.setDataSize(mHistoryBufferLastPos);
mHistoryBuffer.setDataPosition(mHistoryBufferLastPos);
mHistoryBufferLastPos = -1;
- elapsedRealtimeMs = mHistoryLastWritten.time - mHistoryBaseTimeMs;
+
+ elapsedRealtimeMs -= timeDiffMs;
+
// If the last written history had a wakelock tag, we need to retain it.
// Note that the condition above made sure that we aren't in a case where
// both it and the current history item have a wakelock tag.
@@ -1513,7 +1538,7 @@
HistoryItem copy = new HistoryItem();
copy.setTo(cur);
- startNextFile();
+ startNextFile(elapsedRealtimeMs);
// startRecordingHistory will reset mHistoryCur.
startRecordingHistory(elapsedRealtimeMs, uptimeMs, false);
@@ -1548,7 +1573,7 @@
mHistoryBufferLastPos = mHistoryBuffer.dataPosition();
mHistoryLastLastWritten.setTo(mHistoryLastWritten);
final boolean hasTags = mHistoryLastWritten.tagsFirstOccurrence || cur.tagsFirstOccurrence;
- mHistoryLastWritten.setTo(mHistoryBaseTimeMs + elapsedRealtimeMs, cmd, cur);
+ mHistoryLastWritten.setTo(mMonotonicClock.monotonicTime(elapsedRealtimeMs), cmd, cur);
if (mHistoryLastWritten.time < mHistoryLastLastWritten.time - 60000) {
Slog.wtf(TAG, "Significantly earlier event written to battery history:"
+ " time=" + mHistoryLastWritten.time
@@ -1556,7 +1581,6 @@
}
mHistoryLastWritten.tagsFirstOccurrence = hasTags;
writeHistoryDelta(mHistoryBuffer, mHistoryLastWritten, mHistoryLastLastWritten);
- mLastHistoryElapsedRealtimeMs = elapsedRealtimeMs;
cur.wakelockTag = null;
cur.wakeReasonTag = null;
cur.eventCode = HistoryItem.EVENT_NONE;
@@ -1926,6 +1950,10 @@
return;
}
+ // Save the monotonic time first, so that even if the history write below fails,
+ // we still wouldn't end up with overlapping history timelines.
+ mMonotonicClock.write();
+
Parcel p = Parcel.obtain();
try {
final long start = SystemClock.uptimeMillis();
@@ -1951,8 +1979,7 @@
return;
}
- final long historyBaseTime = in.readLong();
-
+ mHistoryBufferStartTime = in.readLong();
mHistoryBuffer.setDataSize(0);
mHistoryBuffer.setDataPosition(0);
@@ -1972,39 +1999,11 @@
mHistoryBuffer.appendFrom(in, curPos, bufSize);
in.setDataPosition(curPos + bufSize);
}
-
- mHistoryBaseTimeMs = historyBaseTime;
- if (DEBUG) {
- StringBuilder sb = new StringBuilder(128);
- sb.append("****************** NEW mHistoryBaseTimeMs: ");
- TimeUtils.formatDuration(mHistoryBaseTimeMs, sb);
- Slog.i(TAG, sb.toString());
- }
-
- if (mHistoryBaseTimeMs > 0) {
- long elapsedRealtimeMs = mClock.elapsedRealtime();
- mLastHistoryElapsedRealtimeMs = elapsedRealtimeMs;
- mHistoryBaseTimeMs = mHistoryBaseTimeMs - elapsedRealtimeMs + 1;
- if (DEBUG) {
- StringBuilder sb = new StringBuilder(128);
- sb.append("****************** ADJUSTED mHistoryBaseTimeMs: ");
- TimeUtils.formatDuration(mHistoryBaseTimeMs, sb);
- Slog.i(TAG, sb.toString());
- }
- }
}
private void writeHistoryBuffer(Parcel out) {
- if (DEBUG) {
- StringBuilder sb = new StringBuilder(128);
- sb.append("****************** WRITING mHistoryBaseTimeMs: ");
- TimeUtils.formatDuration(mHistoryBaseTimeMs, sb);
- sb.append(" mLastHistoryElapsedRealtimeMs: ");
- TimeUtils.formatDuration(mLastHistoryElapsedRealtimeMs, sb);
- Slog.i(TAG, sb.toString());
- }
out.writeInt(BatteryStatsHistory.VERSION);
- out.writeLong(mHistoryBaseTimeMs + mLastHistoryElapsedRealtimeMs);
+ out.writeLong(mHistoryBufferStartTime);
out.writeInt(mHistoryBuffer.dataSize());
if (DEBUG) {
Slog.i(TAG, "***************** WRITING HISTORY: "
diff --git a/core/java/com/android/internal/os/BatteryStatsHistoryIterator.java b/core/java/com/android/internal/os/BatteryStatsHistoryIterator.java
index a5d2d0f..6bd5898 100644
--- a/core/java/com/android/internal/os/BatteryStatsHistoryIterator.java
+++ b/core/java/com/android/internal/os/BatteryStatsHistoryIterator.java
@@ -16,7 +16,6 @@
package com.android.internal.os;
-import android.annotation.CurrentTimeMillisLong;
import android.annotation.NonNull;
import android.os.BatteryManager;
import android.os.BatteryStats;
@@ -34,8 +33,8 @@
private static final boolean DEBUG = false;
private static final String TAG = "BatteryStatsHistoryItr";
private final BatteryStatsHistory mBatteryStatsHistory;
- private final @CurrentTimeMillisLong long mStartTimeMs;
- private final @CurrentTimeMillisLong long mEndTimeMs;
+ private final long mStartTimeMs;
+ private final long mEndTimeMs;
private final BatteryStats.HistoryStepDetails mReadHistoryStepDetails =
new BatteryStats.HistoryStepDetails();
private final SparseArray<BatteryStats.HistoryTag> mHistoryTags = new SparseArray<>();
@@ -43,10 +42,10 @@
new PowerStats.DescriptorRegistry();
private BatteryStats.HistoryItem mHistoryItem = new BatteryStats.HistoryItem();
private boolean mNextItemReady;
+ private boolean mTimeInitialized;
- public BatteryStatsHistoryIterator(@NonNull BatteryStatsHistory history,
- @CurrentTimeMillisLong long startTimeMs,
- @CurrentTimeMillisLong long endTimeMs) {
+ public BatteryStatsHistoryIterator(@NonNull BatteryStatsHistory history, long startTimeMs,
+ long endTimeMs) {
mBatteryStatsHistory = history;
mStartTimeMs = startTimeMs;
mEndTimeMs = (endTimeMs != 0) ? endTimeMs : Long.MAX_VALUE;
@@ -82,7 +81,12 @@
break;
}
- final long lastRealtimeMs = mHistoryItem.time;
+ if (!mTimeInitialized) {
+ mHistoryItem.time = mBatteryStatsHistory.getHistoryBufferStartTime(p);
+ mTimeInitialized = true;
+ }
+
+ final long lastMonotonicTimeMs = mHistoryItem.time;
final long lastWalltimeMs = mHistoryItem.currentTime;
try {
readHistoryDelta(p, mHistoryItem);
@@ -93,12 +97,13 @@
if (mHistoryItem.cmd != BatteryStats.HistoryItem.CMD_CURRENT_TIME
&& mHistoryItem.cmd != BatteryStats.HistoryItem.CMD_RESET
&& lastWalltimeMs != 0) {
- mHistoryItem.currentTime = lastWalltimeMs + (mHistoryItem.time - lastRealtimeMs);
+ mHistoryItem.currentTime =
+ lastWalltimeMs + (mHistoryItem.time - lastMonotonicTimeMs);
}
- if (mEndTimeMs != 0 && mHistoryItem.currentTime >= mEndTimeMs) {
+ if (mEndTimeMs != 0 && mHistoryItem.time >= mEndTimeMs) {
break;
}
- if (mHistoryItem.currentTime >= mStartTimeMs) {
+ if (mHistoryItem.time >= mStartTimeMs) {
mNextItemReady = true;
return;
}
diff --git a/core/java/com/android/internal/os/LongArrayMultiStateCounter.java b/core/java/com/android/internal/os/LongArrayMultiStateCounter.java
index 5ea6ba8..1f44b33 100644
--- a/core/java/com/android/internal/os/LongArrayMultiStateCounter.java
+++ b/core/java/com/android/internal/os/LongArrayMultiStateCounter.java
@@ -191,6 +191,27 @@
}
/**
+ * Sets the new values for the given state.
+ */
+ public void setValues(int state, long[] values) {
+ if (state < 0 || state >= mStateCount) {
+ throw new IllegalArgumentException(
+ "State: " + state + ", outside the range: [0-" + (mStateCount - 1) + "]");
+ }
+ if (values.length != mLength) {
+ throw new IllegalArgumentException(
+ "Invalid array length: " + values.length + ", expected: " + mLength);
+ }
+ LongArrayContainer container = sTmpArrayContainer.getAndSet(null);
+ if (container == null || container.mLength != values.length) {
+ container = new LongArrayContainer(values.length);
+ }
+ container.setValues(values);
+ native_setValues(mNativeObject, state, container.mNativeObject);
+ sTmpArrayContainer.set(container);
+ }
+
+ /**
* Sets the new values. The delta between the previously set values and these values
* is distributed among the state according to the time the object spent in those states
* since the previous call to updateValues.
@@ -317,6 +338,10 @@
private static native void native_setState(long nativeObject, int state, long timestampMs);
@CriticalNative
+ private static native void native_setValues(long nativeObject, int state,
+ long longArrayContainerNativeObject);
+
+ @CriticalNative
private static native void native_updateValues(long nativeObject,
long longArrayContainerNativeObject, long timestampMs);
diff --git a/core/java/com/android/internal/os/MonotonicClock.java b/core/java/com/android/internal/os/MonotonicClock.java
new file mode 100644
index 0000000..6f114e3
--- /dev/null
+++ b/core/java/com/android/internal/os/MonotonicClock.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.os;
+
+import android.annotation.NonNull;
+import android.util.AtomicFile;
+import android.util.Log;
+import android.util.Xml;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.modules.utils.TypedXmlPullParser;
+import com.android.modules.utils.TypedXmlSerializer;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.charset.StandardCharsets;
+
+/**
+ * A clock that is similar to SystemClock#elapsedRealtime(), except that it is not reset
+ * on reboot, but keeps going.
+ */
+public class MonotonicClock {
+ private static final String TAG = "MonotonicClock";
+
+ private static final String XML_TAG_MONOTONIC_TIME = "monotonic_time";
+ private static final String XML_ATTR_TIMESHIFT = "timeshift";
+
+ private final AtomicFile mFile;
+ private final Clock mClock;
+ private long mTimeshift;
+
+ public MonotonicClock(File file) {
+ mFile = new AtomicFile(file);
+ mClock = Clock.SYSTEM_CLOCK;
+ read();
+ }
+
+ public MonotonicClock(long monotonicTime, @NonNull Clock clock) {
+ mClock = clock;
+ mFile = null;
+ mTimeshift = monotonicTime - mClock.elapsedRealtime();
+ }
+
+ /**
+ * Returns time in milliseconds, based on SystemClock.elapsedTime, adjusted so that
+ * after a device reboot the time keeps increasing.
+ */
+ public long monotonicTime() {
+ return monotonicTime(mClock.elapsedRealtime());
+ }
+
+ /**
+ * Like {@link #monotonicTime()}, except the elapsed time is supplied as an argument instead
+ * of being read from the Clock.
+ */
+ public long monotonicTime(long elapsedRealtimeMs) {
+ return mTimeshift + elapsedRealtimeMs;
+ }
+
+ private void read() {
+ if (!mFile.exists()) {
+ return;
+ }
+
+ try {
+ readXml(new ByteArrayInputStream(mFile.readFully()), Xml.newBinaryPullParser());
+ } catch (IOException e) {
+ Log.e(TAG, "Cannot load monotonic clock from " + mFile.getBaseFile(), e);
+ }
+ }
+
+ /**
+ * Saves the timeshift into a file. Call this method just before system shutdown, after
+ * writing the last battery history event.
+ */
+ public void write() {
+ if (mFile == null) {
+ return;
+ }
+
+ mFile.write(out -> {
+ try {
+ writeXml(out, Xml.newBinarySerializer());
+ out.close();
+ } catch (IOException e) {
+ Log.e(TAG, "Cannot write monotonic clock to " + mFile.getBaseFile(), e);
+ }
+ });
+ }
+
+ /**
+ * Parses an XML file containing the persistent state of the monotonic clock.
+ */
+ @VisibleForTesting
+ public void readXml(InputStream inputStream, TypedXmlPullParser parser) throws IOException {
+ long savedTimeshift = 0;
+ try {
+ parser.setInput(inputStream, StandardCharsets.UTF_8.name());
+ int eventType = parser.getEventType();
+ while (eventType != XmlPullParser.END_DOCUMENT) {
+ if (eventType == XmlPullParser.START_TAG
+ && parser.getName().equals(XML_TAG_MONOTONIC_TIME)) {
+ savedTimeshift = parser.getAttributeLong(null, XML_ATTR_TIMESHIFT);
+ }
+ eventType = parser.next();
+ }
+ } catch (XmlPullParserException e) {
+ throw new IOException(e);
+ }
+ mTimeshift = savedTimeshift - mClock.elapsedRealtime();
+ }
+
+ /**
+ * Creates an XML file containing the persistent state of the monotonic clock.
+ */
+ @VisibleForTesting
+ public void writeXml(OutputStream out, TypedXmlSerializer serializer) throws IOException {
+ serializer.setOutput(out, StandardCharsets.UTF_8.name());
+ serializer.startDocument(null, true);
+ serializer.startTag(null, XML_TAG_MONOTONIC_TIME);
+ serializer.attributeLong(null, XML_ATTR_TIMESHIFT, monotonicTime());
+ serializer.endTag(null, XML_TAG_MONOTONIC_TIME);
+ serializer.endDocument();
+ }
+}
diff --git a/core/java/com/android/internal/os/MultiStateStats.java b/core/java/com/android/internal/os/MultiStateStats.java
index f971849..dc5055a 100644
--- a/core/java/com/android/internal/os/MultiStateStats.java
+++ b/core/java/com/android/internal/os/MultiStateStats.java
@@ -16,9 +16,17 @@
package com.android.internal.os;
+import android.util.Slog;
+
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Preconditions;
+import com.android.modules.utils.TypedXmlPullParser;
+import com.android.modules.utils.TypedXmlSerializer;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
import java.io.PrintWriter;
import java.util.Arrays;
@@ -29,15 +37,21 @@
* values;
*/
public class MultiStateStats {
+ private static final String TAG = "MultiStateStats";
+
+ private static final String XML_TAG_STATS = "stats";
+
/**
* A set of states, e.g. on-battery, screen-on, procstate. The state values are integers
* from 0 to States.mLabels.length
*/
public static class States {
+ final String mName;
final boolean mTracked;
final String[] mLabels;
- public States(boolean tracked, String... labels) {
+ public States(String name, boolean tracked, String... labels) {
+ mName = name;
this.mTracked = tracked;
this.mLabels = labels;
}
@@ -155,11 +169,28 @@
>>> mStateBitFieldShifts[stateIndex];
}
- private int setStateInComposite(int baseCompositeState, int stateIndex, int value) {
+ int setStateInComposite(int baseCompositeState, int stateIndex, int value) {
return (baseCompositeState & ~mStateBitFieldMasks[stateIndex])
| (value << mStateBitFieldShifts[stateIndex]);
}
+ int setStateInComposite(int compositeState, String stateName, String stateLabel) {
+ for (int stateIndex = 0; stateIndex < mStates.length; stateIndex++) {
+ States stateConfig = mStates[stateIndex];
+ if (stateConfig.mName.equals(stateName)) {
+ for (int state = 0; state < stateConfig.mLabels.length; state++) {
+ if (stateConfig.mLabels[state].equals(stateLabel)) {
+ return setStateInComposite(compositeState, stateIndex, state);
+ }
+ }
+ Slog.e(TAG, "Unexpected label '" + stateLabel + "' for state: " + stateName);
+ return -1;
+ }
+ }
+ Slog.e(TAG, "Unsupported state: " + stateName);
+ return -1;
+ }
+
/**
* Allocates a new stats container using this Factory's configuration.
*/
@@ -195,6 +226,10 @@
}
return serialState;
}
+
+ int getSerialState(int compositeState) {
+ return mCompositeToSerialState[compositeState];
+ }
}
private final Factory mFactory;
@@ -254,6 +289,106 @@
}
/**
+ * Stores contents in an XML doc.
+ */
+ public void writeXml(TypedXmlSerializer serializer) throws IOException {
+ long[] tmpArray = new long[mCounter.getArrayLength()];
+ writeXmlAllStates(serializer, new int[mFactory.mStates.length], 0, tmpArray);
+ }
+
+ private void writeXmlAllStates(TypedXmlSerializer serializer, int[] states, int stateIndex,
+ long[] values) throws IOException {
+ if (stateIndex < states.length) {
+ if (!mFactory.mStates[stateIndex].mTracked) {
+ writeXmlAllStates(serializer, states, stateIndex + 1, values);
+ return;
+ }
+
+ for (int i = 0; i < mFactory.mStates[stateIndex].mLabels.length; i++) {
+ states[stateIndex] = i;
+ writeXmlAllStates(serializer, states, stateIndex + 1, values);
+ }
+ return;
+ }
+
+ mCounter.getCounts(values, mFactory.getSerialState(states));
+ boolean nonZero = false;
+ for (long value : values) {
+ if (value != 0) {
+ nonZero = true;
+ break;
+ }
+ }
+ if (!nonZero) {
+ return;
+ }
+
+ serializer.startTag(null, XML_TAG_STATS);
+
+ for (int i = 0; i < states.length; i++) {
+ if (mFactory.mStates[i].mTracked && states[i] != 0) {
+ serializer.attribute(null, mFactory.mStates[i].mName,
+ mFactory.mStates[i].mLabels[states[i]]);
+ }
+ }
+ for (int i = 0; i < values.length; i++) {
+ if (values[i] != 0) {
+ serializer.attributeLong(null, "_" + i, values[i]);
+ }
+ }
+ serializer.endTag(null, XML_TAG_STATS);
+ }
+
+ /**
+ * Populates the object with contents in an XML doc. The parser is expected to be
+ * positioned on the opening tag of the corresponding element.
+ */
+ public boolean readFromXml(TypedXmlPullParser parser) throws XmlPullParserException,
+ IOException {
+ String outerTag = parser.getName();
+ long[] tmpArray = new long[mCounter.getArrayLength()];
+ int eventType = parser.getEventType();
+ while (eventType != XmlPullParser.END_DOCUMENT
+ && !(eventType == XmlPullParser.END_TAG && parser.getName().equals(outerTag))) {
+ if (eventType == XmlPullParser.START_TAG) {
+ if (parser.getName().equals(XML_TAG_STATS)) {
+ Arrays.fill(tmpArray, 0);
+ int compositeState = 0;
+ int attributeCount = parser.getAttributeCount();
+ for (int i = 0; i < attributeCount; i++) {
+ String attributeName = parser.getAttributeName(i);
+ if (attributeName.startsWith("_")) {
+ int index;
+ try {
+ index = Integer.parseInt(attributeName.substring(1));
+ } catch (NumberFormatException e) {
+ throw new XmlPullParserException(
+ "Unexpected index syntax: " + attributeName, parser, e);
+ }
+ if (index < 0 || index >= tmpArray.length) {
+ Slog.e(TAG, "State index out of bounds: " + index
+ + " length: " + tmpArray.length);
+ return false;
+ }
+ tmpArray[index] = parser.getAttributeLong(i);
+ } else {
+ String attributeValue = parser.getAttributeValue(i);
+ compositeState = mFactory.setStateInComposite(compositeState,
+ attributeName, attributeValue);
+ if (compositeState == -1) {
+ return false;
+ }
+ }
+ }
+ mCounter.setValues(mFactory.getSerialState(compositeState), tmpArray);
+ }
+ }
+ eventType = parser.next();
+ }
+ return true;
+ }
+
+ /**
* Prints the accumulated stats, one line of every combination of states that has data.
*/
public void dump(PrintWriter pw) {
diff --git a/core/java/com/android/internal/os/OWNERS b/core/java/com/android/internal/os/OWNERS
index e35b7f1..996e424 100644
--- a/core/java/com/android/internal/os/OWNERS
+++ b/core/java/com/android/internal/os/OWNERS
@@ -5,14 +5,10 @@
per-file *BinaryTransparency* = file:/core/java/android/transparency/OWNERS
# BatteryStats
-per-file BatterySipper.java = file:/BATTERY_STATS_OWNERS
per-file BatteryStats* = file:/BATTERY_STATS_OWNERS
-per-file BatteryUsageStats* = file:/BATTERY_STATS_OWNERS
-per-file *ChargeCalculator* = file:/BATTERY_STATS_OWNERS
-per-file *PowerCalculator* = file:/BATTERY_STATS_OWNERS
-per-file *PowerEstimator* = file:/BATTERY_STATS_OWNERS
-per-file *PowerStats* = file:/BATTERY_STATS_OWNERS
per-file *Kernel* = file:/BATTERY_STATS_OWNERS
+per-file *Clock* = file:/BATTERY_STATS_OWNERS
per-file *MultiState* = file:/BATTERY_STATS_OWNERS
per-file *PowerProfile* = file:/BATTERY_STATS_OWNERS
+per-file *PowerStats* = file:/BATTERY_STATS_OWNERS
diff --git a/core/java/com/android/internal/os/PowerStats.java b/core/java/com/android/internal/os/PowerStats.java
index 8f66d1f..1130a45 100644
--- a/core/java/com/android/internal/os/PowerStats.java
+++ b/core/java/com/android/internal/os/PowerStats.java
@@ -24,9 +24,16 @@
import android.os.PersistableBundle;
import android.os.UserHandle;
import android.util.IndentingPrintWriter;
-import android.util.Log;
+import android.util.Slog;
import android.util.SparseArray;
+import com.android.modules.utils.TypedXmlPullParser;
+import com.android.modules.utils.TypedXmlSerializer;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
import java.util.Arrays;
import java.util.Objects;
@@ -61,6 +68,13 @@
* to adjust the algorithm in accordance with the stats available on the device.
*/
public static class Descriptor {
+ public static final String XML_TAG_DESCRIPTOR = "descriptor";
+ private static final String XML_ATTR_ID = "id";
+ private static final String XML_ATTR_NAME = "name";
+ private static final String XML_ATTR_STATS_ARRAY_LENGTH = "stats-array-length";
+ private static final String XML_ATTR_UID_STATS_ARRAY_LENGTH = "uid-stats-array-length";
+ private static final String XML_TAG_EXTRAS = "extras";
+
/**
* {@link BatteryConsumer.PowerComponent} (e.g. CPU, WIFI etc) that this snapshot relates
* to; or a custom power component ID (if the value
@@ -126,7 +140,7 @@
int firstWord = parcel.readInt();
int version = (firstWord & PARCEL_FORMAT_VERSION_MASK) >>> PARCEL_FORMAT_VERSION_SHIFT;
if (version != PARCEL_FORMAT_VERSION) {
- Log.w(TAG, "Cannot read PowerStats from Parcel - the parcel format version has "
+ Slog.w(TAG, "Cannot read PowerStats from Parcel - the parcel format version has "
+ "changed from " + version + " to " + PARCEL_FORMAT_VERSION);
return null;
}
@@ -155,6 +169,71 @@
that.extras); // Since the Parcel is now unparceled, do a deep comparison
}
+ /**
+ * Stores contents in an XML doc.
+ */
+ public void writeXml(TypedXmlSerializer serializer) throws IOException {
+ serializer.startTag(null, XML_TAG_DESCRIPTOR);
+ serializer.attributeInt(null, XML_ATTR_ID, powerComponentId);
+ serializer.attribute(null, XML_ATTR_NAME, name);
+ serializer.attributeInt(null, XML_ATTR_STATS_ARRAY_LENGTH, statsArrayLength);
+ serializer.attributeInt(null, XML_ATTR_UID_STATS_ARRAY_LENGTH, uidStatsArrayLength);
+ try {
+ serializer.startTag(null, XML_TAG_EXTRAS);
+ extras.saveToXml(serializer);
+ serializer.endTag(null, XML_TAG_EXTRAS);
+ } catch (XmlPullParserException e) {
+ throw new IOException(e);
+ }
+ serializer.endTag(null, XML_TAG_DESCRIPTOR);
+ }
+
+ /**
+ * Creates a Descriptor by parsing an XML doc. The parser is expected to be positioned
+ * on or before the opening "descriptor" tag.
+ */
+ public static Descriptor createFromXml(TypedXmlPullParser parser)
+ throws XmlPullParserException, IOException {
+ int powerComponentId = -1;
+ String name = null;
+ int statsArrayLength = 0;
+ int uidStatsArrayLength = 0;
+ PersistableBundle extras = null;
+ int eventType = parser.getEventType();
+ while (eventType != XmlPullParser.END_DOCUMENT
+ && !(eventType == XmlPullParser.END_TAG
+ && parser.getName().equals(XML_TAG_DESCRIPTOR))) {
+ if (eventType == XmlPullParser.START_TAG) {
+ switch (parser.getName()) {
+ case XML_TAG_DESCRIPTOR:
+ powerComponentId = parser.getAttributeInt(null, XML_ATTR_ID);
+ name = parser.getAttributeValue(null, XML_ATTR_NAME);
+ statsArrayLength = parser.getAttributeInt(null,
+ XML_ATTR_STATS_ARRAY_LENGTH);
+ uidStatsArrayLength = parser.getAttributeInt(null,
+ XML_ATTR_UID_STATS_ARRAY_LENGTH);
+ break;
+ case XML_TAG_EXTRAS:
+ extras = PersistableBundle.restoreFromXml(parser);
+ break;
+ }
+ }
+ eventType = parser.next();
+ }
+ if (powerComponentId == -1) {
+ return null;
+ } else if (powerComponentId >= BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID) {
+ return new Descriptor(powerComponentId, name, statsArrayLength, uidStatsArrayLength,
+ extras);
+ } else if (powerComponentId < BatteryConsumer.POWER_COMPONENT_COUNT) {
+ return new Descriptor(powerComponentId, statsArrayLength, uidStatsArrayLength,
+ extras);
+ } else {
+ Slog.e(TAG, "Unrecognized power component: " + powerComponentId);
+ return null;
+ }
+ }
+
@Override
public int hashCode() {
return Objects.hash(powerComponentId);
@@ -259,7 +338,7 @@
Descriptor descriptor = registry.get(powerComponentId);
if (descriptor == null) {
- Log.e(TAG, "Unsupported PowerStats for power component ID: " + powerComponentId);
+ Slog.e(TAG, "Unsupported PowerStats for power component ID: " + powerComponentId);
return null;
}
PowerStats stats = new PowerStats(descriptor);
@@ -274,7 +353,7 @@
stats.uidStats.put(uid, uidStats);
}
if (parcel.dataPosition() != endPos) {
- Log.e(TAG, "Corrupted PowerStats parcel. Expected length: " + length
+ Slog.e(TAG, "Corrupted PowerStats parcel. Expected length: " + length
+ ", actual length: " + (parcel.dataPosition() - startPos));
return null;
}
diff --git a/core/java/com/android/internal/protolog/ProtoLogGroup.java b/core/java/com/android/internal/protolog/ProtoLogGroup.java
index ec525f0..4bb7c33 100644
--- a/core/java/com/android/internal/protolog/ProtoLogGroup.java
+++ b/core/java/com/android/internal/protolog/ProtoLogGroup.java
@@ -91,6 +91,8 @@
WM_DEBUG_BACK_PREVIEW(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, true,
"CoreBackPreview"),
WM_DEBUG_DREAM(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, true, Consts.TAG_WM),
+
+ WM_DEBUG_DIMMER(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false, Consts.TAG_WM),
TEST_GROUP(true, true, false, "WindowManagerProtoLogTest");
private final boolean mEnabled;
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 f79dbe7..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;
@@ -962,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,
@@ -1231,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);
@@ -2179,7 +2182,7 @@
(void*)nativeSetFrameRate },
{"nativeSetDefaultFrameRateCompatibility", "(JJI)V",
(void*)nativeSetDefaultFrameRateCompatibility},
- {"nativeSetFrameRateCategory", "(JJI)V",
+ {"nativeSetFrameRateCategory", "(JJIZ)V",
(void*)nativeSetFrameRateCategory},
{"nativeSetFrameRateSelectionStrategy", "(JJI)V",
(void*)nativeSetFrameRateSelectionStrategy},
@@ -2394,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/jni/com_android_internal_os_LongArrayMultiStateCounter.cpp b/core/jni/com_android_internal_os_LongArrayMultiStateCounter.cpp
index 6920211..1f29735 100644
--- a/core/jni/com_android_internal_os_LongArrayMultiStateCounter.cpp
+++ b/core/jni/com_android_internal_os_LongArrayMultiStateCounter.cpp
@@ -55,6 +55,15 @@
counter->setState(state, timestamp);
}
+static void native_setValues(jlong nativePtr, jint state, jlong longArrayContainerNativePtr) {
+ battery::LongArrayMultiStateCounter *counter =
+ reinterpret_cast<battery::LongArrayMultiStateCounter *>(nativePtr);
+ std::vector<uint64_t> *vector =
+ reinterpret_cast<std::vector<uint64_t> *>(longArrayContainerNativePtr);
+
+ counter->setValue(state, *vector);
+}
+
static void native_updateValues(jlong nativePtr, jlong longArrayContainerNativePtr,
jlong timestamp) {
battery::LongArrayMultiStateCounter *counter =
@@ -210,6 +219,8 @@
// @CriticalNative
{"native_setState", "(JIJ)V", (void *)native_setState},
// @CriticalNative
+ {"native_setValues", "(JIJ)V", (void *)native_setValues},
+ // @CriticalNative
{"native_updateValues", "(JJJ)V", (void *)native_updateValues},
// @CriticalNative
{"native_incrementValues", "(JJJ)V", (void *)native_incrementValues},
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 298eb2f..88b578b 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" />
@@ -6121,7 +6122,9 @@
android:protectionLevel="signature|privileged|development|appop|retailDemo" />
<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />
- <!-- @SystemApi @hide Allows trusted system components to report events to UsageStatsManager -->
+ <!-- @SystemApi @hide
+ @FlaggedApi("backstage_power.report_usage_stats_permission")
+ Allows trusted system components to report events to UsageStatsManager -->
<permission android:name="android.permission.REPORT_USAGE_STATS"
android:protectionLevel="signature|module" />
@@ -7232,13 +7235,25 @@
android:description="@string/permdesc_fullScreenIntent"
android:protectionLevel="normal|appop" />
- <!-- Required for the assistant apps targeting {@link android.os.Build.VERSION_CODES#V}
- that receive voice trigger from the trusted hotword detection service.
+ <!-- @SystemApi Required for the privileged assistant apps targeting
+ {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM}
+ that receive voice trigger from a sandboxed {@link HotwordDetectionService}.
<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 a sandboxed {@link HotwordDetectionService} or
+ {@link VisualQueryDetectionService}.
+ <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/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-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 7ef81ab..b211ac2 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1026,6 +1026,12 @@
<!-- Duration, in milliseconds, of the display white balance animated transitions. -->
<integer name="config_displayWhiteBalanceTransitionTime">3000</integer>
+ <!-- Duration, in milliseconds, of the display white balance animated transitions when increasing cct. -->
+ <integer name="config_displayWhiteBalanceTransitionTimeIncrease">1000</integer>
+
+ <!-- Duration, in milliseconds, of the display white balance animated transitions when decreasing cct. -->
+ <integer name="config_displayWhiteBalanceTransitionTimeDecrease">40000</integer>
+
<!-- Device states where the sensor based rotation values should be reversed around the Z axis
for the default display.
TODO(b/265312193): Remove this workaround when this bug is fixed.-->
diff --git a/core/res/res/values/config_battery_stats.xml b/core/res/res/values/config_battery_stats.xml
index 8fb48bc..e42962c 100644
--- a/core/res/res/values/config_battery_stats.xml
+++ b/core/res/res/values/config_battery_stats.xml
@@ -31,4 +31,13 @@
is a relatively expensive operation, this throttle period may need to be adjusted for low-power
devices-->
<integer name="config_defaultPowerStatsThrottlePeriodCpu">60000</integer>
+
+ <!-- PowerStats aggregation period in milliseconds. This is the interval at which the power
+ stats aggregation procedure is performed and the results stored in PowerStatsStore. -->
+ <integer name="config_powerStatsAggregationPeriod">14400000</integer>
+
+ <!-- PowerStats aggregation span duration in milliseconds. This is the length of battery
+ history time for every aggregated power stats span that is stored stored in PowerStatsStore.
+ It should not be larger than config_powerStatsAggregationPeriod (but it can be the same) -->
+ <integer name="config_aggregatedPowerStatsSpanDuration">3600000</integer>
</resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index fac6aac..a2a4e34f 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,16 @@
<!-- 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>
+
+ <!-- Title of cable don't support displays notifications. [CHAR LIMIT=NONE] -->
+ <string name="connected_display_cable_dont_support_displays_notification_title">Cable may not support displays</string>
+ <!-- Content of cable don't support displays notification. [CHAR LIMIT=NONE] -->
+ <string name="connected_display_cable_dont_support_displays_notification_content">Your USB-C cable may not connect to displays properly</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 643f4b1..76744ea 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1997,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" />
@@ -3486,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.
@@ -3811,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" />
@@ -5064,6 +5068,10 @@
<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="connected_display_cable_dont_support_displays_notification_title"/>
+ <java-symbol type="string" name="connected_display_cable_dont_support_displays_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"/>
@@ -5116,6 +5124,8 @@
<java-symbol type="bool" name="config_batteryStatsResetOnUnplugHighBatteryLevel" />
<java-symbol type="bool" name="config_batteryStatsResetOnUnplugAfterSignificantCharge" />
<java-symbol type="integer" name="config_defaultPowerStatsThrottlePeriodCpu" />
+ <java-symbol type="integer" name="config_powerStatsAggregationPeriod" />
+ <java-symbol type="integer" name="config_aggregatedPowerStatsSpanDuration" />
<java-symbol name="materialColorOnSecondaryFixedVariant" type="attr"/>
<java-symbol name="materialColorOnTertiaryFixedVariant" type="attr"/>
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java
index 824f591..75a7231 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java
@@ -610,8 +610,6 @@
@Test
public void cancel() throws Exception {
openAidlClients(/* numClients= */ 1);
- ProgramSelector initialSel = AidlTestUtils.makeFmSelector(AM_FM_FREQUENCY_LIST[1]);
- mTunerSessions[0].tune(initialSel);
mTunerSessions[0].cancel();
@@ -621,9 +619,6 @@
@Test
public void cancel_forNonCurrentUser_doesNotCancel() throws Exception {
openAidlClients(/* numClients= */ 1);
- ProgramSelector initialSel = AidlTestUtils.makeFmSelector(AM_FM_FREQUENCY_LIST[1]);
- mTunerSessions[0].tune(initialSel);
- verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT).onCurrentProgramInfoChanged(any());
doReturn(false).when(() -> RadioServiceUserController.isCurrentOrSystemUser());
mTunerSessions[0].cancel();
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/TunerSessionHidlTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/TunerSessionHidlTest.java
index 3ec44d1..6edfa02 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/TunerSessionHidlTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/TunerSessionHidlTest.java
@@ -540,8 +540,6 @@
@Test
public void cancel() throws Exception {
openAidlClients(/* numClients= */ 1);
- ProgramSelector initialSel = TestUtils.makeFmSelector(AM_FM_FREQUENCY_LIST[1]);
- mTunerSessions[0].tune(initialSel);
mTunerSessions[0].cancel();
@@ -551,9 +549,6 @@
@Test
public void cancel_forNonCurrentUser_doesNotCancel() throws Exception {
openAidlClients(/* numClients= */ 1);
- ProgramSelector initialSel = TestUtils.makeFmSelector(AM_FM_FREQUENCY_LIST[1]);
- mTunerSessions[0].tune(initialSel);
- verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT).onCurrentProgramInfoChanged(any());
doReturn(false).when(() -> RadioServiceUserController.isCurrentOrSystemUser());
mTunerSessions[0].cancel();
diff --git a/core/tests/coretests/src/android/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/hardware/display/DisplayManagerGlobalTest.java b/core/tests/coretests/src/android/hardware/display/DisplayManagerGlobalTest.java
index 149f58f..c2e6b60c 100644
--- a/core/tests/coretests/src/android/hardware/display/DisplayManagerGlobalTest.java
+++ b/core/tests/coretests/src/android/hardware/display/DisplayManagerGlobalTest.java
@@ -42,8 +42,8 @@
public class DisplayManagerGlobalTest {
private static final long ALL_DISPLAY_EVENTS = DisplayManager.EVENT_FLAG_DISPLAY_ADDED
- | DisplayManager.EVENT_FLAG_DISPLAY_CHANGED
- | DisplayManager.EVENT_FLAG_DISPLAY_REMOVED;
+ | DisplayManager.EVENT_FLAG_DISPLAY_CHANGED
+ | DisplayManager.EVENT_FLAG_DISPLAY_REMOVED;
@Mock
private IDisplayManager mDisplayManager;
@@ -69,7 +69,8 @@
@Test
public void testDisplayListenerIsCalled_WhenDisplayEventOccurs() throws RemoteException {
- mDisplayManagerGlobal.registerDisplayListener(mListener, mHandler, ALL_DISPLAY_EVENTS);
+ mDisplayManagerGlobal.registerDisplayListener(mListener, mHandler, ALL_DISPLAY_EVENTS,
+ null);
Mockito.verify(mDisplayManager)
.registerCallbackWithEventMask(mCallbackCaptor.capture(), anyLong());
IDisplayManagerCallback callback = mCallbackCaptor.getValue();
@@ -97,26 +98,27 @@
public void testDisplayListenerIsNotCalled_WhenClientIsNotSubscribed() throws RemoteException {
// First we subscribe to all events in order to test that the subsequent calls to
// registerDisplayListener will update the event mask.
- mDisplayManagerGlobal.registerDisplayListener(mListener, mHandler, ALL_DISPLAY_EVENTS);
+ mDisplayManagerGlobal.registerDisplayListener(mListener, mHandler, ALL_DISPLAY_EVENTS,
+ null);
Mockito.verify(mDisplayManager)
.registerCallbackWithEventMask(mCallbackCaptor.capture(), anyLong());
IDisplayManagerCallback callback = mCallbackCaptor.getValue();
int displayId = 1;
mDisplayManagerGlobal.registerDisplayListener(mListener, mHandler,
- ALL_DISPLAY_EVENTS & ~DisplayManager.EVENT_FLAG_DISPLAY_ADDED);
+ ALL_DISPLAY_EVENTS & ~DisplayManager.EVENT_FLAG_DISPLAY_ADDED, null);
callback.onDisplayEvent(displayId, DisplayManagerGlobal.EVENT_DISPLAY_ADDED);
waitForHandler();
Mockito.verifyZeroInteractions(mListener);
mDisplayManagerGlobal.registerDisplayListener(mListener, mHandler,
- ALL_DISPLAY_EVENTS & ~DisplayManager.EVENT_FLAG_DISPLAY_CHANGED);
+ ALL_DISPLAY_EVENTS & ~DisplayManager.EVENT_FLAG_DISPLAY_CHANGED, null);
callback.onDisplayEvent(displayId, DisplayManagerGlobal.EVENT_DISPLAY_CHANGED);
waitForHandler();
Mockito.verifyZeroInteractions(mListener);
mDisplayManagerGlobal.registerDisplayListener(mListener, mHandler,
- ALL_DISPLAY_EVENTS & ~DisplayManager.EVENT_FLAG_DISPLAY_REMOVED);
+ ALL_DISPLAY_EVENTS & ~DisplayManager.EVENT_FLAG_DISPLAY_REMOVED, null);
callback.onDisplayEvent(displayId, DisplayManagerGlobal.EVENT_DISPLAY_REMOVED);
waitForHandler();
Mockito.verifyZeroInteractions(mListener);
@@ -139,7 +141,7 @@
public void testDisplayManagerGlobalRegistersWithDisplayManager_WhenThereAreListeners()
throws RemoteException {
mDisplayManagerGlobal.registerDisplayListener(mListener, mHandler,
- DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS);
+ DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS, null);
InOrder inOrder = Mockito.inOrder(mDisplayManager);
inOrder.verify(mDisplayManager)
@@ -163,6 +165,7 @@
}
private void waitForHandler() {
- mHandler.runWithScissors(() -> { }, 0);
+ mHandler.runWithScissors(() -> {
+ }, 0);
}
}
diff --git a/core/tests/coretests/src/android/os/health/SystemHealthManagerTest.java b/core/tests/coretests/src/android/os/health/SystemHealthManagerTest.java
deleted file mode 100644
index e1f9523..0000000
--- a/core/tests/coretests/src/android/os/health/SystemHealthManagerTest.java
+++ /dev/null
@@ -1,126 +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 android.os.health;
-
-import static androidx.test.InstrumentationRegistry.getContext;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.os.ConditionVariable;
-import android.os.PowerMonitor;
-import android.os.PowerMonitorReadings;
-
-import org.junit.Test;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class SystemHealthManagerTest {
- private List<PowerMonitor> mPowerMonitorInfo;
- private PowerMonitorReadings mReadings;
- private RuntimeException mException;
-
- @Test
- public void getPowerMonitors() {
- SystemHealthManager shm = getContext().getSystemService(SystemHealthManager.class);
- List<PowerMonitor> powerMonitorInfo = shm.getSupportedPowerMonitors();
- assertThat(powerMonitorInfo).isNotNull();
- if (powerMonitorInfo.isEmpty()) {
- // This device does not support PowerStats HAL
- return;
- }
-
- PowerMonitor consumerMonitor = null;
- PowerMonitor measurementMonitor = null;
- for (PowerMonitor pmi : powerMonitorInfo) {
- if (pmi.type == PowerMonitor.POWER_MONITOR_TYPE_MEASUREMENT) {
- measurementMonitor = pmi;
- } else {
- consumerMonitor = pmi;
- }
- }
-
- List<PowerMonitor> selectedMonitors = new ArrayList<>();
- if (consumerMonitor != null) {
- selectedMonitors.add(consumerMonitor);
- }
- if (measurementMonitor != null) {
- selectedMonitors.add(measurementMonitor);
- }
-
- PowerMonitorReadings readings = shm.getPowerMonitorReadings(selectedMonitors);
-
- for (PowerMonitor monitor : selectedMonitors) {
- assertThat(readings.getConsumedEnergyUws(monitor)).isAtLeast(0);
- assertThat(readings.getTimestamp(monitor)).isGreaterThan(0);
- }
- }
-
- @Test
- public void getPowerMonitorsAsync() {
- SystemHealthManager shm = getContext().getSystemService(SystemHealthManager.class);
- ConditionVariable done = new ConditionVariable();
- shm.getSupportedPowerMonitors(null, pms -> {
- mPowerMonitorInfo = pms;
- done.open();
- });
- done.block();
- assertThat(mPowerMonitorInfo).isNotNull();
- if (mPowerMonitorInfo.isEmpty()) {
- // This device does not support PowerStats HAL
- return;
- }
-
- PowerMonitor consumerMonitor = null;
- PowerMonitor measurementMonitor = null;
- for (PowerMonitor pmi : mPowerMonitorInfo) {
- if (pmi.type == PowerMonitor.POWER_MONITOR_TYPE_MEASUREMENT) {
- measurementMonitor = pmi;
- } else {
- consumerMonitor = pmi;
- }
- }
-
- List<PowerMonitor> selectedMonitors = new ArrayList<>();
- if (consumerMonitor != null) {
- selectedMonitors.add(consumerMonitor);
- }
- if (measurementMonitor != null) {
- selectedMonitors.add(measurementMonitor);
- }
-
- done.close();
- shm.getPowerMonitorReadings(selectedMonitors, null,
- readings -> {
- mReadings = readings;
- done.open();
- },
- exception -> {
- mException = exception;
- done.open();
- }
- );
- done.block();
-
- assertThat(mException).isNull();
-
- for (PowerMonitor monitor : selectedMonitors) {
- assertThat(mReadings.getConsumedEnergyUws(monitor)).isAtLeast(0);
- assertThat(mReadings.getTimestamp(monitor)).isGreaterThan(0);
- }
- }
-}
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 101f7c2..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..d47d789 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,31 @@
createOptions(
/* enableContentCaptureReceiver= */ true,
new ContentCaptureOptions.ContentProtectionOptions(
- /* enableReceiver= */ true, -BUFFER_SIZE));
+ /* enableReceiver= */ true,
+ -BUFFER_SIZE,
+ /* requiredGroups= */ List.of(List.of("a")),
+ /* optionalGroups= */ Collections.emptyList(),
+ /* optionalGroupsThreshold= */ 0));
+ MainContentCaptureSession session = createSession(options);
+ session.mContentProtectionEventProcessor = mMockContentProtectionEventProcessor;
+
+ session.onSessionStarted(/* resultCode= */ 0, /* binder= */ null);
+
+ assertThat(session.mContentProtectionEventProcessor).isNull();
+ verifyZeroInteractions(mMockContentProtectionEventProcessor);
+ }
+
+ @Test
+ public void onSessionStarted_contentProtectionNoGroups_processorNotCreated() {
+ ContentCaptureOptions options =
+ createOptions(
+ /* enableContentCaptureReceiver= */ true,
+ new ContentCaptureOptions.ContentProtectionOptions(
+ /* enableReceiver= */ true,
+ BUFFER_SIZE,
+ /* requiredGroups= */ Collections.emptyList(),
+ /* optionalGroups= */ Collections.emptyList(),
+ /* optionalGroupsThreshold= */ 0));
MainContentCaptureSession session = createSession(options);
session.mContentProtectionEventProcessor = mMockContentProtectionEventProcessor;
@@ -313,7 +338,11 @@
return createOptions(
enableContentCaptureReceiver,
new ContentCaptureOptions.ContentProtectionOptions(
- enableContentProtectionReceiver, BUFFER_SIZE));
+ enableContentProtectionReceiver,
+ BUFFER_SIZE,
+ /* requiredGroups= */ List.of(List.of("a")),
+ /* optionalGroups= */ Collections.emptyList(),
+ /* optionalGroupsThreshold= */ 0));
}
private ContentCaptureManager createManager(ContentCaptureOptions options) {
diff --git a/core/tests/coretests/src/android/view/contentprotection/ContentProtectionEventProcessorTest.java b/core/tests/coretests/src/android/view/contentprotection/ContentProtectionEventProcessorTest.java
index 39a2e0e..ba0dbf4 100644
--- a/core/tests/coretests/src/android/view/contentprotection/ContentProtectionEventProcessorTest.java
+++ b/core/tests/coretests/src/android/view/contentprotection/ContentProtectionEventProcessorTest.java
@@ -29,12 +29,13 @@
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
+import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.ContentCaptureOptions;
import android.content.Context;
import android.content.pm.ParceledListSlice;
import android.os.Handler;
-import android.os.Looper;
-import android.text.InputType;
+import android.os.test.TestLooper;
import android.view.View;
import android.view.contentcapture.ContentCaptureEvent;
import android.view.contentcapture.IContentCaptureManager;
@@ -57,6 +58,7 @@
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
+import org.mockito.verification.VerificationMode;
import java.time.Instant;
import java.util.ArrayList;
@@ -75,13 +77,25 @@
private static final String PACKAGE_NAME = "com.test.package.name";
- private static final String ANDROID_CLASS_NAME = "android.test.some.class.name";
+ private static final String TEXT_REQUIRED1 = "TEXT REQUIRED1 TEXT";
- private static final String PASSWORD_TEXT = "ENTER PASSWORD HERE";
+ private static final String TEXT_REQUIRED2 = "TEXT REQUIRED2 TEXT";
- private static final String SUSPICIOUS_TEXT = "PLEASE SIGN IN";
+ private static final String TEXT_OPTIONAL1 = "TEXT OPTIONAL1 TEXT";
- private static final String SAFE_TEXT = "SAFE TEXT";
+ private static final String TEXT_OPTIONAL2 = "TEXT OPTIONAL2 TEXT";
+
+ private static final String TEXT_CONTAINS_OPTIONAL3 = "TEXTOPTIONAL3TEXT";
+
+ private static final String TEXT_SHARED = "TEXT SHARED TEXT";
+
+ private static final String TEXT_SAFE = "TEXT SAFE TEXT";
+
+ private static final List<List<String>> REQUIRED_GROUPS =
+ List.of(List.of("required1", "missing"), List.of("required2", "shared"));
+
+ private static final List<List<String>> OPTIONAL_GROUPS =
+ List.of(List.of("optional1"), List.of("optional2", "optional3"), List.of("shared"));
private static final ContentCaptureEvent PROCESS_EVENT = createProcessEvent();
@@ -91,7 +105,17 @@
private static final Set<Integer> EVENT_TYPES_TO_STORE =
ImmutableSet.of(TYPE_VIEW_APPEARED, TYPE_VIEW_DISAPPEARED, TYPE_VIEW_TEXT_CHANGED);
- private static final int RESET_LOGIN_TOTAL_EVENTS_TO_PROCESS = 150;
+ private static final int BUFFER_SIZE = 150;
+
+ private static final int OPTIONAL_GROUPS_THRESHOLD = 1;
+
+ private static final ContentCaptureOptions.ContentProtectionOptions OPTIONS =
+ new ContentCaptureOptions.ContentProtectionOptions(
+ /* enableReceiver= */ true,
+ BUFFER_SIZE,
+ REQUIRED_GROUPS,
+ OPTIONAL_GROUPS,
+ OPTIONAL_GROUPS_THRESHOLD);
@Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
@@ -101,16 +125,19 @@
private final Context mContext = ApplicationProvider.getApplicationContext();
- private ContentProtectionEventProcessor mContentProtectionEventProcessor;
+ private final TestLooper mTestLooper = new TestLooper();
+
+ @NonNull private ContentProtectionEventProcessor mContentProtectionEventProcessor;
@Before
public void setup() {
mContentProtectionEventProcessor =
new ContentProtectionEventProcessor(
mMockEventBuffer,
- new Handler(Looper.getMainLooper()),
+ new Handler(mTestLooper.getLooper()),
mMockContentCaptureManager,
- PACKAGE_NAME);
+ PACKAGE_NAME,
+ OPTIONS);
}
@Test
@@ -156,347 +183,224 @@
}
@Test
- public void processEvent_loginDetected_inspectsOnlyTypeViewAppeared() {
- mContentProtectionEventProcessor.mPasswordFieldDetected = true;
- mContentProtectionEventProcessor.mSuspiciousTextDetected = true;
+ public void processEvent_loginDetected_true_eventText() throws Exception {
+ when(mMockEventBuffer.toArray()).thenReturn(BUFFERED_EVENTS);
+
+ mContentProtectionEventProcessor.processEvent(
+ createProcessEvent(
+ /* eventText= */ TEXT_REQUIRED1,
+ /* viewNodeText= */ null,
+ /* hintText= */ null));
+ mContentProtectionEventProcessor.processEvent(
+ createProcessEvent(
+ /* eventText= */ TEXT_REQUIRED2,
+ /* viewNodeText= */ null,
+ /* hintText= */ null));
+ mContentProtectionEventProcessor.processEvent(
+ createProcessEvent(
+ /* eventText= */ TEXT_OPTIONAL1,
+ /* viewNodeText= */ null,
+ /* hintText= */ null));
+
+ assertLoginDetected();
+ }
+
+ @Test
+ public void processEvent_loginDetected_true_viewNodeText() throws Exception {
+ when(mMockEventBuffer.toArray()).thenReturn(BUFFERED_EVENTS);
+
+ mContentProtectionEventProcessor.processEvent(
+ createProcessEvent(
+ /* eventText= */ null,
+ /* viewNodeText= */ TEXT_REQUIRED1,
+ /* hintText= */ null));
+ mContentProtectionEventProcessor.processEvent(
+ createProcessEvent(
+ /* eventText= */ null,
+ /* viewNodeText= */ TEXT_REQUIRED2,
+ /* hintText= */ null));
+ mContentProtectionEventProcessor.processEvent(
+ createProcessEvent(
+ /* eventText= */ null,
+ /* viewNodeText= */ TEXT_OPTIONAL1,
+ /* hintText= */ null));
+
+ assertLoginDetected();
+ }
+
+ @Test
+ public void processEvent_loginDetected_true_hintText() throws Exception {
+ when(mMockEventBuffer.toArray()).thenReturn(BUFFERED_EVENTS);
+
+ mContentProtectionEventProcessor.processEvent(
+ createProcessEvent(
+ /* eventText= */ null,
+ /* viewNodeText= */ null,
+ /* hintText= */ TEXT_REQUIRED1));
+ mContentProtectionEventProcessor.processEvent(
+ createProcessEvent(
+ /* eventText= */ null,
+ /* viewNodeText= */ null,
+ /* hintText= */ TEXT_REQUIRED2));
+ mContentProtectionEventProcessor.processEvent(
+ createProcessEvent(
+ /* eventText= */ null,
+ /* viewNodeText= */ null,
+ /* hintText= */ TEXT_OPTIONAL1));
+
+ assertLoginDetected();
+ }
+
+ @Test
+ public void processEvent_loginDetected_true_differentOptionalGroup() throws Exception {
+ when(mMockEventBuffer.toArray()).thenReturn(BUFFERED_EVENTS);
+
+ mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_REQUIRED1));
+ mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_REQUIRED2));
+ mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_OPTIONAL2));
+
+ assertLoginDetected();
+ }
+
+ @Test
+ public void processEvent_loginDetected_true_usesContains() throws Exception {
+ when(mMockEventBuffer.toArray()).thenReturn(BUFFERED_EVENTS);
+
+ mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_REQUIRED1));
+ mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_REQUIRED2));
+ mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_CONTAINS_OPTIONAL3));
+
+ assertLoginDetected();
+ }
+
+ @Test
+ public void processEvent_loginDetected_false_missingRequiredGroups() {
+ mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_REQUIRED1));
+ mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_OPTIONAL1));
+
+ assertLoginNotDetected();
+ }
+
+ @Test
+ public void processEvent_loginDetected_false_missingOptionalGroups() {
+ mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_REQUIRED1));
+ mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_REQUIRED2));
+
+ assertLoginNotDetected();
+ }
+
+ @Test
+ public void processEvent_loginDetected_false_safeText() {
+ mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_REQUIRED1));
+ mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_REQUIRED2));
+ mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_SAFE));
+ mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_SAFE));
+ mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_SAFE));
+
+ assertLoginNotDetected();
+ }
+
+ @Test
+ public void processEvent_loginDetected_false_sharedTextOnce() {
+ mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_REQUIRED1));
+ mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_SHARED));
+
+ assertLoginNotDetected();
+ }
+
+ @Test
+ public void processEvent_loginDetected_true_sharedTextMultiple() throws Exception {
+ when(mMockEventBuffer.toArray()).thenReturn(BUFFERED_EVENTS);
+
+ mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_REQUIRED1));
+ mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_SHARED));
+ mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_SHARED));
+
+ assertLoginDetected();
+ }
+
+ @Test
+ public void processEvent_loginDetected_false_inspectsOnlyTypeViewAppeared() {
+ mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_REQUIRED1));
+ mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_REQUIRED2));
for (int type = -100; type <= 100; type++) {
if (type == TYPE_VIEW_APPEARED) {
continue;
}
-
- mContentProtectionEventProcessor.processEvent(createEvent(type));
-
- assertThat(mContentProtectionEventProcessor.mPasswordFieldDetected).isTrue();
- assertThat(mContentProtectionEventProcessor.mSuspiciousTextDetected).isTrue();
+ ContentCaptureEvent event = createEvent(type);
+ event.setText(TEXT_OPTIONAL1);
+ mContentProtectionEventProcessor.processEvent(event);
}
- verify(mMockEventBuffer, never()).clear();
- verify(mMockEventBuffer, never()).toArray();
- verifyZeroInteractions(mMockContentCaptureManager);
+ assertLoginNotDetected();
}
@Test
- public void processEvent_loginDetected() throws Exception {
+ public void processEvent_loginDetected_true_belowResetLimit() throws Exception {
when(mMockEventBuffer.toArray()).thenReturn(BUFFERED_EVENTS);
- mContentProtectionEventProcessor.mPasswordFieldDetected = true;
- mContentProtectionEventProcessor.mSuspiciousTextDetected = true;
- mContentProtectionEventProcessor.processEvent(PROCESS_EVENT);
+ mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_REQUIRED1));
+ mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_REQUIRED2));
- assertThat(mContentProtectionEventProcessor.mPasswordFieldDetected).isFalse();
- assertThat(mContentProtectionEventProcessor.mSuspiciousTextDetected).isFalse();
- verify(mMockEventBuffer).clear();
- verify(mMockEventBuffer).toArray();
- assertOnLoginDetected();
- }
-
- @Test
- public void processEvent_loginDetected_passwordFieldNotDetected() {
- mContentProtectionEventProcessor.mSuspiciousTextDetected = true;
- assertThat(mContentProtectionEventProcessor.mPasswordFieldDetected).isFalse();
-
- mContentProtectionEventProcessor.processEvent(PROCESS_EVENT);
-
- assertThat(mContentProtectionEventProcessor.mPasswordFieldDetected).isFalse();
- assertThat(mContentProtectionEventProcessor.mSuspiciousTextDetected).isTrue();
- verify(mMockEventBuffer, never()).clear();
- verify(mMockEventBuffer, never()).toArray();
- verifyZeroInteractions(mMockContentCaptureManager);
- }
-
- @Test
- public void processEvent_loginDetected_suspiciousTextNotDetected() {
- mContentProtectionEventProcessor.mPasswordFieldDetected = true;
- assertThat(mContentProtectionEventProcessor.mSuspiciousTextDetected).isFalse();
-
- mContentProtectionEventProcessor.processEvent(PROCESS_EVENT);
-
- assertThat(mContentProtectionEventProcessor.mPasswordFieldDetected).isTrue();
- assertThat(mContentProtectionEventProcessor.mSuspiciousTextDetected).isFalse();
- verify(mMockEventBuffer, never()).clear();
- verify(mMockEventBuffer, never()).toArray();
- verifyZeroInteractions(mMockContentCaptureManager);
- }
-
- @Test
- public void processEvent_loginDetected_withoutViewNode() {
- assertThat(mContentProtectionEventProcessor.mPasswordFieldDetected).isFalse();
- assertThat(mContentProtectionEventProcessor.mSuspiciousTextDetected).isFalse();
-
- mContentProtectionEventProcessor.processEvent(PROCESS_EVENT);
-
- assertThat(mContentProtectionEventProcessor.mPasswordFieldDetected).isFalse();
- assertThat(mContentProtectionEventProcessor.mSuspiciousTextDetected).isFalse();
- verify(mMockEventBuffer, never()).clear();
- verify(mMockEventBuffer, never()).toArray();
- verifyZeroInteractions(mMockContentCaptureManager);
- }
-
- @Test
- public void processEvent_loginDetected_belowResetLimit() throws Exception {
- when(mMockEventBuffer.toArray()).thenReturn(BUFFERED_EVENTS);
- mContentProtectionEventProcessor.mSuspiciousTextDetected = true;
- ContentCaptureEvent event =
- createAndroidPasswordFieldEvent(
- ANDROID_CLASS_NAME, InputType.TYPE_TEXT_VARIATION_PASSWORD);
-
- for (int i = 0; i < RESET_LOGIN_TOTAL_EVENTS_TO_PROCESS; i++) {
+ for (int i = 0; i < BUFFER_SIZE - 2; i++) {
mContentProtectionEventProcessor.processEvent(PROCESS_EVENT);
}
- assertThat(mContentProtectionEventProcessor.mPasswordFieldDetected).isFalse();
- assertThat(mContentProtectionEventProcessor.mSuspiciousTextDetected).isTrue();
- verify(mMockEventBuffer, never()).clear();
- verify(mMockEventBuffer, never()).toArray();
+ assertLoginNotDetected();
- mContentProtectionEventProcessor.processEvent(event);
+ mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_OPTIONAL1));
- assertThat(mContentProtectionEventProcessor.mPasswordFieldDetected).isFalse();
- assertThat(mContentProtectionEventProcessor.mSuspiciousTextDetected).isFalse();
- verify(mMockEventBuffer).clear();
- verify(mMockEventBuffer).toArray();
- assertOnLoginDetected();
+ assertLoginDetected();
}
@Test
- public void processEvent_loginDetected_aboveResetLimit() throws Exception {
- mContentProtectionEventProcessor.mSuspiciousTextDetected = true;
- ContentCaptureEvent event =
- createAndroidPasswordFieldEvent(
- ANDROID_CLASS_NAME, InputType.TYPE_TEXT_VARIATION_PASSWORD);
+ public void processEvent_loginDetected_false_aboveResetLimit() {
+ mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_REQUIRED1));
+ mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_REQUIRED2));
- for (int i = 0; i < RESET_LOGIN_TOTAL_EVENTS_TO_PROCESS + 1; i++) {
+ for (int i = 0; i < BUFFER_SIZE - 1; i++) {
mContentProtectionEventProcessor.processEvent(PROCESS_EVENT);
}
- assertThat(mContentProtectionEventProcessor.mPasswordFieldDetected).isFalse();
- assertThat(mContentProtectionEventProcessor.mSuspiciousTextDetected).isFalse();
- verify(mMockEventBuffer, never()).clear();
- verify(mMockEventBuffer, never()).toArray();
+ assertLoginNotDetected();
- mContentProtectionEventProcessor.processEvent(event);
+ mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_OPTIONAL1));
- assertThat(mContentProtectionEventProcessor.mPasswordFieldDetected).isTrue();
- assertThat(mContentProtectionEventProcessor.mSuspiciousTextDetected).isFalse();
- verify(mMockEventBuffer, never()).clear();
- verify(mMockEventBuffer, never()).toArray();
+ assertLoginNotDetected();
}
@Test
public void processEvent_multipleLoginsDetected_belowFlushThreshold() throws Exception {
when(mMockEventBuffer.toArray()).thenReturn(BUFFERED_EVENTS);
- mContentProtectionEventProcessor.mPasswordFieldDetected = true;
- mContentProtectionEventProcessor.mSuspiciousTextDetected = true;
- mContentProtectionEventProcessor.processEvent(PROCESS_EVENT);
+ for (int i = 0; i < 2; i++) {
+ mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_REQUIRED1));
+ mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_REQUIRED2));
+ mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_OPTIONAL1));
+ mContentProtectionEventProcessor.processEvent(PROCESS_EVENT);
+ }
- mContentProtectionEventProcessor.mPasswordFieldDetected = true;
- mContentProtectionEventProcessor.mSuspiciousTextDetected = true;
- mContentProtectionEventProcessor.processEvent(PROCESS_EVENT);
-
- assertThat(mContentProtectionEventProcessor.mPasswordFieldDetected).isFalse();
- assertThat(mContentProtectionEventProcessor.mSuspiciousTextDetected).isFalse();
- verify(mMockEventBuffer).clear();
- verify(mMockEventBuffer).toArray();
- assertOnLoginDetected();
+ assertLoginDetected();
}
@Test
public void processEvent_multipleLoginsDetected_aboveFlushThreshold() throws Exception {
when(mMockEventBuffer.toArray()).thenReturn(BUFFERED_EVENTS);
- mContentProtectionEventProcessor.mPasswordFieldDetected = true;
- mContentProtectionEventProcessor.mSuspiciousTextDetected = true;
+ mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_REQUIRED1));
+ mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_REQUIRED2));
+ mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_OPTIONAL1));
mContentProtectionEventProcessor.processEvent(PROCESS_EVENT);
mContentProtectionEventProcessor.mLastFlushTime = Instant.now().minusSeconds(5);
- mContentProtectionEventProcessor.mPasswordFieldDetected = true;
- mContentProtectionEventProcessor.mSuspiciousTextDetected = true;
+ mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_REQUIRED1));
+ mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_REQUIRED2));
+ mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_OPTIONAL1));
mContentProtectionEventProcessor.processEvent(PROCESS_EVENT);
- assertThat(mContentProtectionEventProcessor.mPasswordFieldDetected).isFalse();
- assertThat(mContentProtectionEventProcessor.mSuspiciousTextDetected).isFalse();
- verify(mMockEventBuffer, times(2)).clear();
- verify(mMockEventBuffer, times(2)).toArray();
- assertOnLoginDetected(PROCESS_EVENT, /* times= */ 2);
- }
-
- @Test
- public void isPasswordField_android() {
- ContentCaptureEvent event =
- createAndroidPasswordFieldEvent(
- ANDROID_CLASS_NAME, InputType.TYPE_TEXT_VARIATION_PASSWORD);
-
- mContentProtectionEventProcessor.processEvent(event);
-
- assertThat(mContentProtectionEventProcessor.mPasswordFieldDetected).isTrue();
- verify(mMockEventBuffer, never()).clear();
- verify(mMockEventBuffer, never()).toArray();
- verifyZeroInteractions(mMockContentCaptureManager);
- }
-
- @Test
- public void isPasswordField_android_withoutClassName() {
- ContentCaptureEvent event =
- createAndroidPasswordFieldEvent(
- /* className= */ null, InputType.TYPE_TEXT_VARIATION_PASSWORD);
-
- mContentProtectionEventProcessor.processEvent(event);
-
- assertThat(mContentProtectionEventProcessor.mPasswordFieldDetected).isFalse();
- verify(mMockEventBuffer, never()).clear();
- verify(mMockEventBuffer, never()).toArray();
- verifyZeroInteractions(mMockContentCaptureManager);
- }
-
- @Test
- public void isPasswordField_android_wrongClassName() {
- ContentCaptureEvent event =
- createAndroidPasswordFieldEvent(
- "wrong.prefix" + ANDROID_CLASS_NAME,
- InputType.TYPE_TEXT_VARIATION_PASSWORD);
-
- mContentProtectionEventProcessor.processEvent(event);
-
- assertThat(mContentProtectionEventProcessor.mPasswordFieldDetected).isFalse();
- verify(mMockEventBuffer, never()).clear();
- verify(mMockEventBuffer, never()).toArray();
- verifyZeroInteractions(mMockContentCaptureManager);
- }
-
- @Test
- public void isPasswordField_android_wrongInputType() {
- ContentCaptureEvent event =
- createAndroidPasswordFieldEvent(
- ANDROID_CLASS_NAME, InputType.TYPE_TEXT_VARIATION_NORMAL);
-
- mContentProtectionEventProcessor.processEvent(event);
-
- assertThat(mContentProtectionEventProcessor.mPasswordFieldDetected).isFalse();
- verify(mMockEventBuffer, never()).clear();
- verify(mMockEventBuffer, never()).toArray();
- verifyZeroInteractions(mMockContentCaptureManager);
- }
-
- @Test
- public void isPasswordField_webView() throws Exception {
- ContentCaptureEvent event =
- createWebViewPasswordFieldEvent(
- /* className= */ null, /* eventText= */ null, PASSWORD_TEXT);
- when(mMockEventBuffer.toArray()).thenReturn(new ContentCaptureEvent[] {event});
-
- mContentProtectionEventProcessor.processEvent(event);
-
- assertThat(mContentProtectionEventProcessor.mPasswordFieldDetected).isFalse();
- verify(mMockEventBuffer).clear();
- verify(mMockEventBuffer).toArray();
- assertOnLoginDetected(event, /* times= */ 1);
- }
-
- @Test
- public void isPasswordField_webView_withClassName() {
- ContentCaptureEvent event =
- createWebViewPasswordFieldEvent(
- /* className= */ "any.class.name", /* eventText= */ null, PASSWORD_TEXT);
-
- mContentProtectionEventProcessor.processEvent(event);
-
- assertThat(mContentProtectionEventProcessor.mPasswordFieldDetected).isFalse();
- verify(mMockEventBuffer, never()).clear();
- verify(mMockEventBuffer, never()).toArray();
- verifyZeroInteractions(mMockContentCaptureManager);
- }
-
- @Test
- public void isPasswordField_webView_withSafeViewNodeText() {
- ContentCaptureEvent event =
- createWebViewPasswordFieldEvent(
- /* className= */ null, /* eventText= */ null, SAFE_TEXT);
-
- mContentProtectionEventProcessor.processEvent(event);
-
- assertThat(mContentProtectionEventProcessor.mPasswordFieldDetected).isFalse();
- verify(mMockEventBuffer, never()).clear();
- verify(mMockEventBuffer, never()).toArray();
- verifyZeroInteractions(mMockContentCaptureManager);
- }
-
- @Test
- public void isPasswordField_webView_withEventText() {
- ContentCaptureEvent event =
- createWebViewPasswordFieldEvent(/* className= */ null, PASSWORD_TEXT, SAFE_TEXT);
-
- mContentProtectionEventProcessor.processEvent(event);
-
- assertThat(mContentProtectionEventProcessor.mPasswordFieldDetected).isFalse();
- verify(mMockEventBuffer, never()).clear();
- verify(mMockEventBuffer, never()).toArray();
- verifyZeroInteractions(mMockContentCaptureManager);
- }
-
- @Test
- public void isSuspiciousText_withSafeText() {
- ContentCaptureEvent event = createSuspiciousTextEvent(SAFE_TEXT, SAFE_TEXT);
-
- mContentProtectionEventProcessor.processEvent(event);
-
- assertThat(mContentProtectionEventProcessor.mSuspiciousTextDetected).isFalse();
- verify(mMockEventBuffer, never()).clear();
- verify(mMockEventBuffer, never()).toArray();
- verifyZeroInteractions(mMockContentCaptureManager);
- }
-
- @Test
- public void isSuspiciousText_eventText_suspiciousText() {
- ContentCaptureEvent event = createSuspiciousTextEvent(SUSPICIOUS_TEXT, SAFE_TEXT);
-
- mContentProtectionEventProcessor.processEvent(event);
-
- assertThat(mContentProtectionEventProcessor.mSuspiciousTextDetected).isTrue();
- verify(mMockEventBuffer, never()).clear();
- verify(mMockEventBuffer, never()).toArray();
- verifyZeroInteractions(mMockContentCaptureManager);
- }
-
- @Test
- public void isSuspiciousText_viewNodeText_suspiciousText() {
- ContentCaptureEvent event = createSuspiciousTextEvent(SAFE_TEXT, SUSPICIOUS_TEXT);
-
- mContentProtectionEventProcessor.processEvent(event);
-
- assertThat(mContentProtectionEventProcessor.mSuspiciousTextDetected).isTrue();
- verify(mMockEventBuffer, never()).clear();
- verify(mMockEventBuffer, never()).toArray();
- verifyZeroInteractions(mMockContentCaptureManager);
- }
-
- @Test
- public void isSuspiciousText_eventText_passwordText() {
- ContentCaptureEvent event = createSuspiciousTextEvent(PASSWORD_TEXT, SAFE_TEXT);
-
- mContentProtectionEventProcessor.processEvent(event);
-
- assertThat(mContentProtectionEventProcessor.mSuspiciousTextDetected).isTrue();
- verify(mMockEventBuffer, never()).clear();
- verify(mMockEventBuffer, never()).toArray();
- verifyZeroInteractions(mMockContentCaptureManager);
- }
-
- @Test
- public void isSuspiciousText_viewNodeText_passwordText() {
- // Specify the class to differ from {@link isPasswordField_webView} test in this version
- ContentCaptureEvent event =
- createProcessEvent(
- "test.class.not.a.web.view", /* inputType= */ 0, SAFE_TEXT, PASSWORD_TEXT);
-
- mContentProtectionEventProcessor.processEvent(event);
-
- assertThat(mContentProtectionEventProcessor.mSuspiciousTextDetected).isTrue();
- verify(mMockEventBuffer, never()).clear();
- verify(mMockEventBuffer, never()).toArray();
- verifyZeroInteractions(mMockContentCaptureManager);
+ assertLoginDetected(times(2));
}
private static ContentCaptureEvent createEvent(int type) {
@@ -511,20 +415,20 @@
return createEvent(TYPE_VIEW_APPEARED);
}
+ private ContentCaptureEvent createProcessEvent(@Nullable String eventText) {
+ return createProcessEvent(eventText, /* viewNodeText= */ null, /* hintText= */ null);
+ }
+
private ContentCaptureEvent createProcessEvent(
- @Nullable String className,
- int inputType,
- @Nullable String eventText,
- @Nullable String viewNodeText) {
+ @Nullable String eventText, @Nullable String viewNodeText, @Nullable String hintText) {
View view = new View(mContext);
ViewStructureImpl viewStructure = new ViewStructureImpl(view);
- if (className != null) {
- viewStructure.setClassName(className);
- }
if (viewNodeText != null) {
viewStructure.setText(viewNodeText);
}
- viewStructure.setInputType(inputType);
+ if (hintText != null) {
+ viewStructure.setHint(hintText);
+ }
ContentCaptureEvent event = createProcessEvent();
event.setViewNode(viewStructure.getNode());
@@ -535,34 +439,28 @@
return event;
}
- private ContentCaptureEvent createAndroidPasswordFieldEvent(
- @Nullable String className, int inputType) {
- return createProcessEvent(
- className, inputType, /* eventText= */ null, /* viewNodeText= */ null);
+ private void assertLoginNotDetected() {
+ mTestLooper.dispatchAll();
+ verify(mMockEventBuffer, never()).clear();
+ verify(mMockEventBuffer, never()).toArray();
+ verifyZeroInteractions(mMockContentCaptureManager);
}
- private ContentCaptureEvent createWebViewPasswordFieldEvent(
- @Nullable String className, @Nullable String eventText, @Nullable String viewNodeText) {
- return createProcessEvent(className, /* inputType= */ 0, eventText, viewNodeText);
+ private void assertLoginDetected() throws Exception {
+ assertLoginDetected(times(1));
}
- private ContentCaptureEvent createSuspiciousTextEvent(
- @Nullable String eventText, @Nullable String viewNodeText) {
- return createProcessEvent(
- /* className= */ null, /* inputType= */ 0, eventText, viewNodeText);
- }
+ private void assertLoginDetected(@NonNull VerificationMode verificationMode) throws Exception {
+ mTestLooper.dispatchAll();
+ verify(mMockEventBuffer, verificationMode).clear();
+ verify(mMockEventBuffer, verificationMode).toArray();
- private void assertOnLoginDetected() throws Exception {
- assertOnLoginDetected(PROCESS_EVENT, /* times= */ 1);
- }
-
- private void assertOnLoginDetected(ContentCaptureEvent event, int times) throws Exception {
ArgumentCaptor<ParceledListSlice> captor = ArgumentCaptor.forClass(ParceledListSlice.class);
- verify(mMockContentCaptureManager, times(times)).onLoginDetected(captor.capture());
+ verify(mMockContentCaptureManager, verificationMode).onLoginDetected(captor.capture());
assertThat(captor.getValue()).isNotNull();
List<ContentCaptureEvent> actual = captor.getValue().getList();
assertThat(actual).isNotNull();
- assertThat(actual).containsExactly(event);
+ assertThat(actual).containsExactly(PROCESS_EVENT);
}
}
diff --git a/core/tests/coretests/src/android/view/contentprotection/ContentProtectionUtilsTest.java b/core/tests/coretests/src/android/view/contentprotection/ContentProtectionUtilsTest.java
index 1459799..fbe478e 100644
--- a/core/tests/coretests/src/android/view/contentprotection/ContentProtectionUtilsTest.java
+++ b/core/tests/coretests/src/android/view/contentprotection/ContentProtectionUtilsTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013 The Android Open Source Project
+ * Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -44,68 +44,74 @@
private static final String TEXT = "TEST_TEXT";
- private static final ContentCaptureEvent EVENT = createEvent();
-
- private static final ViewNode VIEW_NODE = new ViewNode();
-
- private static final ViewNode VIEW_NODE_WITH_TEXT = createViewNodeWithText();
+ private static final String TEXT_LOWER = TEXT.toLowerCase();
@Test
- public void event_getEventText_null() {
- String actual = ContentProtectionUtils.getEventText(EVENT);
+ public void getEventTextLower_null() {
+ String actual = ContentProtectionUtils.getEventTextLower(createEvent());
assertThat(actual).isNull();
}
@Test
- public void event_getEventText_notNull() {
- ContentCaptureEvent event = createEvent();
- event.setText(TEXT);
+ public void getEventTextLower_notNull() {
+ String actual = ContentProtectionUtils.getEventTextLower(createEventWithText());
- String actual = ContentProtectionUtils.getEventText(event);
-
- assertThat(actual).isEqualTo(TEXT);
+ assertThat(actual).isEqualTo(TEXT_LOWER);
}
@Test
- public void event_getViewNodeText_null() {
- String actual = ContentProtectionUtils.getViewNodeText(EVENT);
+ public void getViewNodeTextLower_null() {
+ String actual = ContentProtectionUtils.getViewNodeTextLower(new ViewNode());
assertThat(actual).isNull();
}
@Test
- public void event_getViewNodeText_notNull() {
- ContentCaptureEvent event = createEvent();
- event.setViewNode(VIEW_NODE_WITH_TEXT);
+ public void getViewNodeTextLower_notNull() {
+ String actual = ContentProtectionUtils.getViewNodeTextLower(createViewNodeWithText());
- String actual = ContentProtectionUtils.getViewNodeText(event);
-
- assertThat(actual).isEqualTo(TEXT);
+ assertThat(actual).isEqualTo(TEXT_LOWER);
}
@Test
- public void viewNode_getViewNodeText_null() {
- String actual = ContentProtectionUtils.getViewNodeText(VIEW_NODE);
+ public void getHintTextLower_null() {
+ String actual = ContentProtectionUtils.getHintTextLower(new ViewNode());
assertThat(actual).isNull();
}
@Test
- public void viewNode_getViewNodeText_notNull() {
- String actual = ContentProtectionUtils.getViewNodeText(VIEW_NODE_WITH_TEXT);
+ public void getHintTextLower_notNull() {
+ String actual = ContentProtectionUtils.getHintTextLower(createViewNodeWithHint());
- assertThat(actual).isEqualTo(TEXT);
+ assertThat(actual).isEqualTo(TEXT_LOWER);
}
private static ContentCaptureEvent createEvent() {
return new ContentCaptureEvent(/* sessionId= */ 123, TYPE_SESSION_STARTED);
}
- private static ViewNode createViewNodeWithText() {
+ private static ContentCaptureEvent createEventWithText() {
+ ContentCaptureEvent event = createEvent();
+ event.setText(TEXT);
+ return event;
+ }
+
+ private static ViewStructureImpl createViewStructureImpl() {
View view = new View(ApplicationProvider.getApplicationContext());
- ViewStructureImpl viewStructure = new ViewStructureImpl(view);
- viewStructure.setText(TEXT);
- return viewStructure.getNode();
+ return new ViewStructureImpl(view);
+ }
+
+ private static ViewNode createViewNodeWithText() {
+ ViewStructureImpl viewStructureImpl = createViewStructureImpl();
+ viewStructureImpl.setText(TEXT);
+ return viewStructureImpl.getNode();
+ }
+
+ private static ViewNode createViewNodeWithHint() {
+ ViewStructureImpl viewStructureImpl = createViewStructureImpl();
+ viewStructureImpl.setHint(TEXT);
+ return viewStructureImpl.getNode();
}
}
diff --git a/core/tests/coretests/src/android/window/SystemPerformanceHinterTests.java b/core/tests/coretests/src/android/window/SystemPerformanceHinterTests.java
index 263e563..6229530 100644
--- a/core/tests/coretests/src/android/window/SystemPerformanceHinterTests.java
+++ b/core/tests/coretests/src/android/window/SystemPerformanceHinterTests.java
@@ -31,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;
@@ -154,7 +155,8 @@
eq(FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN));
verify(mTransaction).setFrameRateCategory(
eq(mDefaultDisplayRoot),
- eq(FRAME_RATE_CATEGORY_HIGH));
+ eq(FRAME_RATE_CATEGORY_HIGH),
+ eq(false));
verify(mTransaction).applyAsyncUnsafe();
}
@@ -171,7 +173,8 @@
eq(FRAME_RATE_SELECTION_STRATEGY_SELF));
verify(mTransaction).setFrameRateCategory(
eq(mDefaultDisplayRoot),
- eq(FRAME_RATE_CATEGORY_DEFAULT));
+ eq(FRAME_RATE_CATEGORY_DEFAULT),
+ eq(false));
verify(mTransaction).applyAsyncUnsafe();
}
@@ -241,7 +244,8 @@
eq(FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN));
verify(mTransaction).setFrameRateCategory(
eq(mDefaultDisplayRoot),
- eq(FRAME_RATE_CATEGORY_HIGH));
+ eq(FRAME_RATE_CATEGORY_HIGH),
+ eq(false));
verify(mTransaction).setEarlyWakeupStart();
verify(mTransaction).applyAsyncUnsafe();
verify(mAdpfSession).sendHint(eq(CPU_LOAD_UP));
@@ -261,7 +265,8 @@
eq(FRAME_RATE_SELECTION_STRATEGY_SELF));
verify(mTransaction).setFrameRateCategory(
eq(mDefaultDisplayRoot),
- eq(FRAME_RATE_CATEGORY_DEFAULT));
+ eq(FRAME_RATE_CATEGORY_DEFAULT),
+ eq(false));
verify(mTransaction).setEarlyWakeupEnd();
verify(mTransaction).applyAsyncUnsafe();
verify(mAdpfSession).sendHint(eq(CPU_LOAD_RESET));
@@ -281,7 +286,8 @@
eq(FRAME_RATE_SELECTION_STRATEGY_SELF));
verify(mTransaction).setFrameRateCategory(
eq(mDefaultDisplayRoot),
- eq(FRAME_RATE_CATEGORY_DEFAULT));
+ eq(FRAME_RATE_CATEGORY_DEFAULT),
+ eq(false));
verify(mTransaction).setEarlyWakeupEnd();
verify(mTransaction).applyAsyncUnsafe();
verify(mAdpfSession).sendHint(eq(CPU_LOAD_RESET));
@@ -299,7 +305,8 @@
eq(FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN));
verify(mTransaction).setFrameRateCategory(
eq(mDefaultDisplayRoot),
- eq(FRAME_RATE_CATEGORY_HIGH));
+ eq(FRAME_RATE_CATEGORY_HIGH),
+ eq(false));
verify(mTransaction).setEarlyWakeupStart();
verify(mTransaction).applyAsyncUnsafe();
verify(mAdpfSession).sendHint(eq(CPU_LOAD_UP));
@@ -310,7 +317,7 @@
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());
+ verify(mTransaction, never()).setFrameRateCategory(any(), anyInt(), anyBoolean());
verify(mTransaction, never()).setEarlyWakeupEnd();
verify(mTransaction, never()).applyAsyncUnsafe();
verify(mAdpfSession, never()).sendHint(anyInt());
@@ -318,7 +325,7 @@
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());
+ verify(mTransaction, never()).setFrameRateCategory(any(), anyInt(), anyBoolean());
verify(mTransaction, never()).setEarlyWakeupEnd();
verify(mTransaction, never()).applyAsyncUnsafe();
verify(mAdpfSession, never()).sendHint(anyInt());
@@ -330,7 +337,8 @@
eq(FRAME_RATE_SELECTION_STRATEGY_SELF));
verify(mTransaction).setFrameRateCategory(
eq(mDefaultDisplayRoot),
- eq(FRAME_RATE_CATEGORY_DEFAULT));
+ eq(FRAME_RATE_CATEGORY_DEFAULT),
+ eq(false));
verify(mTransaction).setEarlyWakeupEnd();
verify(mTransaction).applyAsyncUnsafe();
verify(mAdpfSession).sendHint(eq(CPU_LOAD_RESET));
@@ -348,7 +356,8 @@
eq(FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN));
verify(mTransaction).setFrameRateCategory(
eq(mDefaultDisplayRoot),
- eq(FRAME_RATE_CATEGORY_HIGH));
+ eq(FRAME_RATE_CATEGORY_HIGH),
+ eq(false));
verify(mTransaction).setEarlyWakeupStart();
verify(mTransaction).applyAsyncUnsafe();
verify(mAdpfSession).sendHint(eq(CPU_LOAD_UP));
@@ -363,7 +372,8 @@
eq(FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN));
verify(mTransaction).setFrameRateCategory(
eq(mSecondaryDisplayRoot),
- eq(FRAME_RATE_CATEGORY_HIGH));
+ eq(FRAME_RATE_CATEGORY_HIGH),
+ eq(false));
verify(mTransaction, never()).setEarlyWakeupStart();
verify(mTransaction).applyAsyncUnsafe();
verify(mAdpfSession, never()).sendHint(anyInt());
@@ -378,13 +388,15 @@
eq(FRAME_RATE_SELECTION_STRATEGY_SELF));
verify(mTransaction).setFrameRateCategory(
eq(mDefaultDisplayRoot),
- eq(FRAME_RATE_CATEGORY_DEFAULT));
+ eq(FRAME_RATE_CATEGORY_DEFAULT),
+ eq(false));
verify(mTransaction, never()).setFrameRateSelectionStrategy(
eq(mSecondaryDisplayRoot),
anyInt());
verify(mTransaction, never()).setFrameRateCategory(
eq(mSecondaryDisplayRoot),
- anyInt());
+ anyInt(),
+ eq(false));
verify(mTransaction, never()).setEarlyWakeupEnd();
verify(mTransaction).applyAsyncUnsafe();
verify(mAdpfSession, never()).sendHint(anyInt());
@@ -401,7 +413,8 @@
eq(FRAME_RATE_SELECTION_STRATEGY_SELF));
verify(mTransaction).setFrameRateCategory(
eq(mSecondaryDisplayRoot),
- eq(FRAME_RATE_CATEGORY_DEFAULT));
+ eq(FRAME_RATE_CATEGORY_DEFAULT),
+ eq(false));
verify(mTransaction).setEarlyWakeupEnd();
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/coretests/src/com/android/internal/os/BatteryStatsTests.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
index 2cbeaa1..f846ac5 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
@@ -34,6 +34,7 @@
KernelSingleUidTimeReaderTest.class,
LongArrayMultiStateCounterTest.class,
LongMultiStateCounterTest.class,
+ MonotonicClockTest.class,
PowerProfileTest.class,
PowerStatsTest.class,
diff --git a/core/tests/coretests/src/com/android/internal/os/MonotonicClockTest.java b/core/tests/coretests/src/com/android/internal/os/MonotonicClockTest.java
new file mode 100644
index 0000000..7951270
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/os/MonotonicClockTest.java
@@ -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.internal.os;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.util.Xml;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class MonotonicClockTest {
+ private final MockClock mClock = new MockClock();
+
+ @Test
+ public void persistence() throws IOException {
+ MonotonicClock monotonicClock = new MonotonicClock(1000, mClock);
+ mClock.realtime = 234;
+
+ assertThat(monotonicClock.monotonicTime()).isEqualTo(1234);
+
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ monotonicClock.writeXml(out, Xml.newFastSerializer());
+ String xml = out.toString();
+ assertThat(xml).contains("timeshift=\"1234\"");
+
+ mClock.realtime = 42;
+ MonotonicClock newMonotonicClock = new MonotonicClock(0, mClock);
+ newMonotonicClock.readXml(new ByteArrayInputStream(out.toByteArray()),
+ Xml.newFastPullParser());
+
+ mClock.realtime = 2000;
+ assertThat(newMonotonicClock.monotonicTime()).isEqualTo(1234 - 42 + 2000);
+ }
+}
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index 9c65287..b71aaf3 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -1801,6 +1801,12 @@
"group": "WM_ERROR",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
+ "-504637678": {
+ "message": "Starting animation on dim layer %s, requested by %s, alpha: %f -> %f, blur: %d -> %d",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_DIMMER",
+ "at": "com\/android\/server\/wm\/SmoothDimmer.java"
+ },
"-503656156": {
"message": "Update process config of %s to new config %s",
"level": "VERBOSE",
@@ -3739,6 +3745,12 @@
"group": "WM_DEBUG_CONFIGURATION",
"at": "com\/android\/server\/wm\/ActivityClientController.java"
},
+ "1309365288": {
+ "message": "Removing dim surface %s on transaction %s",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_DIMMER",
+ "at": "com\/android\/server\/wm\/SmoothDimmer.java"
+ },
"1316533291": {
"message": "State movement: %s from:%s to:%s reason:%s",
"level": "VERBOSE",
@@ -4003,6 +4015,12 @@
"group": "WM_DEBUG_STATES",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
+ "1620751818": {
+ "message": "Dim %s skipping animation and directly setting alpha=%f, blur=%d",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_DIMMER",
+ "at": "com\/android\/server\/wm\/SmoothDimmer.java"
+ },
"1621562070": {
"message": " startWCT=%s",
"level": "VERBOSE",
@@ -4560,6 +4578,9 @@
"WM_DEBUG_CONTENT_RECORDING": {
"tag": "WindowManager"
},
+ "WM_DEBUG_DIMMER": {
+ "tag": "WindowManager"
+ },
"WM_DEBUG_DRAW": {
"tag": "WindowManager"
},
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/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..9fde0fd 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;
@@ -1606,7 +1609,7 @@
/**
* Returns the color of the shadow layer.
*
- * @return the shadow layer's color encoded as a {@link ColorLong}.
+ * @return the shadow layer's color encoded as a {@code ColorLong}.
* @see #setShadowLayer(float,float,float,int)
* @see #setShadowLayer(float,float,float,long)
* @see Color
@@ -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/text/LineBreaker.java b/graphics/java/android/graphics/text/LineBreaker.java
index dc2e794..0e3fb16 100644
--- a/graphics/java/android/graphics/text/LineBreaker.java
+++ b/graphics/java/android/graphics/text/LineBreaker.java
@@ -249,7 +249,7 @@
* @param useBoundsForWidth True for using bounding box, false for advances.
* @return this builder instance
* @see Layout#getUseBoundsForWidth()
- * @see StaticLayout.Builder#setUseBoundsForWidth(boolean)
+ * @see android.text.StaticLayout.Builder#setUseBoundsForWidth(boolean)
*/
@FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH)
public @NonNull Builder setUseBoundsForWidth(boolean useBoundsForWidth) {
diff --git a/graphics/java/android/view/PixelCopy.java b/graphics/java/android/view/PixelCopy.java
index 0e198d5..e6de597 100644
--- a/graphics/java/android/view/PixelCopy.java
+++ b/graphics/java/android/view/PixelCopy.java
@@ -322,7 +322,7 @@
}
/**
- * Returns the {@link CopyResultStatus} of the copy request.
+ * Returns the status of the copy request.
*/
public @CopyResultStatus int getStatus() {
return mStatus;
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 31c2eb2..b7ea04f 100644
--- a/keystore/java/android/security/AndroidKeyStoreMaintenance.java
+++ b/keystore/java/android/security/AndroidKeyStoreMaintenance.java
@@ -128,25 +128,6 @@
}
/**
- * Queries user state from Keystore 2.0.
- *
- * @param userId - Android user id of the user.
- * @return UserState enum variant as integer if successful or an error
- */
- public static int getState(int userId) {
- StrictMode.noteDiskRead();
- try {
- return getService().getState(userId);
- } catch (ServiceSpecificException e) {
- Log.e(TAG, "getState failed", e);
- return e.errorCode;
- } catch (Exception e) {
- Log.e(TAG, "Can not connect to keystore", e);
- return SYSTEM_ERROR;
- }
- }
-
- /**
* Informs Keystore 2.0 that an off body event was detected.
*/
public static void onDeviceOffBody() {
diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java
index 8045f55..11b8271 100644
--- a/keystore/java/android/security/KeyStore.java
+++ b/keystore/java/android/security/KeyStore.java
@@ -19,8 +19,6 @@
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
import android.os.StrictMode;
-import android.os.UserHandle;
-import android.security.maintenance.UserState;
/**
* @hide This should not be made public in its present form because it
@@ -37,15 +35,6 @@
// Used for UID field to indicate the calling UID.
public static final int UID_SELF = -1;
- // States
- public enum State {
- @UnsupportedAppUsage
- UNLOCKED,
- @UnsupportedAppUsage
- LOCKED,
- UNINITIALIZED
- };
-
private static final KeyStore KEY_STORE = new KeyStore();
@UnsupportedAppUsage
@@ -55,28 +44,6 @@
/** @hide */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- public State state(int userId) {
- int userState = AndroidKeyStoreMaintenance.getState(userId);
- switch (userState) {
- case UserState.UNINITIALIZED:
- return KeyStore.State.UNINITIALIZED;
- case UserState.LSKF_UNLOCKED:
- return KeyStore.State.UNLOCKED;
- case UserState.LSKF_LOCKED:
- return KeyStore.State.LOCKED;
- default:
- throw new AssertionError(userState);
- }
- }
-
- /** @hide */
- @UnsupportedAppUsage
- public State state() {
- return state(UserHandle.myUserId());
- }
-
- /** @hide */
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public byte[] get(String key) {
return null;
}
diff --git a/keystore/java/android/security/KeyStoreException.java b/keystore/java/android/security/KeyStoreException.java
index 253d704..5825fac 100644
--- a/keystore/java/android/security/KeyStoreException.java
+++ b/keystore/java/android/security/KeyStoreException.java
@@ -319,7 +319,7 @@
/**
* Returns one of the error codes exported by the class.
*
- * @return a public error code, one of the values in {@link PublicErrorCode}.
+ * @return a public error code
*/
@PublicErrorCode
public int getNumericErrorCode() {
diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
index 96c257b..1ba41b1 100644
--- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java
+++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
@@ -75,16 +75,18 @@
* {@link java.security.interfaces.ECPublicKey} or {@link java.security.interfaces.RSAPublicKey}
* interfaces.
*
- * <p>For asymmetric key pairs, a self-signed X.509 certificate will be also generated and stored in
- * the Android Keystore. This is because the {@link java.security.KeyStore} abstraction does not
- * support storing key pairs without a certificate. The subject, serial number, and validity dates
- * of the certificate can be customized in this spec. The self-signed certificate may be replaced at
- * a later time by a certificate signed by a Certificate Authority (CA).
+ * <p>For asymmetric key pairs, a X.509 certificate will be also generated and stored in the Android
+ * Keystore. This is because the {@link java.security.KeyStore} abstraction does not support storing
+ * key pairs without a certificate. The subject, serial number, and validity dates of the
+ * certificate can be customized in this spec. The certificate may be replaced at a later time by a
+ * certificate signed by a Certificate Authority (CA).
*
- * <p>NOTE: If a private key is not authorized to sign the self-signed certificate, then the
- * certificate will be created with an invalid signature which will not verify. Such a certificate
- * is still useful because it provides access to the public key. To generate a valid signature for
- * the certificate the key needs to be authorized for all of the following:
+ * <p>NOTE: If attestation is not requested using {@link Builder#setAttestationChallenge(byte[])},
+ * generated certificate may be self-signed. If a private key is not authorized to sign the
+ * certificate, then the certificate will be created with an invalid signature which will not
+ * verify. Such a certificate is still useful because it provides access to the public key. To
+ * generate a valid signature for the certificate the key needs to be authorized for all of the
+ * following:
* <ul>
* <li>{@link KeyProperties#PURPOSE_SIGN},</li>
* <li>operation without requiring the user to be authenticated (see
@@ -989,12 +991,6 @@
* @param purposes set of purposes (e.g., encrypt, decrypt, sign) for which the key can be
* used. Attempts to use the key for any other purpose will be rejected.
*
- * <p>If the set of purposes for which the key can be used does not contain
- * {@link KeyProperties#PURPOSE_SIGN}, the self-signed certificate generated by
- * {@link KeyPairGenerator} of {@code AndroidKeyStore} provider will contain an
- * invalid signature. This is OK if the certificate is only used for obtaining the
- * public key from Android KeyStore.
- *
* <p>See {@link KeyProperties}.{@code PURPOSE} flags.
*/
public Builder(@NonNull String keystoreAlias, @KeyProperties.PurposeEnum int purposes) {
@@ -1140,7 +1136,7 @@
}
/**
- * Sets the subject used for the self-signed certificate of the generated key pair.
+ * Sets the subject used for the certificate of the generated key pair.
*
* <p>By default, the subject is {@code CN=fake}.
*/
@@ -1154,7 +1150,7 @@
}
/**
- * Sets the serial number used for the self-signed certificate of the generated key pair.
+ * Sets the serial number used for the certificate of the generated key pair.
*
* <p>By default, the serial number is {@code 1}.
*/
@@ -1168,8 +1164,7 @@
}
/**
- * Sets the start of the validity period for the self-signed certificate of the generated
- * key pair.
+ * Sets the start of the validity period for the certificate of the generated key pair.
*
* <p>By default, this date is {@code Jan 1 1970}.
*/
@@ -1183,8 +1178,7 @@
}
/**
- * Sets the end of the validity period for the self-signed certificate of the generated key
- * pair.
+ * Sets the end of the validity period for the certificate of the generated key pair.
*
* <p>By default, this date is {@code Jan 1 2048}.
*/
diff --git a/packages/SystemUI/ktfmt_includes.txt b/ktfmt_includes.txt
similarity index 99%
rename from packages/SystemUI/ktfmt_includes.txt
rename to ktfmt_includes.txt
index d3254b7..e4bf4c2 100644
--- a/packages/SystemUI/ktfmt_includes.txt
+++ b/ktfmt_includes.txt
@@ -1,3 +1,4 @@
++services/permission
+packages/SystemUI
-packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt
-packages/SystemUI/animation/src/com/android/systemui/animation/ViewHierarchyAnimator.kt
diff --git a/libs/WindowManager/Shell/aconfig/multitasking.aconfig b/libs/WindowManager/Shell/aconfig/multitasking.aconfig
index d55a41f..7c0d0e3 100644
--- a/libs/WindowManager/Shell/aconfig/multitasking.aconfig
+++ b/libs/WindowManager/Shell/aconfig/multitasking.aconfig
@@ -5,4 +5,18 @@
namespace: "multitasking"
description: "An Example Flag"
bug: "300136750"
-}
\ No newline at end of file
+}
+
+flag {
+ name: "enable_app_pairs"
+ namespace: "multitasking"
+ description: "Enables the ability to create and save app pairs to the Home screen"
+ bug: "274835596"
+}
+
+flag {
+ name: "desktop_windowing"
+ namespace: "multitasking"
+ description: "Enables desktop windowing"
+ bug: "304778354"
+}
diff --git a/libs/WindowManager/Shell/res/values/config.xml b/libs/WindowManager/Shell/res/values/config.xml
index 97a9d48..7436400 100644
--- a/libs/WindowManager/Shell/res/values/config.xml
+++ b/libs/WindowManager/Shell/res/values/config.xml
@@ -58,6 +58,9 @@
if a custom action is present before closing it. -->
<integer name="config_pipForceCloseDelay">1000</integer>
+ <!-- Allow PIP to resize via pinch gesture. -->
+ <bool name="config_pipEnablePinchResize">true</bool>
+
<!-- Animation duration when using long press on recents to dock -->
<integer name="long_press_dock_anim_duration">250</integer>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/RootDisplayAreaOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/RootDisplayAreaOrganizer.java
index 34bf9e0..2e5448a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/RootDisplayAreaOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/RootDisplayAreaOrganizer.java
@@ -18,6 +18,7 @@
import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE;
+import android.annotation.SuppressLint;
import android.app.WindowConfiguration;
import android.util.SparseArray;
import android.view.SurfaceControl;
@@ -29,6 +30,7 @@
import androidx.annotation.NonNull;
import com.android.internal.protolog.common.ProtoLog;
+import com.android.wm.shell.sysui.ShellInit;
import java.io.PrintWriter;
import java.util.List;
@@ -44,9 +46,14 @@
/** Display area leashes, which is mapped by display IDs. */
private final SparseArray<SurfaceControl> mLeashes = new SparseArray<>();
- public RootDisplayAreaOrganizer(Executor executor) {
+ public RootDisplayAreaOrganizer(@NonNull Executor executor, @NonNull ShellInit shellInit) {
super(executor);
- List<DisplayAreaAppearedInfo> infos = registerOrganizer(FEATURE_ROOT);
+ shellInit.addInitCallback(this::onInit, this);
+ }
+
+ @SuppressLint("MissingPermission") // Only called by SysUI.
+ private void onInit() {
+ final List<DisplayAreaAppearedInfo> infos = registerOrganizer(FEATURE_ROOT);
for (int i = infos.size() - 1; i >= 0; --i) {
onDisplayAreaAppeared(infos.get(i).getDisplayAreaInfo(), infos.get(i).getLeash());
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/RootTaskDisplayAreaOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/RootTaskDisplayAreaOrganizer.java
index 38550b4..ab61a48 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/RootTaskDisplayAreaOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/RootTaskDisplayAreaOrganizer.java
@@ -18,6 +18,7 @@
import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TASK_ORG;
+import android.annotation.SuppressLint;
import android.annotation.UiContext;
import android.app.ResourcesManager;
import android.content.Context;
@@ -38,6 +39,7 @@
import androidx.annotation.Nullable;
import com.android.internal.protolog.common.ProtoLog;
+import com.android.wm.shell.sysui.ShellInit;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -69,10 +71,17 @@
private final Context mContext;
- public RootTaskDisplayAreaOrganizer(Executor executor, Context context) {
+ public RootTaskDisplayAreaOrganizer(@NonNull Executor executor, @NonNull Context context,
+ @NonNull ShellInit shellInit) {
super(executor);
mContext = context;
- List<DisplayAreaAppearedInfo> infos = registerOrganizer(FEATURE_DEFAULT_TASK_CONTAINER);
+ shellInit.addInitCallback(this::onInit, this);
+ }
+
+ @SuppressLint("MissingPermission") // Only called by SysUI.
+ private void onInit() {
+ final List<DisplayAreaAppearedInfo> infos =
+ registerOrganizer(FEATURE_DEFAULT_TASK_CONTAINER);
for (int i = infos.size() - 1; i >= 0; --i) {
onDisplayAreaAppeared(infos.get(i).getDisplayAreaInfo(), infos.get(i).getLeash());
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingController.java
index 06ce371..8cf869b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingController.java
@@ -87,33 +87,28 @@
mTransitions.addHandler(this);
}
- @Override
- public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
- @NonNull SurfaceControl.Transaction startTransaction,
- @NonNull SurfaceControl.Transaction finishTransaction,
- @NonNull Transitions.TransitionFinishCallback finishCallback) {
- boolean containsEmbeddingSplit = false;
- boolean containsNonEmbeddedChange = false;
- final List<TransitionInfo.Change> changes = info.getChanges();
- for (int i = changes.size() - 1; i >= 0; i--) {
- final TransitionInfo.Change change = changes.get(i);
- if (!change.hasFlags(FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY)) {
- containsNonEmbeddedChange = true;
- } else if (!change.hasFlags(FLAG_FILLS_TASK)) {
+ /** Whether ActivityEmbeddingController should animate this transition. */
+ public boolean shouldAnimate(@NonNull TransitionInfo info) {
+ boolean containsEmbeddingChange = false;
+ for (TransitionInfo.Change change : info.getChanges()) {
+ if (!change.hasFlags(FLAG_FILLS_TASK) && change.hasFlags(
+ FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY)) {
// Whether the Task contains any ActivityEmbedding split before or after the
// transition.
- containsEmbeddingSplit = true;
+ containsEmbeddingChange = true;
}
}
- if (!containsEmbeddingSplit) {
+ if (!containsEmbeddingChange) {
// Let the system to play the default animation if there is no ActivityEmbedding split
// window. This allows to play the app customized animation when there is no embedding,
// such as the device is in a folded state.
return false;
}
- if (containsNonEmbeddedChange && !handleNonEmbeddedChanges(changes)) {
+
+ if (containsNonEmbeddedChange(info) && !handleNonEmbeddedChanges(info.getChanges())) {
return false;
}
+
final TransitionInfo.AnimationOptions options = info.getAnimationOptions();
if (options != null
// Scene-transition will be handled by app side.
@@ -123,6 +118,17 @@
return false;
}
+ return true;
+ }
+
+ @Override
+ public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
+ @NonNull SurfaceControl.Transaction startTransaction,
+ @NonNull SurfaceControl.Transaction finishTransaction,
+ @NonNull Transitions.TransitionFinishCallback finishCallback) {
+
+ if (!shouldAnimate(info)) return false;
+
// Start ActivityEmbedding animation.
mTransitionCallbacks.put(transition, finishCallback);
mAnimationRunner.startAnimation(transition, info, startTransaction, finishTransaction);
@@ -136,6 +142,16 @@
mAnimationRunner.cancelAnimationFromMerge();
}
+ /** Whether TransitionInfo contains non-ActivityEmbedding embedded window. */
+ private boolean containsNonEmbeddedChange(@NonNull TransitionInfo info) {
+ for (TransitionInfo.Change change : info.getChanges()) {
+ if (!change.hasFlags(FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
private boolean handleNonEmbeddedChanges(List<TransitionInfo.Change> changes) {
final Rect nonClosingEmbeddedArea = new Rect();
for (int i = changes.size() - 1; i >= 0; i--) {
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/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 5dfba5e..a533ca5 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
@@ -32,6 +32,7 @@
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.WindowManagerShellWrapper;
+import com.android.wm.shell.activityembedding.ActivityEmbeddingController;
import com.android.wm.shell.bubbles.BubbleController;
import com.android.wm.shell.bubbles.BubbleData;
import com.android.wm.shell.bubbles.BubbleDataRepository;
@@ -203,6 +204,7 @@
ShellTaskOrganizer taskOrganizer,
DisplayController displayController,
ShellController shellController,
+ DisplayInsetsController displayInsetsController,
SyncTransactionQueue syncQueue,
Transitions transitions,
Optional<DesktopTasksController> desktopTasksController,
@@ -218,6 +220,7 @@
taskOrganizer,
displayController,
shellController,
+ displayInsetsController,
syncQueue,
transitions,
desktopTasksController,
@@ -364,11 +367,12 @@
KeyguardTransitionHandler keyguardTransitionHandler,
Optional<DesktopTasksController> desktopTasksController,
Optional<UnfoldTransitionHandler> unfoldHandler,
+ Optional<ActivityEmbeddingController> activityEmbeddingController,
Transitions transitions) {
return new DefaultMixedHandler(shellInit, transitions, splitScreenOptional,
pipTransitionController, recentsTransitionHandler,
keyguardTransitionHandler, desktopTasksController,
- unfoldHandler);
+ unfoldHandler, activityEmbeddingController);
}
@WMSingleton
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/recents/RecentsTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
index d31476c..d277eef 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
@@ -925,19 +925,8 @@
if (toHome) wct.reorder(mRecentsTask, true /* toTop */);
else wct.restoreTransientOrder(mRecentsTask);
}
- if (!toHome
- // If a recents gesture starts on the 3p launcher, then the 3p launcher is the
- // live tile (pausing app). If the gesture is "cancelled" we need to return to
- // 3p launcher instead of "task-switching" away from it.
- && (!mWillFinishToHome || mPausingSeparateHome)
- && mPausingTasks != null && mState == STATE_NORMAL) {
- if (mPausingSeparateHome) {
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
- " returning to 3p home");
- } else {
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
- " returning to app");
- }
+ if (!toHome && !mWillFinishToHome && mPausingTasks != null && mState == STATE_NORMAL) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, " returning to app");
// The gesture is returning to the pausing-task(s) rather than continuing with
// recents, so end the transition by moving the app back to the top (and also
// re-showing it's task).
@@ -969,6 +958,15 @@
wct.restoreTransientOrder(mRecentsTask);
}
} else {
+ if (mPausingSeparateHome) {
+ if (mOpeningTasks.isEmpty()) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
+ " recents occluded 3p home");
+ } else {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
+ " switch task by recents on 3p home");
+ }
+ }
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, " normal finish");
// The general case: committing to recents, going home, or switching tasks.
for (int i = 0; i < mOpeningTasks.size(); ++i) {
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..7a4834c 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
@@ -23,6 +23,7 @@
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.content.Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT;
import static android.content.res.Configuration.SMALLEST_SCREEN_WIDTH_DP_UNDEFINED;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.RemoteAnimationTarget.MODE_OPENING;
@@ -143,6 +144,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;
@@ -393,6 +396,13 @@
return mMainStage.isActive();
}
+ /** @return whether this transition-request has the launch-adjacent flag. */
+ public boolean requestHasLaunchAdjacentFlag(TransitionRequestInfo request) {
+ final ActivityManager.RunningTaskInfo triggerTask = request.getTriggerTask();
+ return triggerTask != null && triggerTask.baseIntent != null
+ && (triggerTask.baseIntent.getFlags() & FLAG_ACTIVITY_LAUNCH_ADJACENT) != 0;
+ }
+
/** @return whether the transition-request implies entering pip from split. */
public boolean requestImpliesSplitToPip(TransitionRequestInfo request) {
if (!isSplitActive() || !mMixedHandler.requestHasPipEnter(request)) {
@@ -2240,6 +2250,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 =
@@ -2438,10 +2467,20 @@
EXIT_REASON_SCREEN_LOCKED_SHOW_ON_TOP);
}
- // When split in the background, it should be only opening/dismissing transition and
- // would keep out not empty. Prevent intercepting all transitions for split screen when
- // it is in the background and not identify to handle it.
- return (!out.isEmpty() || isSplitScreenVisible()) ? out : null;
+ if (!out.isEmpty()) {
+ // One of the cases above handled it
+ return out;
+ } else if (isSplitScreenVisible()) {
+ // If split is visible, only defer handling this transition if it's launching
+ // adjacent while there is already a split pair -- this may trigger PIP and
+ // that should be handled by the mixed handler.
+ final boolean deferTransition = requestHasLaunchAdjacentFlag(request)
+ && mMainStage.getChildCount() != 0 && mSideStage.getChildCount() != 0;
+ return !deferTransition ? out : null;
+ }
+ // Don't intercept the transition if we are not handling it as a part of one of the
+ // cases above and it is not already visible
+ return null;
} else {
if (isOpening && getStageOfTask(triggerTask) != null) {
// One task is appearing into split, prepare to enter split screen.
@@ -2477,7 +2516,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 +2739,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 +2778,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 +2807,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 +2833,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 +2949,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 +3168,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 e828eed..918a5a4 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
@@ -21,7 +21,9 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.TRANSIT_CHANGE;
+import static android.view.WindowManager.TRANSIT_PIP;
import static android.view.WindowManager.TRANSIT_TO_BACK;
+import static android.window.TransitionInfo.FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY;
import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
import static com.android.wm.shell.common.split.SplitScreenConstants.FLAG_IS_DIVIDER_BAR;
@@ -43,6 +45,7 @@
import android.window.WindowContainerTransaction;
import com.android.internal.protolog.common.ProtoLog;
+import com.android.wm.shell.activityembedding.ActivityEmbeddingController;
import com.android.wm.shell.common.split.SplitScreenUtils;
import com.android.wm.shell.desktopmode.DesktopModeStatus;
import com.android.wm.shell.desktopmode.DesktopTasksController;
@@ -50,6 +53,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;
@@ -73,6 +77,7 @@
private final KeyguardTransitionHandler mKeyguardHandler;
private DesktopTasksController mDesktopTasksController;
private UnfoldTransitionHandler mUnfoldHandler;
+ private ActivityEmbeddingController mActivityEmbeddingController;
private static class MixedTransition {
static final int TYPE_ENTER_PIP_FROM_SPLIT = 1;
@@ -92,9 +97,12 @@
/** Recents Transition while in desktop mode. */
static final int TYPE_RECENTS_DURING_DESKTOP = 6;
- /** Fuld/Unfold transition. */
+ /** Fold/Unfold transition. */
static final int TYPE_UNFOLD = 7;
+ /** Enter pip from one of the Activity Embedding windows. */
+ static final int TYPE_ENTER_PIP_FROM_ACTIVITY_EMBEDDING = 8;
+
/** The default animation for this mixed transition. */
static final int ANIM_TYPE_DEFAULT = 0;
@@ -149,7 +157,8 @@
Optional<RecentsTransitionHandler> recentsHandlerOptional,
KeyguardTransitionHandler keyguardHandler,
Optional<DesktopTasksController> desktopTasksControllerOptional,
- Optional<UnfoldTransitionHandler> unfoldHandler) {
+ Optional<UnfoldTransitionHandler> unfoldHandler,
+ Optional<ActivityEmbeddingController> activityEmbeddingController) {
mPlayer = player;
mKeyguardHandler = keyguardHandler;
if (Transitions.ENABLE_SHELL_TRANSITIONS
@@ -169,6 +178,7 @@
}
mDesktopTasksController = desktopTasksControllerOptional.orElse(null);
mUnfoldHandler = unfoldHandler.orElse(null);
+ mActivityEmbeddingController = activityEmbeddingController.orElse(null);
}, this);
}
}
@@ -191,6 +201,16 @@
mPipHandler.augmentRequest(transition, request, out);
mSplitHandler.addEnterOrExitIfNeeded(request, out);
return out;
+ } else if (request.getType() == TRANSIT_PIP
+ && (request.getFlags() & FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY) != 0 && (
+ mActivityEmbeddingController != null)) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
+ " Got a PiP-enter request from an Activity Embedding split");
+ mActiveTransitions.add(new MixedTransition(
+ MixedTransition.TYPE_ENTER_PIP_FROM_ACTIVITY_EMBEDDING, transition));
+ // Postpone transition splitting to later.
+ WindowContainerTransaction out = new WindowContainerTransaction();
+ return out;
} else if (request.getRemoteTransition() != null
&& TransitionUtil.isOpeningType(request.getType())
&& (request.getTriggerTask() == null
@@ -344,6 +364,8 @@
// Keyguard handler cannot handle it, process through original mixed
mActiveTransitions.remove(keyguardMixed);
}
+ } else if (mPipHandler != null) {
+ mPipHandler.syncPipSurfaceState(info, startTransaction, finishTransaction);
}
}
@@ -352,6 +374,9 @@
if (mixed.mType == MixedTransition.TYPE_ENTER_PIP_FROM_SPLIT) {
return animateEnterPipFromSplit(mixed, info, startTransaction, finishTransaction,
finishCallback);
+ } else if (mixed.mType == MixedTransition.TYPE_ENTER_PIP_FROM_ACTIVITY_EMBEDDING) {
+ return animateEnterPipFromActivityEmbedding(mixed, info, startTransaction,
+ finishTransaction, finishCallback);
} else if (mixed.mType == MixedTransition.TYPE_DISPLAY_AND_SPLIT_CHANGE) {
return false;
} else if (mixed.mType == MixedTransition.TYPE_OPTIONS_REMOTE_AND_PIP_CHANGE) {
@@ -397,6 +422,58 @@
}
}
+ private boolean animateEnterPipFromActivityEmbedding(@NonNull MixedTransition mixed,
+ @NonNull TransitionInfo info,
+ @NonNull SurfaceControl.Transaction startTransaction,
+ @NonNull SurfaceControl.Transaction finishTransaction,
+ @NonNull Transitions.TransitionFinishCallback finishCallback) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Animating a mixed transition for "
+ + "entering PIP from an Activity Embedding window");
+ // Split into two transitions (wct)
+ TransitionInfo.Change pipChange = null;
+ final TransitionInfo everythingElse = subCopy(info, TRANSIT_TO_BACK, true /* changes */);
+ for (int i = info.getChanges().size() - 1; i >= 0; --i) {
+ TransitionInfo.Change change = info.getChanges().get(i);
+ if (mPipHandler.isEnteringPip(change, info.getType())) {
+ if (pipChange != null) {
+ throw new IllegalStateException("More than 1 pip-entering changes in one"
+ + " transition? " + info);
+ }
+ pipChange = change;
+ // going backwards, so remove-by-index is fine.
+ everythingElse.getChanges().remove(i);
+ }
+ }
+
+ final Transitions.TransitionFinishCallback finishCB = (wct) -> {
+ --mixed.mInFlightSubAnimations;
+ mixed.joinFinishArgs(wct);
+ if (mixed.mInFlightSubAnimations > 0) return;
+ mActiveTransitions.remove(mixed);
+ finishCallback.onTransitionFinished(mixed.mFinishWCT);
+ };
+
+ if (!mActivityEmbeddingController.shouldAnimate(everythingElse)) {
+ // Fallback to dispatching to other handlers.
+ return false;
+ }
+
+ // PIP window should always be on the highest Z order.
+ if (pipChange != null) {
+ mixed.mInFlightSubAnimations = 2;
+ mPipHandler.startEnterAnimation(
+ pipChange, startTransaction.setLayer(pipChange.getLeash(), Integer.MAX_VALUE),
+ finishTransaction,
+ finishCB);
+ } else {
+ mixed.mInFlightSubAnimations = 1;
+ }
+
+ mActivityEmbeddingController.startAnimation(mixed.mTransition, everythingElse,
+ startTransaction, finishTransaction, finishCB);
+ return true;
+ }
+
private boolean animateOpenIntentWithRemoteAndPip(@NonNull MixedTransition mixed,
@NonNull TransitionInfo info,
@NonNull SurfaceControl.Transaction startTransaction,
@@ -511,8 +588,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);
@@ -790,6 +885,10 @@
} else {
mPipHandler.end();
}
+ } else if (mixed.mType == MixedTransition.TYPE_ENTER_PIP_FROM_ACTIVITY_EMBEDDING) {
+ mPipHandler.end();
+ mActivityEmbeddingController.mergeAnimation(transition, info, t, mergeTarget,
+ finishCallback);
} else if (mixed.mType == MixedTransition.TYPE_OPTIONS_REMOTE_AND_PIP_CHANGE) {
mPipHandler.end();
if (mixed.mLeftoversHandler != null) {
@@ -830,6 +929,9 @@
if (mixed == null) return;
if (mixed.mType == MixedTransition.TYPE_ENTER_PIP_FROM_SPLIT) {
mPipHandler.onTransitionConsumed(transition, aborted, finishT);
+ } else if (mixed.mType == MixedTransition.TYPE_ENTER_PIP_FROM_ACTIVITY_EMBEDDING) {
+ mPipHandler.onTransitionConsumed(transition, aborted, finishT);
+ mActivityEmbeddingController.onTransitionConsumed(transition, aborted, finishT);
} else if (mixed.mType == MixedTransition.TYPE_RECENTS_DURING_SPLIT) {
mixed.mLeftoversHandler.onTransitionConsumed(transition, aborted, finishT);
} else if (mixed.mType == MixedTransition.TYPE_OPTIONS_REMOTE_AND_PIP_CHANGE) {
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 335a588..0548a8e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
@@ -18,6 +18,7 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.view.WindowInsets.Type.statusBars;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.WindowConfiguration.WindowingMode;
@@ -30,6 +31,8 @@
import android.graphics.Rect;
import android.os.Binder;
import android.view.Display;
+import android.view.InsetsSource;
+import android.view.InsetsState;
import android.view.LayoutInflater;
import android.view.SurfaceControl;
import android.view.SurfaceControlViewHost;
@@ -119,6 +122,7 @@
private WindowlessWindowManager mCaptionWindowManager;
private SurfaceControlViewHost mViewHost;
private Configuration mWindowDecorConfig;
+ private boolean mIsCaptionVisible;
private final Binder mOwner = new Binder();
private final Rect mCaptionInsetsRect = new Rect();
@@ -225,6 +229,8 @@
.inflate(params.mLayoutResId, null);
}
+ updateCaptionVisibility(outResult.mRootView, mTaskInfo.displayId);
+
final Resources resources = mDecorWindowContext.getResources();
final Configuration taskConfig = mTaskInfo.getConfiguration();
final Rect taskBounds = taskConfig.windowConfiguration.getBounds();
@@ -272,12 +278,20 @@
// Caption insets
mCaptionInsetsRect.set(taskBounds);
- mCaptionInsetsRect.bottom = mCaptionInsetsRect.top + captionHeight + params.mCaptionY;
- wct.addInsetsSource(mTaskInfo.token,
- mOwner, 0 /* index */, WindowInsets.Type.captionBar(), mCaptionInsetsRect);
- wct.addInsetsSource(mTaskInfo.token,
- mOwner, 0 /* index */, WindowInsets.Type.mandatorySystemGestures(),
- mCaptionInsetsRect);
+ if (mIsCaptionVisible) {
+ mCaptionInsetsRect.bottom =
+ mCaptionInsetsRect.top + captionHeight + params.mCaptionY;
+ wct.addInsetsSource(mTaskInfo.token,
+ mOwner, 0 /* index */, WindowInsets.Type.captionBar(), mCaptionInsetsRect);
+ wct.addInsetsSource(mTaskInfo.token,
+ mOwner, 0 /* index */, WindowInsets.Type.mandatorySystemGestures(),
+ mCaptionInsetsRect);
+ } else {
+ wct.removeInsetsSource(mTaskInfo.token, mOwner, 0 /* index */,
+ WindowInsets.Type.captionBar());
+ wct.removeInsetsSource(mTaskInfo.token, mOwner, 0 /* index */,
+ WindowInsets.Type.mandatorySystemGestures());
+ }
} else {
startT.hide(mCaptionContainerSurface);
}
@@ -348,10 +362,41 @@
}
}
+ /**
+ * Checks if task has entered/exited immersive mode and requires a change in caption visibility.
+ */
+ private void updateCaptionVisibility(View rootView, int displayId) {
+ final InsetsState insetsState = mDisplayController.getInsetsState(displayId);
+ for (int i = 0; i < insetsState.sourceSize(); i++) {
+ final InsetsSource source = insetsState.sourceAt(i);
+ if (source.getType() != statusBars()) {
+ continue;
+ }
+
+ mIsCaptionVisible = source.isVisible();
+ setCaptionVisibility(rootView, mIsCaptionVisible);
+
+ return;
+ }
+ }
+
+ private void setCaptionVisibility(View rootView, boolean visible) {
+ if (rootView == null) {
+ return;
+ }
+ final int v = visible ? View.VISIBLE : View.GONE;
+ final View captionView = rootView.findViewById(getCaptionViewId());
+ captionView.setVisibility(v);
+ }
+
int getCaptionHeightId(@WindowingMode int windowingMode) {
return Resources.ID_NULL;
}
+ int getCaptionViewId() {
+ return Resources.ID_NULL;
+ }
+
/**
* Obtains the {@link Display} instance for the display ID in {@link #mTaskInfo} if it exists or
* registers {@link #mOnDisplaysChangedListener} if it doesn't.
@@ -466,7 +511,8 @@
*/
public void addCaptionInset(WindowContainerTransaction wct) {
final int captionHeightId = getCaptionHeightId(mTaskInfo.getWindowingMode());
- if (!ViewRootImpl.CAPTION_ON_SHELL || captionHeightId == Resources.ID_NULL) {
+ if (!ViewRootImpl.CAPTION_ON_SHELL || captionHeightId == Resources.ID_NULL
+ || !mIsCaptionVisible) {
return;
}
diff --git a/libs/WindowManager/Shell/tests/flicker/Android.bp b/libs/WindowManager/Shell/tests/flicker/Android.bp
index 0058d11..7f02072 100644
--- a/libs/WindowManager/Shell/tests/flicker/Android.bp
+++ b/libs/WindowManager/Shell/tests/flicker/Android.bp
@@ -116,6 +116,7 @@
"wm-flicker-common-assertions",
"launcher-helper-lib",
"launcher-aosp-tapl",
+ "com_android_wm_shell_flags_lib",
],
}
diff --git a/libs/WindowManager/Shell/tests/flicker/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 d09a90c..aadadd6 100644
--- a/libs/WindowManager/Shell/tests/unittest/Android.bp
+++ b/libs/WindowManager/Shell/tests/unittest/Android.bp
@@ -35,6 +35,7 @@
static_libs: [
"WindowManager-Shell",
"junit",
+ "flag-junit-base",
"androidx.test.runner",
"androidx.test.rules",
"androidx.test.ext.junit",
@@ -49,6 +50,7 @@
"testables",
"platform-test-annotations",
"servicestests-utils",
+ "com_android_wm_shell_flags_lib",
],
libs: [
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsStateTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsStateTest.java
index d34e27b..db98abb 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsStateTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsStateTest.java
@@ -24,6 +24,7 @@
import static org.mockito.Mockito.verify;
import android.content.ComponentName;
+import android.graphics.Point;
import android.graphics.Rect;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -60,6 +61,9 @@
/** The minimum possible size of the override min size's width or height */
private static final int OVERRIDABLE_MIN_SIZE = 40;
+ /** The margin of error for floating point results. */
+ private static final float MARGIN_OF_ERROR = 0.05f;
+
private PipBoundsState mPipBoundsState;
private SizeSpecSource mSizeSpecSource;
private ComponentName mTestComponentName1;
@@ -88,6 +92,27 @@
}
@Test
+ public void testBoundsScale() {
+ mPipBoundsState.setMaxSize(300, 300);
+ mPipBoundsState.setBounds(new Rect(0, 0, 100, 100));
+
+ final int currentWidth = mPipBoundsState.getBounds().width();
+ final Point maxSize = mPipBoundsState.getMaxSize();
+ final float expectedBoundsScale = Math.min((float) currentWidth / maxSize.x, 1.0f);
+
+ // test for currentWidth < maxWidth
+ assertEquals(expectedBoundsScale, mPipBoundsState.getBoundsScale(), MARGIN_OF_ERROR);
+
+ // reset the bounds to be at the maximum size spec
+ mPipBoundsState.setBounds(new Rect(0, 0, maxSize.x, maxSize.y));
+ assertEquals(1.0f, mPipBoundsState.getBoundsScale(), /* delta */ 0f);
+
+ // reset the bounds to be over the maximum size spec
+ mPipBoundsState.setBounds(new Rect(0, 0, maxSize.x * 2, maxSize.y * 2));
+ assertEquals(1.0f, mPipBoundsState.getBoundsScale(), /* delta */ 0f);
+ }
+
+ @Test
public void testSetReentryState() {
final Size size = new Size(100, 100);
final float snapFraction = 0.5f;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java
index 6777a5b..9719ba8 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java
@@ -28,11 +28,13 @@
import android.graphics.Rect;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import android.testing.TestableResources;
import android.view.MotionEvent;
import android.view.ViewConfiguration;
import androidx.test.filters.SmallTest;
+import com.android.wm.shell.R;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.common.FloatingContentCoordinator;
import com.android.wm.shell.common.ShellExecutor;
@@ -98,6 +100,9 @@
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
+ final TestableResources res = mContext.getOrCreateTestableResources();
+ res.addOverride(R.bool.config_pipEnablePinchResize, true);
+
mPipDisplayLayoutState = new PipDisplayLayoutState(mContext);
mSizeSpecSource = new PhoneSizeSpecSource(mContext, mPipDisplayLayoutState);
mPipBoundsState = new PipBoundsState(mContext, mSizeSpecSource, mPipDisplayLayoutState);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
index ebc284b..befc702 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
@@ -208,6 +208,30 @@
@Test
@UiThreadTest
+ public void testRemoteTransitionConsumed() {
+ // Omit side child change
+ TransitionInfo info = new TransitionInfoBuilder(TRANSIT_OPEN, 0)
+ .addChange(TRANSIT_OPEN, mMainChild)
+ .build();
+ TestRemoteTransition testRemote = new TestRemoteTransition();
+
+ IBinder transition = mSplitScreenTransitions.startEnterTransition(
+ TRANSIT_OPEN, new WindowContainerTransaction(),
+ new RemoteTransition(testRemote, "Test"), mStageCoordinator,
+ TRANSIT_SPLIT_SCREEN_PAIR_OPEN, false);
+ mMainStage.onTaskAppeared(mMainChild, createMockSurface());
+ boolean accepted = mStageCoordinator.startAnimation(transition, info,
+ mock(SurfaceControl.Transaction.class),
+ mock(SurfaceControl.Transaction.class),
+ mock(Transitions.TransitionFinishCallback.class));
+ assertTrue(accepted);
+
+ assertTrue(testRemote.isConsumed());
+
+ }
+
+ @Test
+ @UiThreadTest
public void testMonitorInSplit() {
enterSplit();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
index d598678..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 fcb7863..8061aa3 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
@@ -18,6 +18,9 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.view.WindowInsets.Type.captionBar;
+import static android.view.WindowInsets.Type.mandatorySystemGestures;
+import static android.view.WindowInsets.Type.statusBars;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
import static com.android.wm.shell.MockSurfaceControlHelper.createMockSurfaceControlBuilder;
@@ -25,6 +28,8 @@
import static com.google.common.truth.Truth.assertThat;
+import static junit.framework.Assert.assertTrue;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -51,6 +56,7 @@
import android.util.DisplayMetrics;
import android.view.AttachedSurfaceControl;
import android.view.Display;
+import android.view.InsetsState;
import android.view.SurfaceControl;
import android.view.SurfaceControlViewHost;
import android.view.View;
@@ -94,6 +100,7 @@
private static final Rect TASK_BOUNDS = new Rect(100, 300, 400, 400);
private static final Point TASK_POSITION_IN_PARENT = new Point(40, 60);
private static final int CORNER_RADIUS = 20;
+ private static final int STATUS_BAR_INSET_SOURCE_ID = 0;
private final WindowDecoration.RelayoutResult<TestView> mRelayoutResult =
new WindowDecoration.RelayoutResult<>();
@@ -118,6 +125,7 @@
private final List<SurfaceControl.Transaction> mMockSurfaceControlTransactions =
new ArrayList<>();
private final List<SurfaceControl.Builder> mMockSurfaceControlBuilders = new ArrayList<>();
+ private final InsetsState mInsetsState = new InsetsState();
private SurfaceControl.Transaction mMockSurfaceControlStartT;
private SurfaceControl.Transaction mMockSurfaceControlFinishT;
private SurfaceControl.Transaction mMockSurfaceControlAddWindowT;
@@ -141,6 +149,11 @@
.create(any(), any(), any());
when(mMockSurfaceControlViewHost.getRootSurfaceControl())
.thenReturn(mMockRootSurfaceControl);
+ when(mMockView.findViewById(anyInt())).thenReturn(mMockView);
+
+ // Add status bar inset so that WindowDecoration does not think task is in immersive mode
+ mInsetsState.getOrCreateSource(STATUS_BAR_INSET_SOURCE_ID, statusBars()).setVisible(true);
+ doReturn(mInsetsState).when(mMockDisplayController).getInsetsState(anyInt());
}
@Test
@@ -537,12 +550,41 @@
windowDecor.relayout(taskInfo);
- verify(mMockSurfaceControlStartT).setColor(taskSurface, new float[] {1.f, 1.f, 0.f});
+ verify(mMockSurfaceControlStartT).setColor(taskSurface, new float[]{1.f, 1.f, 0.f});
mockitoSession.finishMocking();
}
@Test
+ public void testInsetsAddedWhenCaptionIsVisible() {
+ final Display defaultDisplay = mock(Display.class);
+ doReturn(defaultDisplay).when(mMockDisplayController)
+ .getDisplay(Display.DEFAULT_DISPLAY);
+
+ final ActivityManager.TaskDescription.Builder taskDescriptionBuilder =
+ new ActivityManager.TaskDescription.Builder();
+ final ActivityManager.RunningTaskInfo taskInfo = new TestRunningTaskInfoBuilder()
+ .setDisplayId(Display.DEFAULT_DISPLAY)
+ .setTaskDescriptionBuilder(taskDescriptionBuilder)
+ .setVisible(true)
+ .build();
+ final SurfaceControl taskSurface = mock(SurfaceControl.class);
+ final TestWindowDecoration windowDecor = createWindowDecoration(taskInfo, taskSurface);
+
+ assertTrue(mInsetsState.getOrCreateSource(STATUS_BAR_INSET_SOURCE_ID, statusBars())
+ .isVisible());
+ assertTrue(mInsetsState.sourceSize() == 1);
+ assertTrue(mInsetsState.sourceAt(0).getType() == statusBars());
+
+ windowDecor.relayout(taskInfo);
+
+ verify(mMockWindowContainerTransaction).addInsetsSource(eq(taskInfo.token), any(),
+ eq(0) /* index */, eq(captionBar()), any());
+ verify(mMockWindowContainerTransaction).addInsetsSource(eq(taskInfo.token), any(),
+ eq(0) /* index */, eq(mandatorySystemGestures()), any());
+ }
+
+ @Test
public void testRelayout_fluidResizeEnabled_fullscreenTask_clearTaskSurfaceColor() {
StaticMockitoSession mockitoSession = mockitoSession().mockStatic(
DesktopModeStatus.class).strictness(LENIENT).startMocking();
@@ -581,6 +623,33 @@
mockitoSession.finishMocking();
}
+
+ @Test
+ public void testInsetsRemovedWhenCaptionIsHidden() {
+ final Display defaultDisplay = mock(Display.class);
+ doReturn(defaultDisplay).when(mMockDisplayController)
+ .getDisplay(Display.DEFAULT_DISPLAY);
+
+ mInsetsState.getOrCreateSource(STATUS_BAR_INSET_SOURCE_ID, captionBar()).setVisible(false);
+
+ final ActivityManager.TaskDescription.Builder taskDescriptionBuilder =
+ new ActivityManager.TaskDescription.Builder();
+ final ActivityManager.RunningTaskInfo taskInfo = new TestRunningTaskInfoBuilder()
+ .setDisplayId(Display.DEFAULT_DISPLAY)
+ .setTaskDescriptionBuilder(taskDescriptionBuilder)
+ .setVisible(true)
+ .build();
+ final SurfaceControl taskSurface = mock(SurfaceControl.class);
+ final TestWindowDecoration windowDecor = createWindowDecoration(taskInfo, taskSurface);
+
+ windowDecor.relayout(taskInfo);
+
+ verify(mMockWindowContainerTransaction).removeInsetsSource(eq(taskInfo.token), any(),
+ eq(0) /* index */, eq(captionBar()));
+ verify(mMockWindowContainerTransaction).removeInsetsSource(eq(taskInfo.token), any(),
+ eq(0) /* index */, eq(mandatorySystemGestures()));
+ }
+
private TestWindowDecoration createWindowDecoration(
ActivityManager.RunningTaskInfo taskInfo, SurfaceControl testSurface) {
return new TestWindowDecoration(mContext, mMockDisplayController, mMockShellTaskOrganizer,
diff --git a/libs/hwui/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/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/location/java/android/location/GnssSignalType.java b/location/java/android/location/GnssSignalType.java
index 16c3f2e..2dd67f0 100644
--- a/location/java/android/location/GnssSignalType.java
+++ b/location/java/android/location/GnssSignalType.java
@@ -34,8 +34,7 @@
/**
* Creates a {@link GnssSignalType} with a full list of parameters.
*
- * @param constellationType the constellation type as defined in
- * {@link GnssStatus.ConstellationType}
+ * @param constellationType the constellation type
* @param carrierFrequencyHz the carrier frequency in Hz
* @param codeType the code type as defined in {@link GnssMeasurement#getCodeType()}
*/
@@ -66,7 +65,7 @@
this.mCodeType = codeType;
}
- /** Returns the {@link GnssStatus.ConstellationType}. */
+ /** Returns the constellation type. */
@GnssStatus.ConstellationType
public int getConstellationType() {
return mConstellationType;
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/audiopolicy/AudioPolicy.java b/media/java/android/media/audiopolicy/AudioPolicy.java
index e9a6ed4..9ced2a4 100644
--- a/media/java/android/media/audiopolicy/AudioPolicy.java
+++ b/media/java/android/media/audiopolicy/AudioPolicy.java
@@ -16,6 +16,7 @@
package android.media.audiopolicy;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -48,6 +49,7 @@
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
+import com.android.media.audio.flags.Flags;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -417,6 +419,7 @@
* @return {@link AudioManager#SUCCESS} if the update was successful,
* {@link AudioManager#ERROR} otherwise.
*/
+ @FlaggedApi(Flags.FLAG_AUDIO_POLICY_UPDATE_MIXING_RULES_API)
@RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
public int updateMixingRules(
@NonNull List<Pair<AudioMix, AudioMixingRule>> mixingRuleUpdates) {
diff --git a/media/java/android/media/flags/media_better_together.aconfig b/media/java/android/media/flags/media_better_together.aconfig
index a354f91..c9cfa67 100644
--- a/media/java/android/media/flags/media_better_together.aconfig
+++ b/media/java/android/media/flags/media_better_together.aconfig
@@ -27,3 +27,10 @@
description: "Disables the broadcast receiver that prevents scanning when the screen is off."
bug: "304234628"
}
+
+flag {
+ namespace: "media_solutions"
+ name: "fallback_to_default_handling_when_media_session_has_fixed_volume_handling"
+ description: "Fallbacks to the default handling for volume adjustment when media session has fixed volume handling and its app is in the foreground and setting a media controller."
+ bug: "293743975"
+}
diff --git a/media/java/android/media/midi/MidiUmpDeviceService.java b/media/java/android/media/midi/MidiUmpDeviceService.java
index 6e2aaab..c54bfce 100644
--- a/media/java/android/media/midi/MidiUmpDeviceService.java
+++ b/media/java/android/media/midi/MidiUmpDeviceService.java
@@ -16,6 +16,9 @@
package android.media.midi;
+import static com.android.media.midi.flags.Flags.FLAG_VIRTUAL_UMP;
+
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.Service;
@@ -38,7 +41,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
@@ -54,9 +57,11 @@
* android:resource="@xml/device_info" />
* </service></pre>
*/
+@FlaggedApi(FLAG_VIRTUAL_UMP)
public abstract class MidiUmpDeviceService extends Service {
private static final String TAG = "MidiUmpDeviceService";
+ @FlaggedApi(FLAG_VIRTUAL_UMP)
public static final String SERVICE_INTERFACE = "android.media.midi.MidiUmpDeviceService";
private IMidiManager mMidiManager;
@@ -75,6 +80,7 @@
}
};
+ @FlaggedApi(FLAG_VIRTUAL_UMP)
@Override
public void onCreate() {
mMidiManager = IMidiManager.Stub.asInterface(
@@ -112,6 +118,7 @@
* The number of input and output ports must be equal and non-zero.
* @return list of MidiReceivers
*/
+ @FlaggedApi(FLAG_VIRTUAL_UMP)
public abstract @NonNull List<MidiReceiver> onGetInputPortReceivers();
/**
@@ -120,6 +127,7 @@
* The number of input and output ports must be equal and non-zero.
* @return the list of MidiReceivers
*/
+ @FlaggedApi(FLAG_VIRTUAL_UMP)
public final @NonNull List<MidiReceiver> getOutputPortReceivers() {
if (mServer == null) {
return new ArrayList<MidiReceiver>();
@@ -132,6 +140,7 @@
* Returns the {@link MidiDeviceInfo} instance for this service
* @return the MidiDeviceInfo of the virtual MIDI device if it was successfully created
*/
+ @FlaggedApi(FLAG_VIRTUAL_UMP)
public final @Nullable MidiDeviceInfo getDeviceInfo() {
return mDeviceInfo;
}
@@ -140,6 +149,7 @@
* Called to notify when the {@link MidiDeviceStatus} has changed
* @param status the current status of the MIDI device
*/
+ @FlaggedApi(FLAG_VIRTUAL_UMP)
public void onDeviceStatusChanged(@NonNull MidiDeviceStatus status) {
}
@@ -147,9 +157,11 @@
* Called to notify when the virtual MIDI device running in this service has been closed by
* all its clients
*/
+ @FlaggedApi(FLAG_VIRTUAL_UMP)
public void onClose() {
}
+ @FlaggedApi(FLAG_VIRTUAL_UMP)
@Override
public @Nullable IBinder onBind(@NonNull Intent intent) {
if (SERVICE_INTERFACE.equals(intent.getAction()) && mServer != null) {
diff --git a/media/java/android/media/projection/IMediaProjectionManager.aidl b/media/java/android/media/projection/IMediaProjectionManager.aidl
index d294601..31e65eb 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)")
+ oneway 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/AndroidManifest.xml b/media/tests/MediaFrameworkTest/AndroidManifest.xml
index e886558..7d79a6c 100644
--- a/media/tests/MediaFrameworkTest/AndroidManifest.xml
+++ b/media/tests/MediaFrameworkTest/AndroidManifest.xml
@@ -20,6 +20,7 @@
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
<uses-permission android:name="android.permission.CAMERA"/>
<uses-permission android:name="android.permission.INTERNET"/>
+ <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>
diff --git a/media/tests/MediaFrameworkTest/AndroidTest.xml b/media/tests/MediaFrameworkTest/AndroidTest.xml
index 132028c..91c92cc1 100644
--- a/media/tests/MediaFrameworkTest/AndroidTest.xml
+++ b/media/tests/MediaFrameworkTest/AndroidTest.xml
@@ -23,5 +23,6 @@
<option name="package" value="com.android.mediaframeworktest" />
<option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
<option name="hidden-api-checks" value="false"/>
+ <option name="isolated-storage" value="false"/>
</test>
</configuration>
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkUnitTestRunner.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkUnitTestRunner.java
index 9be7004..30edfa4 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkUnitTestRunner.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkUnitTestRunner.java
@@ -44,7 +44,6 @@
@Override
public TestSuite getAllTests() {
TestSuite suite = new InstrumentationTestSuite(this);
- addMediaMetadataRetrieverStateUnitTests(suite);
addMediaRecorderStateUnitTests(suite);
addMediaPlayerStateUnitTests(suite);
addMediaScannerUnitTests(suite);
@@ -70,11 +69,6 @@
}
// Running all unit tests checking the state machine may be time-consuming.
- private void addMediaMetadataRetrieverStateUnitTests(TestSuite suite) {
- suite.addTestSuite(MediaMetadataRetrieverTest.class);
- }
-
- // Running all unit tests checking the state machine may be time-consuming.
private void addMediaRecorderStateUnitTests(TestSuite suite) {
suite.addTestSuite(MediaRecorderPrepareStateUnitTest.class);
suite.addTestSuite(MediaRecorderResetStateUnitTest.class);
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaMetadataRetrieverTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaMetadataRetrieverTest.java
index bdca474..f70d2d1 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaMetadataRetrieverTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaMetadataRetrieverTest.java
@@ -16,26 +16,34 @@
package com.android.mediaframeworktest.unit;
+import static org.junit.Assert.assertTrue;
+
import android.graphics.Bitmap;
import android.media.MediaMetadataRetriever;
-import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.LargeTest;
import android.test.suitebuilder.annotation.MediumTest;
import android.util.Log;
+import androidx.test.runner.AndroidJUnit4;
+
import com.android.mediaframeworktest.MediaNames;
import com.android.mediaframeworktest.MediaProfileReader;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
import java.io.FileOutputStream;
import java.io.IOException;
-public class MediaMetadataRetrieverTest extends AndroidTestCase {
+@RunWith(AndroidJUnit4.class)
+public class MediaMetadataRetrieverTest {
private static final String TAG = "MediaMetadataRetrieverTest";
// Test album art extraction.
@MediumTest
- public static void testGetEmbeddedPicture() throws Exception {
+ @Test
+ public void testGetEmbeddedPicture() throws Exception {
Log.v(TAG, "testGetEmbeddedPicture starts.");
MediaMetadataRetriever retriever = new MediaMetadataRetriever();
boolean supportWMA = MediaProfileReader.getWMAEnable();
@@ -78,7 +86,8 @@
// Test frame capture
@LargeTest
- public static void testThumbnailCapture() throws Exception {
+ @Test
+ public void testThumbnailCapture() throws Exception {
MediaMetadataRetriever retriever = new MediaMetadataRetriever();
boolean supportWMA = MediaProfileReader.getWMAEnable();
boolean supportWMV = MediaProfileReader.getWMVEnable();
@@ -134,7 +143,8 @@
}
@LargeTest
- public static void testMetadataRetrieval() throws Exception {
+ @Test
+ public void testMetadataRetrieval() throws Exception {
boolean supportWMA = MediaProfileReader.getWMAEnable();
boolean supportWMV = MediaProfileReader.getWMVEnable();
boolean hasFailed = false;
@@ -169,7 +179,8 @@
// If the specified call order and valid media file is used, no exception
// should be thrown.
@MediumTest
- public static void testBasicNormalMethodCallSequence() throws Exception {
+ @Test
+ public void testBasicNormalMethodCallSequence() throws Exception {
boolean hasFailed = false;
MediaMetadataRetriever retriever = new MediaMetadataRetriever();
try {
@@ -197,7 +208,8 @@
// If setDataSource() has not been called, both getFrameAtTime() and extractMetadata() must
// return null.
@MediumTest
- public static void testBasicAbnormalMethodCallSequence() {
+ @Test
+ public void testBasicAbnormalMethodCallSequence() {
boolean hasFailed = false;
MediaMetadataRetriever retriever = new MediaMetadataRetriever();
if (retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_ALBUM) != null) {
@@ -213,7 +225,8 @@
// Test setDataSource()
@MediumTest
- public static void testSetDataSource() throws IOException {
+ @Test
+ public void testSetDataSource() throws IOException {
MediaMetadataRetriever retriever = new MediaMetadataRetriever();
boolean hasFailed = false;
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 473d7b6f..477e61d 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
@@ -61,14 +61,20 @@
import androidx.credentials.provider.PublicKeyCredentialEntry
import androidx.credentials.provider.RemoteEntry
import org.json.JSONObject
+import android.credentials.flags.Flags
import java.time.Instant
+
fun getAppLabel(
pm: PackageManager,
appPackageName: String
): String? {
return try {
- val pkgInfo = getPackageInfo(pm, appPackageName)
+ val pkgInfo = if (Flags.instantAppsEnabled()) {
+ getPackageInfo(pm, appPackageName)
+ } else {
+ pm.getPackageInfo(appPackageName, PackageManager.PackageInfoFlags.of(0))
+ }
val applicationInfo = checkNotNull(pkgInfo.applicationInfo)
applicationInfo.loadSafeLabel(
pm, 0f,
@@ -91,7 +97,14 @@
// Test data has only package name not component name.
// For test data usage only.
try {
- val pkgInfo = getPackageInfo(pm, providerFlattenedComponentName)
+ val pkgInfo = if (Flags.instantAppsEnabled()) {
+ getPackageInfo(pm, providerFlattenedComponentName)
+ } else {
+ pm.getPackageInfo(
+ providerFlattenedComponentName,
+ PackageManager.PackageInfoFlags.of(0)
+ )
+ }
val applicationInfo = checkNotNull(pkgInfo.applicationInfo)
providerLabel =
applicationInfo.loadSafeLabel(
@@ -115,7 +128,14 @@
// Added for mdoc use case where the provider may not need to register a service and
// instead only relies on the registration api.
try {
- val pkgInfo = getPackageInfo(pm, providerFlattenedComponentName)
+ val pkgInfo = if (Flags.instantAppsEnabled()) {
+ getPackageInfo(pm, providerFlattenedComponentName)
+ } else {
+ pm.getPackageInfo(
+ component.packageName,
+ PackageManager.PackageInfoFlags.of(0)
+ )
+ }
val applicationInfo = checkNotNull(pkgInfo.applicationInfo)
providerLabel =
applicationInfo.loadSafeLabel(
@@ -143,12 +163,12 @@
pm: PackageManager,
packageName: String
): PackageInfo {
- val flags = PackageManager.MATCH_INSTANT
+ val packageManagerFlags = PackageManager.MATCH_INSTANT
return pm.getPackageInfo(
packageName,
PackageManager.PackageInfoFlags.of(
- (flags).toLong())
+ (packageManagerFlags).toLong())
)
}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
index 2318bb9..9355517 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
@@ -27,14 +27,22 @@
import android.os.CancellationSignal
import android.os.OutcomeReceiver
import android.service.autofill.AutofillService
+import android.service.autofill.Dataset
+import android.service.autofill.Field
import android.service.autofill.FillCallback
import android.service.autofill.FillRequest
import android.service.autofill.FillResponse
+import android.service.autofill.InlinePresentation
+import android.service.autofill.Presentations
import android.service.autofill.SaveCallback
import android.service.autofill.SaveRequest
import android.service.credentials.CredentialProviderService
import android.util.Log
import android.view.autofill.AutofillId
+import org.json.JSONException
+import android.widget.inline.InlinePresentationSpec
+import androidx.autofill.inline.v1.InlineSuggestionUi
+import com.android.credentialmanager.GetFlowUtils
import org.json.JSONObject
import java.util.concurrent.Executors
@@ -49,11 +57,9 @@
private const val SYS_PROVIDER_REQ_KEY = "isSystemProviderRequired"
private const val CRED_OPTIONS_KEY = "credentialOptions"
private const val TYPE_KEY = "type"
+ private const val REQ_TYPE_KEY = "get"
}
- private val credentialManager: CredentialManager =
- getSystemService(Context.CREDENTIAL_SERVICE) as CredentialManager
-
override fun onFillRequest(
request: FillRequest,
cancellationSignal: CancellationSignal,
@@ -66,16 +72,24 @@
val getCredRequest: GetCredentialRequest? = getCredManRequest(structure)
if (getCredRequest == null) {
+ Log.i(TAG, "No credential manager request found")
callback.onFailure("No credential manager request found")
return
}
+ val credentialManager: CredentialManager =
+ getSystemService(Context.CREDENTIAL_SERVICE) as CredentialManager
val outcome = object : OutcomeReceiver<GetCandidateCredentialsResponse,
GetCandidateCredentialsException> {
override fun onResult(result: GetCandidateCredentialsResponse) {
Log.i(TAG, "getCandidateCredentials onResponse")
- val fillResponse: FillResponse? = convertToFillResponse(result, request)
- callback.onSuccess(fillResponse)
+ val fillResponse = convertToFillResponse(result, request)
+ if (fillResponse != null) {
+ callback.onSuccess(fillResponse)
+ } else {
+ Log.e(TAG, "Failed to create a FillResponse from the CredentialResponse.")
+ callback.onFailure("No dataset was created from the CredentialResponse")
+ }
}
override fun onError(error: GetCandidateCredentialsException) {
@@ -97,7 +111,74 @@
getCredResponse: GetCandidateCredentialsResponse,
filLRequest: FillRequest
): FillResponse? {
- TODO("Not yet implemented")
+ val providerList = GetFlowUtils.toProviderList(
+ getCredResponse.candidateProviderDataList,
+ this@CredentialAutofillService)
+ var totalEntryCount = 0
+ providerList.forEach { provider ->
+ totalEntryCount += provider.credentialEntryList.size
+ }
+ val inlineSuggestionsRequest = filLRequest.inlineSuggestionsRequest
+ val inlineMaxSuggestedCount = inlineSuggestionsRequest?.maxSuggestionCount ?: 0
+ val inlinePresentationSpecs = inlineSuggestionsRequest?.inlinePresentationSpecs
+ val inlinePresentationSpecsCount = inlinePresentationSpecs?.size ?: 0
+ var maxItemCount = totalEntryCount
+ if (inlineMaxSuggestedCount > 0) {
+ maxItemCount = maxItemCount.coerceAtMost(inlineMaxSuggestedCount)
+ }
+ var i = 0
+ val fillResponseBuilder = FillResponse.Builder()
+ var emptyFillResponse = true
+ providerList.forEach {provider ->
+ // TODO(b/299321128): Before iterating the list, sort the list so that
+ // the relevant entries don't get truncated
+ provider.credentialEntryList.forEach entryLoop@ {entry ->
+ val autofillId: AutofillId? = entry.fillInIntent?.getParcelableExtra(
+ CredentialProviderService.EXTRA_AUTOFILL_ID,
+ AutofillId::class.java)
+ val pendingIntent = entry.pendingIntent
+ if (autofillId == null || pendingIntent == null) {
+ return@entryLoop
+ }
+ var inlinePresentation: InlinePresentation? = null
+ // Create inline presentation
+ if (inlinePresentationSpecs != null && i < maxItemCount) {
+ val spec: InlinePresentationSpec
+ if (i < inlinePresentationSpecsCount) {
+ spec = inlinePresentationSpecs[i]
+ } else {
+ spec = inlinePresentationSpecs[inlinePresentationSpecsCount - 1]
+ }
+ val sliceBuilder = InlineSuggestionUi
+ .newContentBuilder(pendingIntent)
+ .setTitle(entry.userName)
+ inlinePresentation = InlinePresentation(
+ sliceBuilder.build().slice, spec, /* pinned= */ false)
+ }
+ i++
+
+ val dataSetBuilder = Dataset.Builder()
+ val presentationBuilder = Presentations.Builder()
+ if (inlinePresentation != null) {
+ presentationBuilder.setInlinePresentation(inlinePresentation)
+ }
+ fillResponseBuilder.addDataset(
+ dataSetBuilder
+ .setField(
+ autofillId,
+ Field.Builder().setPresentations(
+ presentationBuilder.build())
+ .build())
+ .setAuthentication(entry.pendingIntent.intentSender)
+ .setAuthenticationExtras(entry.fillInIntent.extras)
+ .build())
+ emptyFillResponse = false
+ }
+ }
+ if (emptyFillResponse) {
+ return null
+ }
+ return fillResponseBuilder.build()
}
override fun onSaveRequest(request: SaveRequest, callback: SaveCallback) {
@@ -165,8 +246,12 @@
val credentialOptions: MutableList<CredentialOption> = mutableListOf()
for (credentialHint in credentialHints) {
- convertJsonToCredentialOption(credentialHint, autofillId)
- .let { credentialOptions.addAll(it) }
+ try {
+ convertJsonToCredentialOption(credentialHint, autofillId)
+ .let { credentialOptions.addAll(it) }
+ } catch (e: JSONException) {
+ Log.i(TAG, "Exception while parsing response: " + e.message)
+ }
}
return credentialOptions
}
@@ -178,7 +263,8 @@
val credentialOptions: MutableList<CredentialOption> = mutableListOf()
val json = JSONObject(jsonString)
- val options = json.getJSONArray(CRED_OPTIONS_KEY)
+ val jsonGet = json.getJSONObject(REQ_TYPE_KEY)
+ val options = jsonGet.getJSONArray(CRED_OPTIONS_KEY)
for (i in 0 until options.length()) {
val option = options.getJSONObject(i)
val candidateBundle = convertJsonToBundle(option.getJSONObject(CANDIDATE_DATA_KEY))
diff --git a/packages/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/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/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/build.gradle.kts b/packages/SettingsLib/Spa/build.gradle.kts
index 1cc2867..0b7a568 100644
--- a/packages/SettingsLib/Spa/build.gradle.kts
+++ b/packages/SettingsLib/Spa/build.gradle.kts
@@ -26,7 +26,7 @@
}
allprojects {
- extra["jetpackComposeVersion"] = "1.6.0-alpha02"
+ extra["jetpackComposeVersion"] = "1.6.0-alpha07"
}
subprojects {
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/button/ActionButtonsPage.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/button/ActionButtonPageProvider.kt
similarity index 96%
rename from packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/button/ActionButtonsPage.kt
rename to packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/button/ActionButtonPageProvider.kt
index df1d7d1..b001cad 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/button/ActionButtonsPage.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/button/ActionButtonPageProvider.kt
@@ -18,8 +18,8 @@
import android.os.Bundle
import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.automirrored.outlined.Launch
import androidx.compose.material.icons.outlined.Delete
-import androidx.compose.material.icons.outlined.Launch
import androidx.compose.material.icons.outlined.WarningAmber
import androidx.compose.runtime.Composable
import androidx.compose.ui.tooling.preview.Preview
@@ -47,7 +47,7 @@
override fun Page(arguments: Bundle?) {
RegularScaffold(title = TITLE) {
val 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/gradle/libs.versions.toml b/packages/SettingsLib/Spa/gradle/libs.versions.toml
index 8b56336..aafae5f 100644
--- a/packages/SettingsLib/Spa/gradle/libs.versions.toml
+++ b/packages/SettingsLib/Spa/gradle/libs.versions.toml
@@ -15,11 +15,11 @@
#
[versions]
-agp = "8.1.1"
+agp = "8.1.2"
compose-compiler = "1.5.1"
dexmaker-mockito = "2.28.3"
kotlin = "1.9.0"
-truth = "1.1"
+truth = "1.1.5"
[libraries]
dexmaker-mockito = { module = "com.linkedin.dexmaker:dexmaker-mockito", version.ref = "dexmaker-mockito" }
diff --git a/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.properties b/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.properties
index da04f42..ce89de6 100644
--- a/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.properties
+++ b/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.properties
@@ -16,7 +16,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
diff --git a/packages/SettingsLib/Spa/spa/build.gradle.kts b/packages/SettingsLib/Spa/spa/build.gradle.kts
index b810511..b73bbd8 100644
--- a/packages/SettingsLib/Spa/spa/build.gradle.kts
+++ b/packages/SettingsLib/Spa/spa/build.gradle.kts
@@ -57,13 +57,13 @@
api("androidx.slice:slice-builders:1.1.0-alpha02")
api("androidx.slice:slice-core:1.1.0-alpha02")
api("androidx.slice:slice-view:1.1.0-alpha02")
- api("androidx.compose.material3:material3:1.2.0-alpha04")
+ api("androidx.compose.material3:material3:1.2.0-alpha09")
api("androidx.compose.material:material-icons-extended:$jetpackComposeVersion")
api("androidx.compose.runtime:runtime-livedata:$jetpackComposeVersion")
api("androidx.compose.ui:ui-tooling-preview:$jetpackComposeVersion")
api("androidx.lifecycle:lifecycle-livedata-ktx")
api("androidx.lifecycle:lifecycle-runtime-compose")
- api("androidx.navigation:navigation-compose:2.7.1")
+ api("androidx.navigation:navigation-compose:2.7.4")
api("com.github.PhilJay:MPAndroidChart:v3.1.0-alpha")
api("com.google.android.material:material:1.7.0-alpha03")
debugApi("androidx.compose.ui:ui-tooling:$jetpackComposeVersion")
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/button/ActionButtons.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/button/ActionButtons.kt
index 0552c40..1ad075c 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/button/ActionButtons.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/button/ActionButtons.kt
@@ -31,7 +31,6 @@
import androidx.compose.foundation.layout.width
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Delete
-import androidx.compose.material.icons.outlined.Launch
import androidx.compose.material.icons.outlined.WarningAmber
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.FilledTonalButton
@@ -52,6 +51,7 @@
import com.android.settingslib.spa.framework.theme.SettingsShape
import com.android.settingslib.spa.framework.theme.SettingsTheme
import com.android.settingslib.spa.framework.theme.divider
+import androidx.compose.material.icons.automirrored.outlined.Launch
data class ActionButton(
val text: String,
@@ -101,7 +101,9 @@
modifier = Modifier.size(SettingsDimension.itemIconSize),
)
Box(
- modifier = Modifier.padding(top = 4.dp).fillMaxHeight(),
+ modifier = Modifier
+ .padding(top = 4.dp)
+ .fillMaxHeight(),
contentAlignment = Alignment.Center,
) {
Text(
@@ -129,7 +131,7 @@
SettingsTheme {
ActionButtons(
listOf(
- ActionButton(text = "Open", imageVector = Icons.Outlined.Launch) {},
+ ActionButton(text = "Open", imageVector = Icons.AutoMirrored.Outlined.Launch) {},
ActionButton(text = "Uninstall", imageVector = Icons.Outlined.Delete) {},
ActionButton(text = "Force stop", imageVector = Icons.Outlined.WarningAmber) {},
)
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/Actions.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/Actions.kt
index 62189dc..6ef4590 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/Actions.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/Actions.kt
@@ -18,7 +18,6 @@
import androidx.appcompat.R
import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.outlined.ArrowBack
import androidx.compose.material.icons.outlined.Clear
import androidx.compose.material.icons.outlined.FindInPage
import androidx.compose.material3.Icon
@@ -31,6 +30,7 @@
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.LayoutDirection
import com.android.settingslib.spa.framework.compose.LocalNavController
+import androidx.compose.material.icons.automirrored.outlined.ArrowBack
/** Action that navigates back to last page. */
@Composable
@@ -53,7 +53,7 @@
private fun BackAction(contentDescription: String, onClick: () -> Unit) {
IconButton(onClick) {
Icon(
- imageVector = Icons.Outlined.ArrowBack,
+ imageVector = Icons.AutoMirrored.Outlined.ArrowBack,
contentDescription = contentDescription,
modifier = Modifier.autoMirrored(),
)
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SettingsPager.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SettingsPager.kt
index aa148b0..9f7f040 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SettingsPager.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SettingsPager.kt
@@ -21,7 +21,7 @@
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.pager.HorizontalPager
import androidx.compose.foundation.pager.rememberPagerState
-import androidx.compose.material3.TabRow
+import androidx.compose.material3.PrimaryTabRow
import androidx.compose.runtime.Composable
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
@@ -43,7 +43,7 @@
val coroutineScope = rememberCoroutineScope()
val pagerState = rememberPagerState { titles.size }
- TabRow(
+ PrimaryTabRow(
selectedTabIndex = pagerState.currentPage,
modifier = Modifier.padding(horizontal = SettingsDimension.itemPaddingEnd),
containerColor = Color.Transparent,
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/button/ActionButtonsTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/button/ActionButtonsTest.kt
index f59b0de..8d9bac6 100644
--- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/button/ActionButtonsTest.kt
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/button/ActionButtonsTest.kt
@@ -17,8 +17,8 @@
package com.android.settingslib.spa.widget.button
import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.automirrored.outlined.Launch
import androidx.compose.material.icons.outlined.Close
-import androidx.compose.material.icons.outlined.Launch
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
@@ -43,7 +43,10 @@
composeTestRule.setContent {
ActionButtons(
listOf(
- ActionButton(text = "Open", imageVector = Icons.Outlined.Launch) {},
+ ActionButton(
+ text = "Open",
+ imageVector = Icons.AutoMirrored.Outlined.Launch
+ ) {},
)
)
}
@@ -57,7 +60,7 @@
composeTestRule.setContent {
ActionButtons(
listOf(
- ActionButton(text = "Open", imageVector = Icons.Outlined.Launch) {
+ ActionButton(text = "Open", imageVector = Icons.AutoMirrored.Outlined.Launch) {
clicked = true
},
)
@@ -74,7 +77,10 @@
composeTestRule.setContent {
ActionButtons(
listOf(
- ActionButton(text = "Open", imageVector = Icons.Outlined.Launch) {},
+ ActionButton(
+ text = "Open",
+ imageVector = Icons.AutoMirrored.Outlined.Launch
+ ) {},
ActionButton(text = "Close", imageVector = Icons.Outlined.Close) {},
)
)
diff --git a/packages/SettingsLib/Spa/testutils/build.gradle.kts b/packages/SettingsLib/Spa/testutils/build.gradle.kts
index 50243dc..cce8235 100644
--- a/packages/SettingsLib/Spa/testutils/build.gradle.kts
+++ b/packages/SettingsLib/Spa/testutils/build.gradle.kts
@@ -41,7 +41,7 @@
api("androidx.arch.core:core-testing:2.2.0-alpha01")
api("androidx.compose.ui:ui-test-junit4:$jetpackComposeVersion")
api("androidx.lifecycle:lifecycle-runtime-testing")
- api("org.mockito.kotlin:mockito-kotlin:5.1.0")
+ api("org.mockito.kotlin:mockito-kotlin:2.2.11")
api("org.mockito:mockito-core") {
version {
strictly("2.28.2")
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/model/enterprise/RestrictionsProvider.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictionsProvider.kt
index f54de15..09cb98e 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictionsProvider.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictionsProvider.kt
@@ -22,6 +22,8 @@
import android.os.UserManager
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
+import androidx.compose.runtime.remember
+import androidx.compose.ui.platform.LocalContext
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.settingslib.RestrictedLockUtils
import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin
@@ -32,15 +34,15 @@
import com.android.settingslib.widget.restricted.R
data class Restrictions(
- val userId: Int,
+ val userId: Int = UserHandle.myUserId(),
val keys: List<String>,
)
sealed interface RestrictedMode
-object NoRestricted : RestrictedMode
+data object NoRestricted : RestrictedMode
-object BaseUserRestricted : RestrictedMode
+data object BaseUserRestricted : RestrictedMode
interface BlockedByAdmin : RestrictedMode {
fun getSummary(checked: Boolean?): String
@@ -79,6 +81,17 @@
typealias RestrictionsProviderFactory = (Context, Restrictions) -> RestrictionsProvider
+@Composable
+internal fun RestrictionsProviderFactory.rememberRestrictedMode(
+ restrictions: Restrictions,
+): State<RestrictedMode?> {
+ val context = LocalContext.current
+ val restrictionsProvider = remember(restrictions) {
+ this(context, restrictions)
+ }
+ return restrictionsProvider.restrictedModeState()
+}
+
internal class RestrictionsProviderImpl(
private val context: Context,
private val restrictions: Restrictions,
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPage.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPage.kt
index 1fa854a..17e9708 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPage.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPage.kt
@@ -45,6 +45,7 @@
import com.android.settingslib.spaprivileged.model.enterprise.Restrictions
import com.android.settingslib.spaprivileged.model.enterprise.RestrictionsProviderFactory
import com.android.settingslib.spaprivileged.model.enterprise.RestrictionsProviderImpl
+import com.android.settingslib.spaprivileged.model.enterprise.rememberRestrictedMode
import com.android.settingslib.spaprivileged.template.preference.RestrictedSwitchPreference
import kotlinx.coroutines.flow.Flow
@@ -149,14 +150,13 @@
@Composable
fun getSummary(record: T): State<String> {
- val restrictionsProvider = remember(record.app.userId) {
- val restrictions = Restrictions(
+ val restrictions = remember(record.app.userId) {
+ Restrictions(
userId = record.app.userId,
keys = listModel.switchRestrictionKeys,
)
- restrictionsProviderFactory(context, restrictions)
}
- val restrictedMode = restrictionsProvider.restrictedModeState()
+ val restrictedMode = restrictionsProviderFactory.rememberRestrictedMode(restrictions)
val allowed = listModel.isAllowed(record)
return remember {
derivedStateOf {
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/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/preference/RestrictedPreference.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/preference/RestrictedPreference.kt
new file mode 100644
index 0000000..50490c0
--- /dev/null
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/preference/RestrictedPreference.kt
@@ -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.settingslib.spaprivileged.template.preference
+
+import androidx.annotation.VisibleForTesting
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Box
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.semantics.Role
+import com.android.settingslib.spa.framework.compose.stateOf
+import com.android.settingslib.spa.widget.preference.Preference
+import com.android.settingslib.spa.widget.preference.PreferenceModel
+import com.android.settingslib.spaprivileged.model.enterprise.BaseUserRestricted
+import com.android.settingslib.spaprivileged.model.enterprise.BlockedByAdmin
+import com.android.settingslib.spaprivileged.model.enterprise.NoRestricted
+import com.android.settingslib.spaprivileged.model.enterprise.RestrictedMode
+import com.android.settingslib.spaprivileged.model.enterprise.Restrictions
+import com.android.settingslib.spaprivileged.model.enterprise.RestrictionsProviderFactory
+import com.android.settingslib.spaprivileged.model.enterprise.RestrictionsProviderImpl
+import com.android.settingslib.spaprivileged.model.enterprise.rememberRestrictedMode
+
+@Composable
+fun RestrictedPreference(
+ model: PreferenceModel,
+ restrictions: Restrictions,
+) {
+ RestrictedPreference(model, restrictions, ::RestrictionsProviderImpl)
+}
+
+@VisibleForTesting
+@Composable
+internal fun RestrictedPreference(
+ model: PreferenceModel,
+ restrictions: Restrictions,
+ restrictionsProviderFactory: RestrictionsProviderFactory,
+) {
+ if (restrictions.keys.isEmpty()) {
+ Preference(model)
+ return
+ }
+ val restrictedMode = restrictionsProviderFactory.rememberRestrictedMode(restrictions).value
+ val restrictedSwitchModel = remember(restrictedMode) {
+ RestrictedPreferenceModel(model, restrictedMode)
+ }
+ restrictedSwitchModel.RestrictionWrapper {
+ Preference(restrictedSwitchModel)
+ }
+}
+
+private class RestrictedPreferenceModel(
+ model: PreferenceModel,
+ private val restrictedMode: RestrictedMode?,
+) : PreferenceModel {
+ override val title = model.title
+ override val summary = model.summary
+ override val icon = model.icon
+
+ override val enabled = when (restrictedMode) {
+ NoRestricted -> model.enabled
+ else -> stateOf(false)
+ }
+
+ override val onClick = when (restrictedMode) {
+ NoRestricted -> model.onClick
+ // Need to passthrough onClick for clickable semantics, although since enabled is false so
+ // this will not be called.
+ BaseUserRestricted -> model.onClick
+ else -> null
+ }
+
+ @Composable
+ fun RestrictionWrapper(content: @Composable () -> Unit) {
+ if (restrictedMode !is BlockedByAdmin) {
+ content()
+ return
+ }
+ Box(
+ Modifier
+ .clickable(
+ role = Role.Button,
+ onClick = { restrictedMode.sendShowAdminSupportDetailsIntent() },
+ )
+ ) { content() }
+ }
+}
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/preference/RestrictedSwitchPreference.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/preference/RestrictedSwitchPreference.kt
index e77dcd4..2129403 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/preference/RestrictedSwitchPreference.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/preference/RestrictedSwitchPreference.kt
@@ -17,6 +17,7 @@
package com.android.settingslib.spaprivileged.template.preference
import android.content.Context
+import androidx.annotation.VisibleForTesting
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.runtime.Composable
@@ -40,22 +41,29 @@
import com.android.settingslib.spaprivileged.model.enterprise.Restrictions
import com.android.settingslib.spaprivileged.model.enterprise.RestrictionsProviderFactory
import com.android.settingslib.spaprivileged.model.enterprise.RestrictionsProviderImpl
+import com.android.settingslib.spaprivileged.model.enterprise.rememberRestrictedMode
@Composable
fun RestrictedSwitchPreference(
model: SwitchPreferenceModel,
restrictions: Restrictions,
- restrictionsProviderFactory: RestrictionsProviderFactory = ::RestrictionsProviderImpl,
+) {
+ RestrictedSwitchPreference(model, restrictions, ::RestrictionsProviderImpl)
+}
+
+@VisibleForTesting
+@Composable
+internal fun RestrictedSwitchPreference(
+ model: SwitchPreferenceModel,
+ restrictions: Restrictions,
+ restrictionsProviderFactory: RestrictionsProviderFactory,
) {
if (restrictions.keys.isEmpty()) {
SwitchPreference(model)
return
}
val context = LocalContext.current
- val restrictionsProvider = remember(restrictions) {
- restrictionsProviderFactory(context, restrictions)
- }
- val restrictedMode = restrictionsProvider.restrictedModeState().value
+ val restrictedMode = restrictionsProviderFactory.rememberRestrictedMode(restrictions).value
val restrictedSwitchModel = remember(restrictedMode) {
RestrictedSwitchPreferenceModel(context, model, restrictedMode)
}
@@ -112,8 +120,8 @@
override val onCheckedChange = when (restrictedMode) {
null -> null
is NoRestricted -> model.onCheckedChange
- // Need to pass a non null onCheckedChange to enable semantics ToggleableState, although
- // since changeable is false this will not be called.
+ // Need to passthrough onCheckedChange for toggleable semantics, although since changeable
+ // is false so this will not be called.
is BaseUserRestricted -> model.onCheckedChange
// Pass null since semantics ToggleableState is provided in RestrictionWrapper.
is BlockedByAdmin -> null
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/scaffold/RestrictedMenuItem.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/scaffold/RestrictedMenuItem.kt
index 86b6f02..f9abefc 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/scaffold/RestrictedMenuItem.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/scaffold/RestrictedMenuItem.kt
@@ -16,15 +16,15 @@
package com.android.settingslib.spaprivileged.template.scaffold
+import androidx.annotation.VisibleForTesting
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.remember
-import androidx.compose.ui.platform.LocalContext
import com.android.settingslib.spa.widget.scaffold.MoreOptionsScope
import com.android.settingslib.spaprivileged.model.enterprise.BaseUserRestricted
import com.android.settingslib.spaprivileged.model.enterprise.BlockedByAdmin
import com.android.settingslib.spaprivileged.model.enterprise.Restrictions
import com.android.settingslib.spaprivileged.model.enterprise.RestrictionsProviderFactory
import com.android.settingslib.spaprivileged.model.enterprise.RestrictionsProviderImpl
+import com.android.settingslib.spaprivileged.model.enterprise.rememberRestrictedMode
@Composable
fun MoreOptionsScope.RestrictedMenuItem(
@@ -35,6 +35,7 @@
RestrictedMenuItemImpl(text, restrictions, onClick, ::RestrictionsProviderImpl)
}
+@VisibleForTesting
@Composable
internal fun MoreOptionsScope.RestrictedMenuItemImpl(
text: String,
@@ -42,12 +43,8 @@
onClick: () -> Unit,
restrictionsProviderFactory: RestrictionsProviderFactory,
) {
- val context = LocalContext.current
- val restrictionsProvider = remember(restrictions) {
- restrictionsProviderFactory(context, restrictions)
- }
- val restrictedMode = restrictionsProvider.restrictedModeState().value
- MenuItem(text = text, enabled = restrictedMode !is BaseUserRestricted) {
+ val restrictedMode = restrictionsProviderFactory.rememberRestrictedMode(restrictions).value
+ MenuItem(text = text, enabled = restrictedMode !== BaseUserRestricted) {
when (restrictedMode) {
is BlockedByAdmin -> restrictedMode.sendShowAdminSupportDetailsIntent()
else -> onClick()
diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/preference/RestrictedPreferenceTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/preference/RestrictedPreferenceTest.kt
new file mode 100644
index 0000000..eadf0ca
--- /dev/null
+++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/preference/RestrictedPreferenceTest.kt
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.spaprivileged.template.preference
+
+import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.assertIsEnabled
+import androidx.compose.ui.test.assertIsNotEnabled
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithText
+import androidx.compose.ui.test.onRoot
+import androidx.compose.ui.test.performClick
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settingslib.spa.widget.preference.PreferenceModel
+import com.android.settingslib.spaprivileged.model.enterprise.BaseUserRestricted
+import com.android.settingslib.spaprivileged.model.enterprise.NoRestricted
+import com.android.settingslib.spaprivileged.model.enterprise.Restrictions
+import com.android.settingslib.spaprivileged.tests.testutils.FakeBlockedByAdmin
+import com.android.settingslib.spaprivileged.tests.testutils.FakeRestrictionsProvider
+import com.google.common.truth.Truth.assertThat
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class RestrictedPreferenceTest {
+ @get:Rule
+ val composeTestRule = createComposeRule()
+
+ private val fakeBlockedByAdmin = FakeBlockedByAdmin()
+
+ private val fakeRestrictionsProvider = FakeRestrictionsProvider()
+
+ private var clicked = false
+
+ private val preferenceModel = object : PreferenceModel {
+ override val title = TITLE
+ override val onClick = { clicked = true }
+ }
+
+ @Test
+ fun whenRestrictionsKeysIsEmpty_enabled() {
+ val restrictions = Restrictions(userId = USER_ID, keys = emptyList())
+
+ setContent(restrictions)
+
+ composeTestRule.onNodeWithText(TITLE).assertIsDisplayed().assertIsEnabled()
+ }
+
+ @Test
+ fun whenRestrictionsKeysIsEmpty_clickable() {
+ val restrictions = Restrictions(userId = USER_ID, keys = emptyList())
+
+ setContent(restrictions)
+ composeTestRule.onRoot().performClick()
+
+ assertThat(clicked).isTrue()
+ }
+
+ @Test
+ fun whenNoRestricted_enabled() {
+ val restrictions = Restrictions(userId = USER_ID, keys = listOf(RESTRICTION_KEY))
+ fakeRestrictionsProvider.restrictedMode = NoRestricted
+
+ setContent(restrictions)
+
+ composeTestRule.onNodeWithText(TITLE).assertIsDisplayed().assertIsEnabled()
+ }
+
+ @Test
+ fun whenNoRestricted_clickable() {
+ val restrictions = Restrictions(userId = USER_ID, keys = listOf(RESTRICTION_KEY))
+ fakeRestrictionsProvider.restrictedMode = NoRestricted
+
+ setContent(restrictions)
+ composeTestRule.onRoot().performClick()
+
+ assertThat(clicked).isTrue()
+ }
+
+ @Test
+ fun whenBaseUserRestricted_disabled() {
+ val restrictions = Restrictions(userId = USER_ID, keys = listOf(RESTRICTION_KEY))
+ fakeRestrictionsProvider.restrictedMode = BaseUserRestricted
+
+ setContent(restrictions)
+
+ composeTestRule.onNodeWithText(TITLE).assertIsDisplayed().assertIsNotEnabled()
+ }
+
+ @Test
+ fun whenBaseUserRestricted_notClickable() {
+ val restrictions = Restrictions(userId = USER_ID, keys = listOf(RESTRICTION_KEY))
+ fakeRestrictionsProvider.restrictedMode = BaseUserRestricted
+
+ setContent(restrictions)
+ composeTestRule.onRoot().performClick()
+
+ assertThat(clicked).isFalse()
+ }
+
+ @Test
+ fun whenBlockedByAdmin_widgetInEnableStateToAllowClick() {
+ val restrictions = Restrictions(userId = USER_ID, keys = listOf(RESTRICTION_KEY))
+ fakeRestrictionsProvider.restrictedMode = fakeBlockedByAdmin
+
+ setContent(restrictions)
+
+ composeTestRule.onNodeWithText(TITLE).assertIsDisplayed().assertIsEnabled()
+ }
+
+ @Test
+ fun whenBlockedByAdmin_click() {
+ val restrictions = Restrictions(userId = USER_ID, keys = listOf(RESTRICTION_KEY))
+ fakeRestrictionsProvider.restrictedMode = fakeBlockedByAdmin
+
+ setContent(restrictions)
+ composeTestRule.onRoot().performClick()
+
+ assertThat(fakeBlockedByAdmin.sendShowAdminSupportDetailsIntentIsCalled).isTrue()
+ }
+
+ private fun setContent(restrictions: Restrictions) {
+ composeTestRule.setContent {
+ RestrictedPreference(preferenceModel, restrictions) { _, _ ->
+ fakeRestrictionsProvider
+ }
+ }
+ }
+
+ private companion object {
+ const val TITLE = "Title"
+ const val USER_ID = 0
+ const val RESTRICTION_KEY = "restriction_key"
+ }
+}
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 cab0841..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..b4bc319 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..9e550fb 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 00a98bc..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/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/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowColorDisplayManager.java b/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowColorDisplayManager.java
new file mode 100644
index 0000000..a9fd380
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowColorDisplayManager.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 android.Manifest;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.hardware.display.ColorDisplayManager;
+
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+
+@Implements(ColorDisplayManager.class)
+public class ShadowColorDisplayManager extends org.robolectric.shadows.ShadowColorDisplayManager {
+
+ private boolean mIsReduceBrightColorsActivated;
+
+ @Implementation
+ @SystemApi
+ @RequiresPermission(Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS)
+ public boolean setReduceBrightColorsActivated(boolean activated) {
+ mIsReduceBrightColorsActivated = activated;
+ return true;
+ }
+
+ @Implementation
+ @SystemApi
+ public boolean isReduceBrightColorsActivated() {
+ return mIsReduceBrightColorsActivated;
+ }
+
+}
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 96f3290d..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,
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
index f5d9475..fe39c4f 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
@@ -103,5 +103,8 @@
Settings.System.SCREEN_FLASH_NOTIFICATION_COLOR,
Settings.System.PEAK_REFRESH_RATE,
Settings.System.MIN_REFRESH_RATE,
+ Settings.System.NOTIFICATION_COOLDOWN_ENABLED,
+ Settings.System.NOTIFICATION_COOLDOWN_ALL,
+ Settings.System.NOTIFICATION_COOLDOWN_VIBRATE_UNLOCKED,
};
}
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
index ba06185..7e8fe7e 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
@@ -171,6 +171,7 @@
VALIDATORS.put(Global.WIFI_SCAN_THROTTLE_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Global.APP_AUTO_RESTRICTION_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Global.ZEN_DURATION, ANY_INTEGER_VALIDATOR);
+ VALIDATORS.put(Global.REVERSE_CHARGING_AUTO_ON, ANY_INTEGER_VALIDATOR);
VALIDATORS.put(Global.CHARGING_VIBRATION_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Global.DEVICE_PROVISIONING_MOBILE_DATA_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Global.REQUIRE_PASSWORD_TO_DECRYPT, BOOLEAN_VALIDATOR);
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index f48600d..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,
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
index 410269f..eba74ab 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
@@ -31,6 +31,7 @@
import android.content.ComponentName;
import android.hardware.display.ColorDisplayManager;
import android.os.BatteryManager;
+import android.provider.Settings.Global;
import android.provider.Settings.System;
import android.util.ArrayMap;
@@ -239,5 +240,8 @@
VALIDATORS.put(System.SCREEN_FLASH_NOTIFICATION_COLOR, ANY_INTEGER_VALIDATOR);
VALIDATORS.put(System.PEAK_REFRESH_RATE, NON_NEGATIVE_FLOAT_VALIDATOR);
VALIDATORS.put(System.MIN_REFRESH_RATE, NON_NEGATIVE_FLOAT_VALIDATOR);
+ VALIDATORS.put(System.NOTIFICATION_COOLDOWN_ENABLED, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(System.NOTIFICATION_COOLDOWN_ALL, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(System.NOTIFICATION_COOLDOWN_VIBRATE_UNLOCKED, BOOLEAN_VALIDATOR);
}
}
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index e40fcb2..9d3200d 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",
@@ -241,9 +243,6 @@
"tests/src/com/android/systemui/statusbar/pipeline/shared/data/repository/FakeConnectivityRepository.kt",
"tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/FakeWifiRepository.kt",
- /* Log fakes */
- "tests/src/com/android/systemui/log/core/FakeLogBuffer.kt",
-
/* QS fakes */
"tests/src/com/android/systemui/qs/pipeline/domain/interactor/FakeQSTile.kt",
],
@@ -435,6 +434,7 @@
"SystemUI-statsd",
"SettingsLib",
"com_android_systemui_flags_lib",
+ "flag-junit-base",
"androidx.viewpager2_viewpager2",
"androidx.legacy_legacy-support-v4",
"androidx.recyclerview_recyclerview",
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/TEST_MAPPING b/packages/SystemUI/TEST_MAPPING
index 0623d4a..03f7c99 100644
--- a/packages/SystemUI/TEST_MAPPING
+++ b/packages/SystemUI/TEST_MAPPING
@@ -144,9 +144,32 @@
"exclude-annotation": "androidx.test.filters.FlakyTest"
},
{
+ "exclude-annotation": "android.platform.test.annotations.FlakyTest"
+ },
+ {
"exclude-annotation": "android.platform.test.annotations.Postsubmit"
}
]
}
+ ],
+ // v2/sysui/suite/test-mapping-sysui-screenshot-test-staged
+ "sysui-screenshot-test-staged": [
+ {
+ "name": "SystemUIGoogleScreenshotTests",
+ "options": [
+ {
+ "exclude-annotation": "org.junit.Ignore"
+ },
+ {
+ "include-annotation": "androidx.test.filters.FlakyTest"
+ },
+ {
+ "include-annotation": "android.platform.test.annotations.FlakyTest"
+ },
+ {
+ "include-annotation": "android.platform.test.annotations.Postsubmit"
+ }
+ ]
+ }
]
}
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/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 18117a8..2509cfd 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -21,3 +21,11 @@
"(containing the \"Clear all\" button). Should not bring any behavior changes"
bug: "293167744"
}
+
+flag {
+ name: "notification_lifetime_extension_refactor"
+ namespace: "systemui"
+ description: "Enables moving notification lifetime extension management from SystemUI to "
+ "Notification Manager Service"
+ bug: "299448097"
+}
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
index 4aac279..4ea57a8 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
@@ -893,7 +893,7 @@
return
}
- Log.i(TAG, "Remote animation timed out")
+ Log.wtf(TAG, "Remote animation timed out")
timedOut = true
if (DEBUG_LAUNCH_ANIMATION) {
diff --git a/packages/SystemUI/communal/layout/Android.bp b/packages/SystemUI/communal/layout/Android.bp
new file mode 100644
index 0000000..88dad66
--- /dev/null
+++ b/packages/SystemUI/communal/layout/Android.bp
@@ -0,0 +1,35 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ default_applicable_licenses: ["frameworks_base_packages_SystemUI_license"],
+}
+
+android_library {
+ name: "CommunalLayoutLib",
+ srcs: [
+ "src/**/*.kt",
+ ],
+ static_libs: [
+ "androidx.arch.core_core-runtime",
+ "androidx.compose.animation_animation-graphics",
+ "androidx.compose.runtime_runtime",
+ "androidx.compose.material3_material3",
+ "jsr330",
+ "kotlinx-coroutines-android",
+ "kotlinx-coroutines-core",
+ ],
+ manifest: "AndroidManifest.xml",
+ kotlincflags: ["-Xjvm-default=all"],
+}
diff --git a/packages/SystemUI/communal/layout/AndroidManifest.xml b/packages/SystemUI/communal/layout/AndroidManifest.xml
new file mode 100644
index 0000000..141be07
--- /dev/null
+++ b/packages/SystemUI/communal/layout/AndroidManifest.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!-- Copyright (C) 2023 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest package="com.android.systemui.communal.layout" />
diff --git a/packages/SystemUI/communal/layout/src/com/android/systemui/communal/layout/CommunalLayoutEngine.kt b/packages/SystemUI/communal/layout/src/com/android/systemui/communal/layout/CommunalLayoutEngine.kt
new file mode 100644
index 0000000..df87d19d
--- /dev/null
+++ b/packages/SystemUI/communal/layout/src/com/android/systemui/communal/layout/CommunalLayoutEngine.kt
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.layout
+
+import com.android.systemui.communal.layout.ui.compose.config.CommunalGridLayoutCard
+
+/** Computes the arrangement of cards. */
+class CommunalLayoutEngine {
+ companion object {
+ /**
+ * Determines the size that each card should be rendered in, and distributes the cards into
+ * columns.
+ *
+ * Returns a nested list where the outer list contains columns, and the inner list contains
+ * cards in each column.
+ *
+ * Currently treats the first supported size as the size to be rendered in, ignoring other
+ * supported sizes.
+ */
+ fun distributeCardsIntoColumns(
+ cards: List<CommunalGridLayoutCard>,
+ ): List<List<CommunalGridLayoutCardInfo>> {
+ val result = ArrayList<ArrayList<CommunalGridLayoutCardInfo>>()
+
+ var capacityOfLastColumn = 0
+ for (card in cards) {
+ val cardSize = card.supportedSizes.first()
+ if (capacityOfLastColumn >= cardSize.value) {
+ // Card fits in last column
+ capacityOfLastColumn -= cardSize.value
+ } else {
+ // Create a new column
+ result.add(arrayListOf())
+ capacityOfLastColumn = CommunalGridLayoutCard.Size.FULL.value - cardSize.value
+ }
+
+ result.last().add(CommunalGridLayoutCardInfo(card, cardSize))
+ }
+
+ return result
+ }
+ }
+
+ /**
+ * A data class that wraps around a [CommunalGridLayoutCard] and also contains the size that the
+ * card should be rendered in.
+ */
+ data class CommunalGridLayoutCardInfo(
+ val card: CommunalGridLayoutCard,
+ val size: CommunalGridLayoutCard.Size,
+ )
+}
diff --git a/packages/SystemUI/communal/layout/src/com/android/systemui/communal/layout/ui/compose/CommunalGridLayout.kt b/packages/SystemUI/communal/layout/src/com/android/systemui/communal/layout/ui/compose/CommunalGridLayout.kt
new file mode 100644
index 0000000..4ed78b3
--- /dev/null
+++ b/packages/SystemUI/communal/layout/src/com/android/systemui/communal/layout/ui/compose/CommunalGridLayout.kt
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.layout.ui.compose
+
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.lazy.LazyRow
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import com.android.systemui.communal.layout.CommunalLayoutEngine
+import com.android.systemui.communal.layout.ui.compose.config.CommunalGridLayoutCard
+import com.android.systemui.communal.layout.ui.compose.config.CommunalGridLayoutConfig
+
+/**
+ * An arrangement of cards with a horizontal scroll, where each card is displayed in the right size
+ * and follows a specific order based on its priority, ensuring a seamless layout without any gaps.
+ */
+@Composable
+fun CommunalGridLayout(
+ modifier: Modifier,
+ layoutConfig: CommunalGridLayoutConfig,
+ communalCards: List<CommunalGridLayoutCard>,
+) {
+ val columns = CommunalLayoutEngine.distributeCardsIntoColumns(communalCards)
+ LazyRow(
+ modifier = modifier.height(layoutConfig.gridHeight),
+ horizontalArrangement = Arrangement.spacedBy(layoutConfig.gridGutter),
+ ) {
+ for (column in columns) {
+ item {
+ Column(
+ modifier = Modifier.width(layoutConfig.cardWidth),
+ verticalArrangement = Arrangement.spacedBy(layoutConfig.gridGutter),
+ ) {
+ for (cardInfo in column) {
+ Row(
+ modifier = Modifier.height(layoutConfig.cardHeight(cardInfo.size)),
+ ) {
+ cardInfo.card.Content(Modifier.fillMaxSize())
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/communal/layout/src/com/android/systemui/communal/layout/ui/compose/config/CommunalGridLayoutCard.kt b/packages/SystemUI/communal/layout/src/com/android/systemui/communal/layout/ui/compose/config/CommunalGridLayoutCard.kt
new file mode 100644
index 0000000..ac8aa67
--- /dev/null
+++ b/packages/SystemUI/communal/layout/src/com/android/systemui/communal/layout/ui/compose/config/CommunalGridLayoutCard.kt
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.layout.ui.compose.config
+
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+
+/** A card that hosts content to be rendered in the communal grid layout. */
+abstract class CommunalGridLayoutCard {
+ /**
+ * Content to be hosted by the card.
+ *
+ * To host non-Compose views, see
+ * https://developer.android.com/jetpack/compose/migrate/interoperability-apis/views-in-compose.
+ */
+ @Composable abstract fun Content(modifier: Modifier)
+
+ /**
+ * Sizes supported by the card.
+ *
+ * If multiple sizes are available, they should be ranked in order of preference, from most to
+ * least preferred.
+ */
+ abstract val supportedSizes: List<Size>
+
+ /**
+ * Priority of the content hosted by the card.
+ *
+ * The value of priority is relative to other cards. Cards with a higher priority are generally
+ * ordered first.
+ */
+ open val priority: Int = 0
+
+ /**
+ * Size of the card.
+ *
+ * @param value A numeric value that represents the size. Must be less than or equal to
+ * [Size.FULL].
+ */
+ enum class Size(val value: Int) {
+ /** The card takes up full height of the grid layout. */
+ FULL(value = 6),
+
+ /** The card takes up half of the vertical space of the grid layout. */
+ HALF(value = 3),
+
+ /** The card takes up a third of the vertical space of the grid layout. */
+ THIRD(value = 2),
+ }
+}
diff --git a/packages/SystemUI/communal/layout/src/com/android/systemui/communal/layout/ui/compose/config/CommunalGridLayoutConfig.kt b/packages/SystemUI/communal/layout/src/com/android/systemui/communal/layout/ui/compose/config/CommunalGridLayoutConfig.kt
new file mode 100644
index 0000000..143df83
--- /dev/null
+++ b/packages/SystemUI/communal/layout/src/com/android/systemui/communal/layout/ui/compose/config/CommunalGridLayoutConfig.kt
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.layout.ui.compose.config
+
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.times
+
+/**
+ * Configurations of the communal grid layout.
+ *
+ * The communal grid layout follows Material Design's responsive layout grid (see
+ * https://m2.material.io/design/layout/responsive-layout-grid.html), in which the layout is divided
+ * up by columns and gutters, and each card occupies one or multiple columns.
+ */
+data class CommunalGridLayoutConfig(
+ /**
+ * Size in dp of each grid column.
+ *
+ * Every card occupies one or more grid columns, which means that the width of each card is
+ * influenced by the size of the grid columns.
+ */
+ val gridColumnSize: Dp,
+
+ /**
+ * Size in dp of each grid gutter.
+ *
+ * A gutter is the space between columns that helps separate content. This is, therefore, also
+ * the size of the gaps between cards, both horizontally and vertically.
+ */
+ val gridGutter: Dp,
+
+ /**
+ * Height in dp of the grid layout.
+ *
+ * Cards with a full size take up the entire height of the grid layout.
+ */
+ val gridHeight: Dp,
+
+ /**
+ * Number of grid columns that each card occupies.
+ *
+ * It's important to note that all the cards take up the same number of grid columns, or in
+ * simpler terms, they all have the same width.
+ */
+ val gridColumnsPerCard: Int,
+) {
+ /**
+ * Width in dp of each card.
+ *
+ * It's important to note that all the cards take up the same number of grid columns, or in
+ * simpler terms, they all have the same width.
+ */
+ val cardWidth = gridColumnSize * gridColumnsPerCard + gridGutter * (gridColumnsPerCard - 1)
+
+ /** Returns the height of a card in dp, based on its size. */
+ fun cardHeight(cardSize: CommunalGridLayoutCard.Size): Dp {
+ return when (cardSize) {
+ CommunalGridLayoutCard.Size.FULL -> cardHeightBy(denominator = 1)
+ CommunalGridLayoutCard.Size.HALF -> cardHeightBy(denominator = 2)
+ CommunalGridLayoutCard.Size.THIRD -> cardHeightBy(denominator = 3)
+ }
+ }
+
+ /** Returns the height of a card in dp when the layout is evenly divided by [denominator]. */
+ private fun cardHeightBy(denominator: Int): Dp {
+ return (gridHeight - (denominator - 1) * gridGutter) / denominator
+ }
+}
diff --git a/packages/SystemUI/communal/layout/tests/Android.bp b/packages/SystemUI/communal/layout/tests/Android.bp
new file mode 100644
index 0000000..9a05504
--- /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",
+ ],
+ 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/facade/disabled/src/com/android/systemui/compose/ComposeFacade.kt b/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/compose/ComposeFacade.kt
index 5b4a8fb..c6e429a 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
@@ -22,6 +22,7 @@
import android.view.WindowInsets
import androidx.activity.ComponentActivity
import androidx.lifecycle.LifecycleOwner
+import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
import com.android.systemui.people.ui.viewmodel.PeopleViewModel
import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel
import com.android.systemui.scene.shared.model.Scene
@@ -64,6 +65,13 @@
throwComposeUnavailableError()
}
+ override fun createCommunalView(
+ context: Context,
+ viewModel: CommunalViewModel,
+ ): 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..1722685 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,8 @@
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.communal.ui.viewmodel.CommunalViewModel
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 +95,15 @@
}
}
+ override fun createCommunalView(
+ context: Context,
+ viewModel: CommunalViewModel,
+ ): View {
+ return ComposeView(context).apply {
+ setContent { PlatformTheme { CommunalHub(viewModel = viewModel) } }
+ }
+ }
+
// 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..16c2437 100644
--- a/packages/SystemUI/compose/features/Android.bp
+++ b/packages/SystemUI/compose/features/Android.bp
@@ -31,8 +31,10 @@
],
static_libs: [
+ "CommunalLayoutLib",
"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..3d827fb
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
@@ -0,0 +1,65 @@
+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.Card
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.getValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.res.dimensionResource
+import androidx.compose.ui.res.integerResource
+import com.android.systemui.communal.layout.ui.compose.CommunalGridLayout
+import com.android.systemui.communal.layout.ui.compose.config.CommunalGridLayoutCard
+import com.android.systemui.communal.layout.ui.compose.config.CommunalGridLayoutConfig
+import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
+import com.android.systemui.res.R
+
+@Composable
+fun CommunalHub(
+ modifier: Modifier = Modifier,
+ viewModel: CommunalViewModel,
+) {
+ val showTutorial by viewModel.showTutorialContent.collectAsState(initial = false)
+ Box(
+ modifier = modifier.fillMaxSize().background(Color.White),
+ ) {
+ CommunalGridLayout(
+ modifier = Modifier.align(Alignment.CenterStart),
+ layoutConfig =
+ CommunalGridLayoutConfig(
+ gridColumnSize = dimensionResource(R.dimen.communal_grid_column_size),
+ gridGutter = dimensionResource(R.dimen.communal_grid_gutter_size),
+ gridHeight = dimensionResource(R.dimen.communal_grid_height),
+ gridColumnsPerCard = integerResource(R.integer.communal_grid_columns_per_card),
+ ),
+ communalCards = if (showTutorial) tutorialContent else emptyList(),
+ )
+ }
+}
+
+private val tutorialContent =
+ listOf(
+ tutorialCard(CommunalGridLayoutCard.Size.FULL),
+ tutorialCard(CommunalGridLayoutCard.Size.THIRD),
+ tutorialCard(CommunalGridLayoutCard.Size.THIRD),
+ tutorialCard(CommunalGridLayoutCard.Size.THIRD),
+ tutorialCard(CommunalGridLayoutCard.Size.HALF),
+ tutorialCard(CommunalGridLayoutCard.Size.HALF),
+ tutorialCard(CommunalGridLayoutCard.Size.HALF),
+ tutorialCard(CommunalGridLayoutCard.Size.HALF),
+ )
+
+private fun tutorialCard(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/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..f3bef7b 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,15 +16,10 @@
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.communal.ui.viewmodel.CommunalViewModel
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.scene.shared.model.Direction
import com.android.systemui.scene.shared.model.SceneKey
@@ -38,7 +33,11 @@
/** The communal scene shows glanceable hub when the device is locked and docked. */
@SysUISingleton
-class CommunalScene @Inject constructor() : ComposableScene {
+class CommunalScene
+@Inject
+constructor(
+ private val viewModel: CommunalViewModel,
+) : ComposableScene {
override val key = SceneKey.Communal
override val destinationScenes: StateFlow<Map<UserAction, SceneModel>> =
@@ -51,13 +50,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, viewModel)
}
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/people/ui/compose/PeopleScreen.kt b/packages/SystemUI/compose/features/src/com/android/systemui/people/ui/compose/PeopleScreen.kt
index e12b7ea..73cb72c 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/people/ui/compose/PeopleScreen.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/people/ui/compose/PeopleScreen.kt
@@ -47,10 +47,10 @@
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import com.android.compose.theme.LocalAndroidColorScheme
-import com.android.systemui.res.R
import com.android.systemui.compose.modifiers.sysuiResTag
import com.android.systemui.people.ui.viewmodel.PeopleTileViewModel
import com.android.systemui.people.ui.viewmodel.PeopleViewModel
+import com.android.systemui.res.R
/**
* Compose the screen associated to a [PeopleViewModel].
@@ -86,9 +86,9 @@
modifier = Modifier.fillMaxSize(),
) {
if (priorityTiles.isNotEmpty() || recentTiles.isNotEmpty()) {
- PeopleScreenWithConversations(priorityTiles, recentTiles, viewModel::onTileClicked)
+ PeopleScreenWithConversations(priorityTiles, recentTiles, viewModel.onTileClicked)
} else {
- PeopleScreenEmpty(viewModel::onUserJourneyCancelled)
+ PeopleScreenEmpty(viewModel.onUserJourneyCancelled)
}
}
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
index 0da562b..2e93a09 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
@@ -161,8 +161,7 @@
fromScene = fromScene.toModel().key,
toScene = toScene.toModel().key,
progress = progress,
- isInitiatedByUserInput = isInitiatedByUserInput,
- isUserInputOngoing = isUserInputOngoing,
+ isUserInputDriven = isUserInputDriven,
)
}
}
diff --git a/packages/SystemUI/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 591fa76..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
@@ -371,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 90%
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
index 60c3fd3..88944f10 100644
--- 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
@@ -108,7 +108,7 @@
) {
val fromScene = layoutImpl.state.transitionState.currentScene
val isUserInput =
- (layoutImpl.state.transitionState as? TransitionState.Transition)?.isInitiatedByUserInput
+ (layoutImpl.state.transitionState as? TransitionState.Transition)?.isUserInputDriven
?: false
val animationSpec = layoutImpl.transitions.transitionSpec(fromScene, target).spec
@@ -119,23 +119,9 @@
val targetProgress = if (reversed) 0f else 1f
val transition =
if (reversed) {
- OneOffTransition(
- fromScene = target,
- toScene = fromScene,
- currentScene = target,
- isUserInput,
- isUserInputOngoing = false,
- animatable,
- )
+ OneOffTransition(target, fromScene, currentScene = target, isUserInput, animatable)
} else {
- OneOffTransition(
- fromScene = fromScene,
- toScene = target,
- currentScene = target,
- isUserInput,
- isUserInputOngoing = false,
- animatable,
- )
+ OneOffTransition(fromScene, target, currentScene = target, isUserInput, animatable)
}
// Change the current layout state to use this new transition.
@@ -156,8 +142,7 @@
override val fromScene: SceneKey,
override val toScene: SceneKey,
override val currentScene: SceneKey,
- override val isInitiatedByUserInput: Boolean,
- override val isUserInputOngoing: Boolean,
+ override val isUserInputDriven: Boolean,
private val animatable: Animatable<Float, AnimationVector1D>,
) : TransitionState.Transition {
override val progress: Float
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/Element.kt b/packages/SystemUI/compose/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 87%
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
index 1b79dbd..ccdec6e 100644
--- 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
@@ -52,14 +52,7 @@
* scene, this value will remain true after the pointer is no longer touching the screen and
* will be true in any transition created to animate back to the original position.
*/
- val isInitiatedByUserInput: Boolean,
-
- /**
- * Whether user input is currently driving the transition. For example, if a user is
- * dragging a pointer, this emits true. Once they lift their finger, this emits false while
- * the transition completes/settles.
- */
- val isUserInputOngoing: Flow<Boolean>,
+ val isUserInputDriven: Boolean,
) : ObservableTransitionState()
}
@@ -80,8 +73,7 @@
fromScene = state.fromScene,
toScene = state.toScene,
progress = snapshotFlow { state.progress },
- isInitiatedByUserInput = state.isInitiatedByUserInput,
- isUserInputOngoing = snapshotFlow { state.isUserInputOngoing },
+ isUserInputDriven = state.isUserInputDriven,
)
}
}
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/Scene.kt b/packages/SystemUI/compose/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 76%
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 58c7bdb..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,6 +158,19 @@
): 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
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 94%
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
index b9f83c5..7a21211 100644
--- 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
@@ -70,9 +70,6 @@
val progress: Float
/** Whether the transition was triggered by user input rather than being programmatic. */
- val isInitiatedByUserInput: Boolean
-
- /** Whether user input is currently driving the transition. */
- val isUserInputOngoing: Boolean
+ val isUserInputDriven: Boolean
}
}
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/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 93%
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
index e275fca..1cbfe30 100644
--- 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
@@ -66,7 +66,7 @@
// swipe in the other direction.
val startDragImmediately =
state == transition &&
- !transition.isUserInputOngoing &&
+ transition.isAnimatingOffset &&
!currentScene.shouldEnableSwipes(orientation.opposite())
// The velocity threshold at which the intent of the user is to swipe up or down. It is the same
@@ -126,7 +126,7 @@
override val progress: Float
get() {
- val offset = if (isUserInputOngoing) dragOffset else offsetAnimatable.value
+ val offset = if (isAnimatingOffset) offsetAnimatable.value else dragOffset
if (distance == 0f) {
// This can happen only if fromScene == toScene.
error(
@@ -137,15 +137,16 @@
return offset / distance
}
- override val isInitiatedByUserInput = true
-
- var _isUserInputOngoing by mutableStateOf(false)
- override val isUserInputOngoing: Boolean
- get() = _isUserInputOngoing
+ override val isUserInputDriven = true
/** The current offset caused by the drag gesture. */
var dragOffset by mutableFloatStateOf(0f)
+ /**
+ * Whether the offset is animated (the user lifted their finger) or if it is driven by gesture.
+ */
+ var isAnimatingOffset by mutableStateOf(false)
+
/** The animatable used to animate the offset once the user lifted its finger. */
val offsetAnimatable = Animatable(0f, visibilityThreshold = OffsetVisibilityThreshold)
@@ -208,11 +209,9 @@
transition: SwipeTransition,
orientation: Orientation,
) {
- transition._isUserInputOngoing = true
-
if (layoutImpl.state.transitionState == transition) {
// This [transition] was already driving the animation: simply take over it.
- if (!transition.isUserInputOngoing) {
+ if (transition.isAnimatingOffset) {
// Stop animating and start from where the current offset. Setting the animation job to
// `null` will effectively cancel the animation.
transition.stopOffsetAnimation()
@@ -457,29 +456,30 @@
) {
transition.startOffsetAnimation {
launch {
- if (transition.isUserInputOngoing) {
- transition.offsetAnimatable.snapTo(transition.dragOffset)
- }
- transition._isUserInputOngoing = false
+ if (!transition.isAnimatingOffset) {
+ transition.offsetAnimatable.snapTo(transition.dragOffset)
+ }
+ transition.isAnimatingOffset = true
- transition.offsetAnimatable.animateTo(
- targetOffset,
- // TODO(b/290184746): Make this spring spec configurable.
- spring(
- stiffness = Spring.StiffnessMediumLow,
- visibilityThreshold = OffsetVisibilityThreshold
- ),
- initialVelocity = initialVelocity,
- )
+ transition.offsetAnimatable.animateTo(
+ targetOffset,
+ // TODO(b/290184746): Make this spring spec configurable.
+ spring(
+ stiffness = Spring.StiffnessMediumLow,
+ visibilityThreshold = OffsetVisibilityThreshold
+ ),
+ initialVelocity = initialVelocity,
+ )
- // Now that the animation is done, the state should be idle. Note that if the state
- // was changed since this animation started, some external code changed it and we
- // shouldn't do anything here. Note also that this job will be cancelled in the case
- // where the user intercepts this swipe.
- if (layoutImpl.state.transitionState == transition) {
- layoutImpl.state.transitionState = TransitionState.Idle(targetScene)
+ // Now that the animation is done, the state should be idle. Note that if the state
+ // was changed since this animation started, some external code changed it and we
+ // shouldn't do anything here. Note also that this job will be cancelled in the case
+ // where the user intercepts this swipe.
+ if (layoutImpl.state.transitionState == transition) {
+ layoutImpl.state.transitionState = TransitionState.Idle(targetScene)
+ }
}
- }
+ .also { it.invokeOnCompletion { transition.isAnimatingOffset = false } }
}
}
diff --git a/packages/SystemUI/compose/core/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 95%
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 53ed2b5..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,
@@ -122,8 +122,7 @@
assertThat(transition.toScene).isEqualTo(TestScenes.SceneB)
assertThat(transition.currentScene).isEqualTo(TestScenes.SceneA)
assertThat(transition.progress).isEqualTo(55.dp / LayoutWidth)
- assertThat(transition.isInitiatedByUserInput).isTrue()
- assertThat(transition.isUserInputOngoing).isTrue()
+ assertThat(transition.isUserInputDriven).isTrue()
// Release the finger. We should now be animating back to A (currentScene = SceneA) given
// that 55dp < positional threshold.
@@ -135,8 +134,7 @@
assertThat(transition.toScene).isEqualTo(TestScenes.SceneB)
assertThat(transition.currentScene).isEqualTo(TestScenes.SceneA)
assertThat(transition.progress).isEqualTo(55.dp / LayoutWidth)
- assertThat(transition.isInitiatedByUserInput).isTrue()
- assertThat(transition.isUserInputOngoing).isFalse()
+ assertThat(transition.isUserInputDriven).isTrue()
// Wait for the animation to finish. We should now be in scene A.
rule.waitForIdle()
@@ -158,8 +156,7 @@
assertThat(transition.toScene).isEqualTo(TestScenes.SceneC)
assertThat(transition.currentScene).isEqualTo(TestScenes.SceneA)
assertThat(transition.progress).isEqualTo(56.dp / LayoutHeight)
- assertThat(transition.isInitiatedByUserInput).isTrue()
- assertThat(transition.isUserInputOngoing).isTrue()
+ assertThat(transition.isUserInputDriven).isTrue()
// Release the finger. We should now be animating to C (currentScene = SceneC) given
// that 56dp >= positional threshold.
@@ -171,8 +168,7 @@
assertThat(transition.toScene).isEqualTo(TestScenes.SceneC)
assertThat(transition.currentScene).isEqualTo(TestScenes.SceneC)
assertThat(transition.progress).isEqualTo(56.dp / LayoutHeight)
- assertThat(transition.isInitiatedByUserInput).isTrue()
- assertThat(transition.isUserInputOngoing).isFalse()
+ assertThat(transition.isUserInputDriven).isTrue()
// Wait for the animation to finish. We should now be in scene C.
rule.waitForIdle()
diff --git a/packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/TestTransition.kt b/packages/SystemUI/compose/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/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/drawable/progress_bar.xml b/packages/SystemUI/res-keyguard/drawable/progress_bar.xml
new file mode 100644
index 0000000..910a74a
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/drawable/progress_bar.xml
@@ -0,0 +1,45 @@
+<?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.
+ -->
+
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ android:paddingMode="stack">
+ <item
+ android:id="@android:id/background"
+ android:gravity="center_vertical|fill_horizontal">
+ <shape
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:shape="rectangle">
+ <corners android:radius="30dp" />
+ <solid android:color="?androidprv:attr/materialColorSurfaceContainerHighest" />
+ </shape>
+ </item>
+ <item
+ android:id="@android:id/progress"
+ android:gravity="center_vertical|fill_horizontal">
+ <clip>
+ <shape
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:shape="rectangle">
+ <corners android:radius="30dp" />
+ <solid android:color="?androidprv:attr/textColorPrimary" />
+ </shape>
+ </clip>
+ </item>
+</layer-list>
\ No newline at end of file
diff --git a/packages/SystemUI/res-keyguard/layout/sidefps_progress_bar.xml b/packages/SystemUI/res-keyguard/layout/sidefps_progress_bar.xml
new file mode 100644
index 0000000..183f0e5
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/layout/sidefps_progress_bar.xml
@@ -0,0 +1,32 @@
+<?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.
+ ~
+ -->
+
+<LinearLayout android:layout_height="match_parent"
+ android:layout_width="match_parent"
+ android:orientation="vertical"
+ android:layoutDirection="ltr"
+ android:gravity="center"
+ xmlns:android="http://schemas.android.com/apk/res/android">
+ <ProgressBar
+ android:id="@+id/side_fps_progress_bar"
+ android:layout_width="55dp"
+ android:layout_height="10dp"
+ android:indeterminateOnly="false"
+ android:min="0"
+ android:max="100"
+ android:progressDrawable="@drawable/progress_bar" />
+</LinearLayout>
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/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 5b5ac73..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 1fda572..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..9ac1e9f 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -730,6 +730,9 @@
<!-- Whether the communal service should be enabled -->
<bool name="config_communalServiceEnabled">false</bool>
+ <!-- Component names of allowed communal widgets -->
+ <string-array name="config_communalWidgetAllowlist" translatable="false" />
+
<!-- Component name of communal source service -->
<string name="config_communalSourceComponent" translatable="false">@null</string>
@@ -947,6 +950,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 +960,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..10fd8b5 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>
@@ -760,6 +766,8 @@
<dimen name="keyguard_clock_switch_y_shift">14dp</dimen>
<!-- When large clock is showing, offset the smartspace by this amount -->
<dimen name="keyguard_smartspace_top_offset">12dp</dimen>
+ <!-- The amount to translate lockscreen elements on the GONE->AOD transition -->
+ <dimen name="keyguard_enter_from_top_translation_y">-100dp</dimen>
<dimen name="notification_scrim_corner_radius">32dp</dimen>
@@ -846,12 +854,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 +1363,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>
@@ -1653,6 +1669,15 @@
<!-- Height percentage of the parent container occupied by the communal view -->
<item name="communal_source_height_percentage" format="float" type="dimen">0.80</item>
+ <!-- Size of each communal grid column -->
+ <dimen name="communal_grid_column_size">64dp</dimen>
+ <!-- Size of each communal grid gutter between columns -->
+ <dimen name="communal_grid_gutter_size">16dp</dimen>
+ <!-- Height of the communal grid layout -->
+ <dimen name="communal_grid_height">630dp</dimen>
+ <!-- Number of columns for each communal card -->
+ <integer name="communal_grid_columns_per_card">6</integer>
+
<dimen name="drag_and_drop_icon_size">70dp</dimen>
<dimen name="qs_tile_service_request_dialog_width">304dp</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..7a6d29a 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -209,6 +209,8 @@
<string name="screenshot_saved_title">Screenshot saved</string>
<!-- Notification title displayed when we fail to take a screenshot. [CHAR LIMIT=50] -->
<string name="screenshot_failed_title">Couldn\'t save screenshot</string>
+ <!-- Appended to the notification content when a screenshot failure happens on an external display. [CHAR LIMIT=50] -->
+ <string name="screenshot_failed_external_display_indication">External Display</string>
<!-- Notification text displayed when we fail to save a screenshot due to locked storage. [CHAR LIMIT=100] -->
<string name="screenshot_failed_to_save_user_locked_text">Device must be unlocked before screenshot can be saved</string>
<!-- Notification text displayed when we fail to save a screenshot for unknown reasons. [CHAR LIMIT=100] -->
@@ -711,8 +713,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 +1045,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/shared/system/ActivityManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
index 80040a3..631423e 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
@@ -155,6 +155,26 @@
}
}
+
+ /**
+ * Requests for a new snapshot to be taken for the given task, stores it in the cache, and
+ * returns a {@link ThumbnailData} with the result.
+ */
+ @NonNull
+ public ThumbnailData takeTaskThumbnail(int taskId) {
+ TaskSnapshot snapshot = null;
+ try {
+ snapshot = getService().takeTaskSnapshot(taskId, /* updateCache= */ true);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to take task snapshot", e);
+ }
+ if (snapshot != null) {
+ return new ThumbnailData(snapshot);
+ } else {
+ return new ThumbnailData();
+ }
+ }
+
/**
* Removes the outdated snapshot of home task.
*
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationRunnerCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationRunnerCompat.java
index 1e7222d..88b9c02 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationRunnerCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationRunnerCompat.java
@@ -233,6 +233,11 @@
runner.onAnimationCancelled();
finishRunnable.run();
}
+
+ @Override
+ public void onTransitionConsumed(IBinder iBinder, boolean aborted)
+ throws RemoteException {
+ }
};
}
}
diff --git a/packages/SystemUI/src-debug/com/android/systemui/flags/FlagsFactory.kt b/packages/SystemUI/src-debug/com/android/systemui/flags/FlagsFactory.kt
index 3360c96..aef8371 100644
--- a/packages/SystemUI/src-debug/com/android/systemui/flags/FlagsFactory.kt
+++ b/packages/SystemUI/src-debug/com/android/systemui/flags/FlagsFactory.kt
@@ -24,7 +24,7 @@
val knownFlags: Map<String, Flag<*>>
get() {
// We need to access Flags in order to initialize our map.
- assert(flagMap.contains(Flags.TEAMFOOD.name)) { "Where is teamfood?" }
+ assert(flagMap.contains(Flags.NULL_FLAG.name)) { "Where is the null flag?" }
return flagMap
}
diff --git a/packages/SystemUI/src-release/com/android/systemui/flags/FlagsFactory.kt b/packages/SystemUI/src-release/com/android/systemui/flags/FlagsFactory.kt
index 75465c2..f4b4296 100644
--- a/packages/SystemUI/src-release/com/android/systemui/flags/FlagsFactory.kt
+++ b/packages/SystemUI/src-release/com/android/systemui/flags/FlagsFactory.kt
@@ -24,7 +24,7 @@
val knownFlags: Map<String, Flag<*>>
get() {
// We need to access Flags in order to initialize our map.
- assert(flagMap.contains(Flags.TEAMFOOD.name)) { "Where is teamfood?" }
+ assert(flagMap.contains(Flags.NULL_FLAG.name)) { "Where is the null flag?" }
return flagMap
}
diff --git a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
index eb7a735..e47d36f 100644
--- a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
+++ b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
@@ -15,6 +15,8 @@
*/
package com.android.keyguard
+import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
+import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
@@ -34,9 +36,10 @@
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
+import com.android.systemui.flags.Flags.MIGRATE_KEYGUARD_STATUS_VIEW
import com.android.systemui.flags.Flags.REGION_SAMPLING
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
@@ -60,6 +63,7 @@
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.merge
import kotlinx.coroutines.launch
import java.util.Locale
import java.util.TimeZone
@@ -79,7 +83,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,
@@ -296,7 +300,7 @@
object : KeyguardUpdateMonitorCallback() {
override fun onKeyguardVisibilityChanged(visible: Boolean) {
isKeyguardVisible = visible
- if (!featureFlags.isEnabled(DOZING_MIGRATION_1)) {
+ if (!featureFlags.isEnabled(MIGRATE_KEYGUARD_STATUS_VIEW)) {
if (!isKeyguardVisible) {
clock?.run {
smallClock.animations.doze(if (isDozing) 1f else 0f)
@@ -341,9 +345,9 @@
keyguardUpdateMonitor.registerCallback(keyguardUpdateMonitorCallback)
disposableHandle =
parent.repeatWhenAttached {
- repeatOnLifecycle(Lifecycle.State.STARTED) {
+ repeatOnLifecycle(Lifecycle.State.CREATED) {
listenForDozing(this)
- if (featureFlags.isEnabled(DOZING_MIGRATION_1)) {
+ if (featureFlags.isEnabled(MIGRATE_KEYGUARD_STATUS_VIEW)) {
listenForDozeAmountTransition(this)
listenForAnyStateToAodTransition(this)
} else {
@@ -453,8 +457,9 @@
@VisibleForTesting
internal fun listenForAnyStateToAodTransition(scope: CoroutineScope): Job {
return scope.launch {
- keyguardTransitionInteractor.anyStateToAodTransition
- .filter { it.transitionState == TransitionState.FINISHED }
+ keyguardTransitionInteractor.transitionStepsToState(AOD)
+ .filter { it.transitionState == TransitionState.STARTED }
+ .filter { it.from != LOCKSCREEN }
.collect { handleDoze(1f) }
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
index f6a0563..9bddcd7 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
@@ -22,10 +22,10 @@
import com.android.app.animation.Interpolators;
import com.android.keyguard.dagger.KeyguardStatusViewScope;
-import com.android.systemui.res.R;
import com.android.systemui.log.LogBuffer;
import com.android.systemui.log.core.LogLevel;
import com.android.systemui.plugins.ClockController;
+import com.android.systemui.res.R;
import com.android.systemui.shared.clocks.DefaultClockController;
import java.io.PrintWriter;
@@ -452,6 +452,10 @@
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
+ // TODO: b/305022530
+ if (mClock.getConfig().getId().equals("DIGITAL_CLOCK_METRO")) {
+ mClock.getEvents().onColorPaletteChanged(mContext.getResources());
+ }
if (changed) {
post(() -> updateClockTargetRegions());
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
index 50be97e..3585feb 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
@@ -44,8 +44,6 @@
import android.view.WindowInsets;
import android.view.WindowInsetsAnimationControlListener;
import android.view.WindowInsetsAnimationController;
-import android.view.animation.AnimationUtils;
-import android.view.animation.Interpolator;
import android.widget.TextView;
import androidx.annotation.NonNull;
@@ -66,18 +64,8 @@
*/
public class KeyguardPasswordView extends KeyguardAbsKeyInputView {
- private final int mDisappearYTranslation;
-
- private static final long IME_DISAPPEAR_DURATION_MS = 125;
-
- // A delay constant to be used in a workaround for the situation where InputMethodManagerService
- // is not switched to the new user yet.
- // TODO: Remove this by ensuring such a race condition never happens.
-
private TextView mPasswordEntry;
private TextViewInputDisabler mPasswordEntryDisabler;
- private Interpolator mLinearOutSlowInInterpolator;
- private Interpolator mFastOutLinearInInterpolator;
private DisappearAnimationListener mDisappearAnimationListener;
@Nullable private MotionLayout mContainerMotionLayout;
private boolean mAlreadyUsingSplitBouncer = false;
@@ -93,12 +81,6 @@
public KeyguardPasswordView(Context context, AttributeSet attrs) {
super(context, attrs);
- mDisappearYTranslation = getResources().getDimensionPixelSize(
- R.dimen.disappear_y_translation);
- mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(
- context, android.R.interpolator.linear_out_slow_in);
- mFastOutLinearInInterpolator = AnimationUtils.loadInterpolator(
- context, android.R.interpolator.fast_out_linear_in);
}
/**
diff --git a/packages/SystemUI/src/com/android/keyguard/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/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
index 79642bd..758d1fe 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
@@ -54,6 +54,9 @@
import com.android.systemui.flags.FeatureFlags;
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.TransitionState;
+import com.android.systemui.keyguard.shared.model.TransitionStep;
import com.android.systemui.plugins.ClockController;
import com.android.systemui.power.domain.interactor.PowerInteractor;
import com.android.systemui.power.shared.model.ScreenPowerState;
@@ -102,10 +105,11 @@
private final Rect mClipBounds = new Rect();
private final KeyguardInteractor mKeyguardInteractor;
private final PowerInteractor mPowerInteractor;
+ private final KeyguardTransitionInteractor mKeyguardTransitionInteractor;
private Boolean mSplitShadeEnabled = false;
private Boolean mStatusViewCentered = true;
-
+ private boolean mGoneToAodTransitionRunning = false;
private DumpManager mDumpManager;
private final TransitionListenerAdapter mKeyguardStatusAlignmentTransitionListener =
@@ -135,6 +139,7 @@
FeatureFlags featureFlags,
InteractionJankMonitor interactionJankMonitor,
KeyguardInteractor keyguardInteractor,
+ KeyguardTransitionInteractor keyguardTransitionInteractor,
DumpManager dumpManager,
PowerInteractor powerInteractor) {
super(keyguardStatusView);
@@ -144,12 +149,13 @@
mConfigurationController = configurationController;
mKeyguardVisibilityHelper = new KeyguardVisibilityHelper(mView, keyguardStateController,
dozeParameters, screenOffAnimationController, /* animateYPos= */ true,
- logger.getBuffer());
+ featureFlags, logger.getBuffer());
mInteractionJankMonitor = interactionJankMonitor;
mFeatureFlags = featureFlags;
mDumpManager = dumpManager;
mKeyguardInteractor = keyguardInteractor;
mPowerInteractor = powerInteractor;
+ mKeyguardTransitionInteractor = keyguardTransitionInteractor;
}
@Override
@@ -199,6 +205,15 @@
dozeTimeTick();
}
}, context);
+
+ collectFlow(mView, mKeyguardTransitionInteractor.getGoneToAodTransition(),
+ (TransitionStep step) -> {
+ if (step.getTransitionState() == TransitionState.RUNNING) {
+ mGoneToAodTransitionRunning = true;
+ } else {
+ mGoneToAodTransitionRunning = false;
+ }
+ }, context);
}
public KeyguardStatusView getView() {
@@ -266,7 +281,7 @@
* Set keyguard status view alpha.
*/
public void setAlpha(float alpha) {
- if (!mKeyguardVisibilityHelper.isVisibilityAnimating()) {
+ if (!mKeyguardVisibilityHelper.isVisibilityAnimating() && !mGoneToAodTransitionRunning) {
mView.setAlpha(alpha);
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 3f5ec7d..205c297 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -153,7 +153,6 @@
import com.android.settingslib.WirelessUtils;
import com.android.settingslib.fuelgauge.BatteryStatus;
import com.android.systemui.Dumpable;
-import com.android.systemui.res.R;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.biometrics.FingerprintInteractiveToAuthProvider;
import com.android.systemui.broadcast.BroadcastDispatcher;
@@ -177,6 +176,7 @@
import com.android.systemui.log.SessionTracker;
import com.android.systemui.plugins.WeatherData;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.res.R;
import com.android.systemui.settings.DisplayTracker;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.shared.system.TaskStackChangeListener;
@@ -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;
@@ -1977,6 +1984,7 @@
@Override
public void onAuthenticationAcquired(int acquireInfo) {
Trace.beginSection("KeyguardUpdateMonitor#onAuthenticationAcquired");
+ mLogger.logFingerprintAcquired(acquireInfo);
handleFingerprintAcquired(acquireInfo);
Trace.endSection();
}
@@ -3455,7 +3463,8 @@
@Deprecated
private boolean isUnlockWithFacePossible(int userId) {
if (isFaceAuthInteractorEnabled()) {
- return getFaceAuthInteractor().canFaceAuthRun();
+ return getFaceAuthInteractor() != null
+ && getFaceAuthInteractor().isFaceAuthEnabledAndEnrolled();
}
return isFaceAuthEnabledForUser(userId) && !isFaceDisabled(userId);
}
@@ -4430,6 +4439,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/KeyguardVisibilityHelper.java b/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java
index c64ae01..d524e4a 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java
@@ -23,6 +23,8 @@
import android.view.View;
import com.android.app.animation.Interpolators;
+import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.Flags;
import com.android.systemui.log.LogBuffer;
import com.android.systemui.log.core.LogLevel;
import com.android.systemui.statusbar.StatusBarState;
@@ -53,6 +55,7 @@
private boolean mKeyguardViewVisibilityAnimating;
private boolean mLastOccludedState = false;
private final AnimationProperties mAnimationProperties = new AnimationProperties();
+ private final FeatureFlags mFeatureFlags;
private final LogBuffer mLogBuffer;
public KeyguardVisibilityHelper(View view,
@@ -60,12 +63,14 @@
DozeParameters dozeParameters,
ScreenOffAnimationController screenOffAnimationController,
boolean animateYPos,
+ FeatureFlags featureFlags,
LogBuffer logBuffer) {
mView = view;
mKeyguardStateController = keyguardStateController;
mDozeParameters = dozeParameters;
mScreenOffAnimationController = screenOffAnimationController;
mAnimateYPos = animateYPos;
+ mFeatureFlags = featureFlags;
mLogBuffer = logBuffer;
}
@@ -162,13 +167,17 @@
animProps,
true /* animate */);
} else if (mScreenOffAnimationController.shouldAnimateInKeyguard()) {
- log("ScreenOff transition");
- mKeyguardViewVisibilityAnimating = true;
+ if (mFeatureFlags.isEnabled(Flags.MIGRATE_KEYGUARD_STATUS_VIEW)) {
+ log("Using GoneToAodTransition");
+ mKeyguardViewVisibilityAnimating = false;
+ } else {
+ log("ScreenOff transition");
+ mKeyguardViewVisibilityAnimating = true;
- // Ask the screen off animation controller to animate the keyguard visibility for us
- // since it may need to be cancelled due to keyguard lifecycle events.
- mScreenOffAnimationController.animateInKeyguard(
- mView, mSetVisibleEndRunnable);
+ // Ask the screen off animation controller to animate the keyguard visibility
+ // for us since it may need to be cancelled due to keyguard lifecycle events.
+ mScreenOffAnimationController.animateInKeyguard(mView, mSetVisibleEndRunnable);
+ }
} else {
log("Direct set Visibility to VISIBLE");
mView.setVisibility(View.VISIBLE);
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
index 0d3f726..83da80f 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
@@ -692,10 +692,10 @@
mVelocityTracker.addMovement(event);
// Compute pointer velocity in pixels per second.
mVelocityTracker.computeCurrentVelocity(1000);
- float velocity = UdfpsController.computePointerSpeed(mVelocityTracker,
+ float velocity = computePointerSpeed(mVelocityTracker,
mActivePointerId);
if (event.getClassification() != MotionEvent.CLASSIFICATION_DEEP_PRESS
- && UdfpsController.exceedsVelocityThreshold(velocity)) {
+ && exceedsVelocityThreshold(velocity)) {
Log.v(TAG, "lock icon long-press rescheduled due to "
+ "high pointer velocity=" + velocity);
mLongPressCancelRunnable.run();
@@ -713,6 +713,23 @@
return true;
}
+ /**
+ * Calculate the pointer speed given a velocity tracker and the pointer id.
+ * This assumes that the velocity tracker has already been passed all relevant motion events.
+ */
+ private static float computePointerSpeed(@NonNull VelocityTracker tracker, int pointerId) {
+ final float vx = tracker.getXVelocity(pointerId);
+ final float vy = tracker.getYVelocity(pointerId);
+ return (float) Math.sqrt(Math.pow(vx, 2.0) + Math.pow(vy, 2.0));
+ }
+
+ /**
+ * Whether the velocity exceeds the acceptable UDFPS debouncing threshold.
+ */
+ private static boolean exceedsVelocityThreshold(float velocity) {
+ return velocity > 750f;
+ }
+
private boolean actionableDownEventStartedOnView(MotionEvent event) {
if (!isActionable()) {
return false;
diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardDisplayModule.kt b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardDisplayModule.kt
index c581788..1f145d8 100644
--- a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardDisplayModule.kt
+++ b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardDisplayModule.kt
@@ -4,6 +4,7 @@
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
@@ -23,11 +24,12 @@
companion object {
@Provides
@DisplaySpecific
- fun getDisplayContext(context: Context, display: Optional<Display>): Context {
- return if (display.isPresent) {
- context.createDisplayContext(display.get())
- } else {
+ 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)
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
index 2dfb370..fa07072 100644
--- a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
+++ b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
@@ -44,6 +44,7 @@
import javax.inject.Inject
private const val TAG = "KeyguardUpdateMonitorLog"
+private const val FP_LOG_TAG = "KeyguardFingerprintLog"
/** Helper class for logging for [com.android.keyguard.KeyguardUpdateMonitor] */
class KeyguardUpdateMonitorLogger
@@ -157,7 +158,7 @@
fun logFingerprintAuthForWrongUser(authUserId: Int) {
logBuffer.log(
- TAG,
+ FP_LOG_TAG,
DEBUG,
{ int1 = authUserId },
{ "Fingerprint authenticated for wrong user: $int1" }
@@ -166,7 +167,7 @@
fun logFingerprintDisabledForUser(userId: Int) {
logBuffer.log(
- TAG,
+ FP_LOG_TAG,
DEBUG,
{ int1 = userId },
{ "Fingerprint disabled by DPM for userId: $int1" }
@@ -174,12 +175,17 @@
}
fun logFingerprintLockoutReset(@LockoutMode mode: Int) {
- logBuffer.log(TAG, DEBUG, { int1 = mode }, { "handleFingerprintLockoutReset: $int1" })
+ logBuffer.log(
+ FP_LOG_TAG,
+ DEBUG,
+ { int1 = mode },
+ { "handleFingerprintLockoutReset: $int1" }
+ )
}
fun logFingerprintRunningState(fingerprintRunningState: Int) {
logBuffer.log(
- TAG,
+ FP_LOG_TAG,
DEBUG,
{ int1 = fingerprintRunningState },
{ "fingerprintRunningState: $int1" }
@@ -188,7 +194,7 @@
fun logFingerprintSuccess(userId: Int, isStrongBiometric: Boolean) {
logBuffer.log(
- TAG,
+ FP_LOG_TAG,
DEBUG,
{
int1 = userId
@@ -212,7 +218,7 @@
fun logFingerprintDetected(userId: Int, isStrongBiometric: Boolean) {
logBuffer.log(
- TAG,
+ FP_LOG_TAG,
DEBUG,
{
int1 = userId
@@ -224,7 +230,7 @@
fun logFingerprintError(msgId: Int, originalErrMsg: String) {
logBuffer.log(
- TAG,
+ FP_LOG_TAG,
DEBUG,
{
str1 = originalErrMsg
@@ -451,7 +457,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?) {
@@ -751,4 +757,25 @@
{ "userSwitchComplete: $str1, userId: $int1" }
)
}
+
+ fun logFingerprintHelp(helpMsgId: Int, helpString: CharSequence) {
+ logBuffer.log(
+ FP_LOG_TAG,
+ DEBUG,
+ {
+ int1 = helpMsgId
+ str1 = "$helpString"
+ },
+ { "fingerprint help message: $int1, $str1" }
+ )
+ }
+
+ fun logFingerprintAcquired(acquireInfo: Int) {
+ logBuffer.log(
+ FP_LOG_TAG,
+ DEBUG,
+ { int1 = acquireInfo },
+ { "fingerprint acquire message: $int1" }
+ )
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index 7739021..1a34cc4 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -15,123 +15,55 @@
package com.android.systemui;
import android.annotation.Nullable;
-import android.app.AlarmManager;
-import android.app.INotificationManager;
-import android.app.IWallpaperManager;
-import android.hardware.SensorPrivacyManager;
-import android.hardware.display.NightDisplayListener;
import android.os.Handler;
import android.os.Looper;
import android.util.ArrayMap;
-import android.util.DisplayMetrics;
-import android.view.IWindowManager;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.util.Preconditions;
-import com.android.keyguard.KeyguardSecurityModel;
import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.systemui.accessibility.AccessibilityButtonModeObserver;
import com.android.systemui.accessibility.AccessibilityButtonTargetsObserver;
-import com.android.systemui.accessibility.floatingmenu.AccessibilityFloatingMenuController;
import com.android.systemui.animation.DialogLaunchAnimator;
-import com.android.systemui.appops.AppOpsController;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.broadcast.BroadcastDispatcher;
-import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.dock.DockManager;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.fragments.FragmentService;
-import com.android.systemui.keyguard.ScreenLifecycle;
-import com.android.systemui.keyguard.WakefulnessLifecycle;
-import com.android.systemui.media.dialog.MediaOutputDialogFactory;
import com.android.systemui.model.SysUiState;
import com.android.systemui.navigationbar.NavigationBarController;
import com.android.systemui.navigationbar.NavigationModeController;
-import com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler;
-import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.DarkIconDispatcher;
-import com.android.systemui.plugins.PluginDependencyProvider;
import com.android.systemui.plugins.PluginManager;
import com.android.systemui.plugins.VolumeDialogController;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.power.EnhancedEstimates;
-import com.android.systemui.power.PowerUI;
-import com.android.systemui.privacy.PrivacyItemController;
-import com.android.systemui.qs.ReduceBrightColorsController;
-import com.android.systemui.qs.tiles.dialog.InternetDialogFactory;
import com.android.systemui.recents.OverviewProxyService;
-import com.android.systemui.screenrecord.RecordingController;
import com.android.systemui.settings.UserTracker;
-import com.android.systemui.shade.ShadeController;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.shared.system.DevicePolicyManagerWrapper;
-import com.android.systemui.shared.system.PackageManagerWrapper;
import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.NotificationListener;
-import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
-import com.android.systemui.statusbar.NotificationShadeWindowController;
-import com.android.systemui.statusbar.SmartReplyController;
-import com.android.systemui.statusbar.VibratorHelper;
-import com.android.systemui.statusbar.events.PrivacyDotViewController;
-import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager;
import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
-import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.stack.AmbientState;
import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager;
-import com.android.systemui.statusbar.phone.AutoHideController;
-import com.android.systemui.statusbar.phone.DozeParameters;
-import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
import com.android.systemui.statusbar.phone.LightBarController;
-import com.android.systemui.statusbar.phone.LockscreenGestureLogger;
-import com.android.systemui.statusbar.phone.ManagedProfileController;
import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider;
-import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.statusbar.phone.SystemUIDialogManager;
-import com.android.systemui.statusbar.policy.AccessibilityController;
-import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
import com.android.systemui.statusbar.policy.BluetoothController;
-import com.android.systemui.statusbar.policy.CastController;
-import com.android.systemui.statusbar.policy.DataSaverController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
-import com.android.systemui.statusbar.policy.ExtensionController;
import com.android.systemui.statusbar.policy.FlashlightController;
-import com.android.systemui.statusbar.policy.HotspotController;
-import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.statusbar.policy.LocationController;
-import com.android.systemui.statusbar.policy.NextAlarmController;
-import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler;
-import com.android.systemui.statusbar.policy.RotationLockController;
-import com.android.systemui.statusbar.policy.SecurityController;
-import com.android.systemui.statusbar.policy.SensorPrivacyController;
-import com.android.systemui.statusbar.policy.SmartReplyConstants;
-import com.android.systemui.statusbar.policy.UserInfoController;
-import com.android.systemui.statusbar.policy.UserSwitcherController;
-import com.android.systemui.statusbar.policy.ZenModeController;
-import com.android.systemui.statusbar.window.StatusBarWindowController;
-import com.android.systemui.telephony.TelephonyListenerManager;
import com.android.systemui.tuner.TunablePadding.TunablePaddingService;
import com.android.systemui.tuner.TunerService;
-import com.android.systemui.util.DeviceConfigProxy;
-import com.android.systemui.util.leak.GarbageMonitor;
-import com.android.systemui.util.leak.LeakDetector;
-import com.android.systemui.util.leak.LeakReporter;
-import com.android.systemui.util.sensors.AsyncSensorManager;
import dagger.Lazy;
-import java.util.concurrent.Executor;
import java.util.function.Consumer;
import javax.inject.Inject;
@@ -154,10 +86,6 @@
*/
@SysUISingleton
public class Dependency {
- /**
- * Key for getting a the main looper.
- */
- private static final String MAIN_LOOPER_NAME = "main_looper";
/**
* Key for getting a background Looper for background work.
@@ -171,15 +99,6 @@
* Generic handler on the main thread.
*/
private static final String MAIN_HANDLER_NAME = "main_handler";
- /**
- * Generic executor on the main thread.
- */
- private static final String MAIN_EXECUTOR_NAME = "main_executor";
-
- /**
- * Generic executor on a background thread.
- */
- private static final String BACKGROUND_EXECUTOR_NAME = "background_executor";
/**
* An email address to send memory leak reports to by default.
@@ -197,10 +116,6 @@
*/
public static final DependencyKey<Looper> BG_LOOPER = new DependencyKey<>(BG_LOOPER_NAME);
/**
- * Key for getting a mainer Looper.
- */
- public static final DependencyKey<Looper> MAIN_LOOPER = new DependencyKey<>(MAIN_LOOPER_NAME);
- /**
* Key for getting a Handler for receiving time tick broadcasts on.
*/
public static final DependencyKey<Handler> TIME_TICK_HANDLER =
@@ -211,133 +126,43 @@
public static final DependencyKey<Handler> MAIN_HANDLER =
new DependencyKey<>(MAIN_HANDLER_NAME);
- /**
- * Generic executor on the main thread.
- */
- public static final DependencyKey<Executor> MAIN_EXECUTOR =
- new DependencyKey<>(MAIN_EXECUTOR_NAME);
- /**
- * Generic executor on a background thread.
- */
- public static final DependencyKey<Executor> BACKGROUND_EXECUTOR =
- new DependencyKey<>(BACKGROUND_EXECUTOR_NAME);
-
- /**
- * An email address to send memory leak reports to by default.
- */
- public static final DependencyKey<String> LEAK_REPORT_EMAIL =
- new DependencyKey<>(LEAK_REPORT_EMAIL_NAME);
-
private final ArrayMap<Object, Object> mDependencies = new ArrayMap<>();
private final ArrayMap<Object, LazyDependencyCreator> mProviders = new ArrayMap<>();
@Inject DumpManager mDumpManager;
- @Inject Lazy<ActivityStarter> mActivityStarter;
@Inject Lazy<BroadcastDispatcher> mBroadcastDispatcher;
- @Inject Lazy<AsyncSensorManager> mAsyncSensorManager;
@Inject Lazy<BluetoothController> mBluetoothController;
- @Inject Lazy<LocationController> mLocationController;
- @Inject Lazy<RotationLockController> mRotationLockController;
- @Inject Lazy<ZenModeController> mZenModeController;
- @Inject Lazy<HotspotController> mHotspotController;
- @Inject Lazy<CastController> mCastController;
@Inject Lazy<FlashlightController> mFlashlightController;
- @Inject Lazy<UserSwitcherController> mUserSwitcherController;
- @Inject Lazy<UserInfoController> mUserInfoController;
- @Inject Lazy<KeyguardStateController> mKeyguardMonitor;
@Inject Lazy<KeyguardUpdateMonitor> mKeyguardUpdateMonitor;
- @Inject Lazy<NightDisplayListener> mNightDisplayListener;
- @Inject Lazy<ReduceBrightColorsController> mReduceBrightColorsController;
- @Inject Lazy<ManagedProfileController> mManagedProfileController;
- @Inject Lazy<NextAlarmController> mNextAlarmController;
- @Inject Lazy<DataSaverController> mDataSaverController;
- @Inject Lazy<AccessibilityController> mAccessibilityController;
@Inject Lazy<DeviceProvisionedController> mDeviceProvisionedController;
@Inject Lazy<PluginManager> mPluginManager;
@Inject Lazy<AssistManager> mAssistManager;
- @Inject Lazy<SecurityController> mSecurityController;
- @Inject Lazy<LeakDetector> mLeakDetector;
- @Inject Lazy<LeakReporter> mLeakReporter;
- @Inject Lazy<GarbageMonitor> mGarbageMonitor;
@Inject Lazy<TunerService> mTunerService;
- @Inject Lazy<NotificationShadeWindowController> mNotificationShadeWindowController;
- @Inject Lazy<StatusBarWindowController> mTempStatusBarWindowController;
@Inject Lazy<DarkIconDispatcher> mDarkIconDispatcher;
- @Inject Lazy<StatusBarIconController> mStatusBarIconController;
- @Inject Lazy<ScreenLifecycle> mScreenLifecycle;
- @Inject Lazy<WakefulnessLifecycle> mWakefulnessLifecycle;
@Inject Lazy<FragmentService> mFragmentService;
- @Inject Lazy<ExtensionController> mExtensionController;
- @Inject Lazy<PluginDependencyProvider> mPluginDependencyProvider;
@Nullable
- @Inject Lazy<LocalBluetoothManager> mLocalBluetoothManager;
@Inject Lazy<VolumeDialogController> mVolumeDialogController;
@Inject Lazy<MetricsLogger> mMetricsLogger;
- @Inject Lazy<AccessibilityManagerWrapper> mAccessibilityManagerWrapper;
- @Inject Lazy<SysuiColorExtractor> mSysuiColorExtractor;
@Inject Lazy<TunablePaddingService> mTunablePaddingService;
@Inject Lazy<UiOffloadThread> mUiOffloadThread;
- @Inject Lazy<PowerUI.WarningsUI> mWarningsUI;
@Inject Lazy<LightBarController> mLightBarController;
- @Inject Lazy<IWindowManager> mIWindowManager;
@Inject Lazy<OverviewProxyService> mOverviewProxyService;
@Inject Lazy<NavigationModeController> mNavBarModeController;
@Inject Lazy<AccessibilityButtonModeObserver> mAccessibilityButtonModeObserver;
@Inject Lazy<AccessibilityButtonTargetsObserver> mAccessibilityButtonListController;
- @Inject Lazy<EnhancedEstimates> mEnhancedEstimates;
- @Inject Lazy<VibratorHelper> mVibratorHelper;
@Inject Lazy<IStatusBarService> mIStatusBarService;
- @Inject Lazy<DisplayMetrics> mDisplayMetrics;
- @Inject Lazy<LockscreenGestureLogger> mLockscreenGestureLogger;
- @Inject Lazy<ShadeController> mShadeController;
@Inject Lazy<NotificationRemoteInputManager.Callback> mNotificationRemoteInputManagerCallback;
- @Inject Lazy<AppOpsController> mAppOpsController;
@Inject Lazy<NavigationBarController> mNavigationBarController;
- @Inject Lazy<AccessibilityFloatingMenuController> mAccessibilityFloatingMenuController;
@Inject Lazy<StatusBarStateController> mStatusBarStateController;
- @Inject Lazy<NotificationLockscreenUserManager> mNotificationLockscreenUserManager;
@Inject Lazy<NotificationMediaManager> mNotificationMediaManager;
- @Inject Lazy<NotificationRemoteInputManager> mNotificationRemoteInputManager;
- @Inject Lazy<SmartReplyConstants> mSmartReplyConstants;
- @Inject Lazy<NotificationListener> mNotificationListener;
- @Inject Lazy<NotificationLogger> mNotificationLogger;
- @Inject Lazy<KeyguardDismissUtil> mKeyguardDismissUtil;
- @Inject Lazy<SmartReplyController> mSmartReplyController;
- @Inject Lazy<RemoteInputQuickSettingsDisabler> mRemoteInputQuickSettingsDisabler;
- @Inject Lazy<SensorPrivacyManager> mSensorPrivacyManager;
- @Inject Lazy<AutoHideController> mAutoHideController;
- @Inject Lazy<PrivacyItemController> mPrivacyItemController;
@Inject @Background Lazy<Looper> mBgLooper;
- @Inject @Background Lazy<Handler> mBgHandler;
- @Inject @Main Lazy<Looper> mMainLooper;
@Inject @Main Lazy<Handler> mMainHandler;
@Inject @Named(TIME_TICK_HANDLER_NAME) Lazy<Handler> mTimeTickHandler;
- @Inject @Named(LEAK_REPORT_EMAIL_NAME) Lazy<String> mLeakReportEmail;
- @Inject @Main Lazy<Executor> mMainExecutor;
- @Inject @Background Lazy<Executor> mBackgroundExecutor;
- @Inject Lazy<ActivityManagerWrapper> mActivityManagerWrapper;
- @Inject Lazy<DevicePolicyManagerWrapper> mDevicePolicyManagerWrapper;
- @Inject Lazy<PackageManagerWrapper> mPackageManagerWrapper;
- @Inject Lazy<SensorPrivacyController> mSensorPrivacyController;
- @Inject Lazy<DockManager> mDockManager;
- @Inject Lazy<INotificationManager> mINotificationManager;
@Inject Lazy<SysUiState> mSysUiStateFlagsContainer;
- @Inject Lazy<AlarmManager> mAlarmManager;
- @Inject Lazy<KeyguardSecurityModel> mKeyguardSecurityModel;
- @Inject Lazy<DozeParameters> mDozeParameters;
- @Inject Lazy<IWallpaperManager> mWallpaperManager;
@Inject Lazy<CommandQueue> mCommandQueue;
- @Inject Lazy<RecordingController> mRecordingController;
- @Inject Lazy<MediaOutputDialogFactory> mMediaOutputDialogFactory;
- @Inject Lazy<DeviceConfigProxy> mDeviceConfigProxy;
- @Inject Lazy<TelephonyListenerManager> mTelephonyListenerManager;
- @Inject Lazy<SystemStatusAnimationScheduler> mSystemStatusAnimationSchedulerLazy;
- @Inject Lazy<PrivacyDotViewController> mPrivacyDotViewControllerLazy;
- @Inject Lazy<EdgeBackGestureHandler.Factory> mEdgeBackGestureHandlerFactoryLazy;
@Inject Lazy<UiEventLogger> mUiEventLogger;
@Inject Lazy<StatusBarContentInsetsProvider> mContentInsetsProviderLazy;
- @Inject Lazy<InternetDialogFactory> mInternetDialogFactory;
@Inject Lazy<FeatureFlags> mFeatureFlagsLazy;
@Inject Lazy<NotificationSectionsManager> mNotificationSectionsManagerLazy;
@Inject Lazy<ScreenOffAnimationController> mScreenOffAnimationController;
@@ -360,183 +185,36 @@
// on imports.
mProviders.put(TIME_TICK_HANDLER, mTimeTickHandler::get);
mProviders.put(BG_LOOPER, mBgLooper::get);
- mProviders.put(MAIN_LOOPER, mMainLooper::get);
mProviders.put(MAIN_HANDLER, mMainHandler::get);
- mProviders.put(MAIN_EXECUTOR, mMainExecutor::get);
- mProviders.put(BACKGROUND_EXECUTOR, mBackgroundExecutor::get);
- mProviders.put(ActivityStarter.class, mActivityStarter::get);
mProviders.put(BroadcastDispatcher.class, mBroadcastDispatcher::get);
-
- mProviders.put(AsyncSensorManager.class, mAsyncSensorManager::get);
-
mProviders.put(BluetoothController.class, mBluetoothController::get);
- mProviders.put(SensorPrivacyManager.class, mSensorPrivacyManager::get);
-
- mProviders.put(LocationController.class, mLocationController::get);
-
- mProviders.put(RotationLockController.class, mRotationLockController::get);
-
- mProviders.put(ZenModeController.class, mZenModeController::get);
-
- mProviders.put(HotspotController.class, mHotspotController::get);
-
- mProviders.put(CastController.class, mCastController::get);
-
mProviders.put(FlashlightController.class, mFlashlightController::get);
-
- mProviders.put(KeyguardStateController.class, mKeyguardMonitor::get);
-
mProviders.put(KeyguardUpdateMonitor.class, mKeyguardUpdateMonitor::get);
-
- mProviders.put(UserSwitcherController.class, mUserSwitcherController::get);
-
- mProviders.put(UserInfoController.class, mUserInfoController::get);
-
- mProviders.put(NightDisplayListener.class, mNightDisplayListener::get);
-
- mProviders.put(ReduceBrightColorsController.class, mReduceBrightColorsController::get);
-
- mProviders.put(ManagedProfileController.class, mManagedProfileController::get);
-
- mProviders.put(NextAlarmController.class, mNextAlarmController::get);
-
- mProviders.put(DataSaverController.class, mDataSaverController::get);
-
- mProviders.put(AccessibilityController.class, mAccessibilityController::get);
-
mProviders.put(DeviceProvisionedController.class, mDeviceProvisionedController::get);
-
mProviders.put(PluginManager.class, mPluginManager::get);
-
mProviders.put(AssistManager.class, mAssistManager::get);
-
- mProviders.put(SecurityController.class, mSecurityController::get);
-
- mProviders.put(LeakDetector.class, mLeakDetector::get);
-
- mProviders.put(LEAK_REPORT_EMAIL, mLeakReportEmail::get);
-
- mProviders.put(LeakReporter.class, mLeakReporter::get);
-
- mProviders.put(GarbageMonitor.class, mGarbageMonitor::get);
-
mProviders.put(TunerService.class, mTunerService::get);
-
- mProviders.put(NotificationShadeWindowController.class,
- mNotificationShadeWindowController::get);
-
- mProviders.put(StatusBarWindowController.class, mTempStatusBarWindowController::get);
-
mProviders.put(DarkIconDispatcher.class, mDarkIconDispatcher::get);
-
- mProviders.put(StatusBarIconController.class, mStatusBarIconController::get);
-
- mProviders.put(ScreenLifecycle.class, mScreenLifecycle::get);
-
- mProviders.put(WakefulnessLifecycle.class, mWakefulnessLifecycle::get);
-
mProviders.put(FragmentService.class, mFragmentService::get);
-
- mProviders.put(ExtensionController.class, mExtensionController::get);
-
- mProviders.put(PluginDependencyProvider.class, mPluginDependencyProvider::get);
-
- mProviders.put(LocalBluetoothManager.class, mLocalBluetoothManager::get);
-
mProviders.put(VolumeDialogController.class, mVolumeDialogController::get);
-
mProviders.put(MetricsLogger.class, mMetricsLogger::get);
-
- mProviders.put(AccessibilityManagerWrapper.class, mAccessibilityManagerWrapper::get);
-
- mProviders.put(SysuiColorExtractor.class, mSysuiColorExtractor::get);
-
mProviders.put(TunablePaddingService.class, mTunablePaddingService::get);
-
mProviders.put(UiOffloadThread.class, mUiOffloadThread::get);
-
- mProviders.put(PowerUI.WarningsUI.class, mWarningsUI::get);
-
mProviders.put(LightBarController.class, mLightBarController::get);
-
- mProviders.put(IWindowManager.class, mIWindowManager::get);
-
mProviders.put(OverviewProxyService.class, mOverviewProxyService::get);
-
mProviders.put(NavigationModeController.class, mNavBarModeController::get);
-
mProviders.put(AccessibilityButtonModeObserver.class,
mAccessibilityButtonModeObserver::get);
mProviders.put(AccessibilityButtonTargetsObserver.class,
mAccessibilityButtonListController::get);
-
- mProviders.put(EnhancedEstimates.class, mEnhancedEstimates::get);
-
- mProviders.put(VibratorHelper.class, mVibratorHelper::get);
-
mProviders.put(IStatusBarService.class, mIStatusBarService::get);
-
- mProviders.put(DisplayMetrics.class, mDisplayMetrics::get);
-
- mProviders.put(LockscreenGestureLogger.class, mLockscreenGestureLogger::get);
-
- mProviders.put(ShadeController.class, mShadeController::get);
-
mProviders.put(NotificationRemoteInputManager.Callback.class,
mNotificationRemoteInputManagerCallback::get);
-
- mProviders.put(AppOpsController.class, mAppOpsController::get);
-
mProviders.put(NavigationBarController.class, mNavigationBarController::get);
-
- mProviders.put(AccessibilityFloatingMenuController.class,
- mAccessibilityFloatingMenuController::get);
-
mProviders.put(StatusBarStateController.class, mStatusBarStateController::get);
- mProviders.put(NotificationLockscreenUserManager.class,
- mNotificationLockscreenUserManager::get);
mProviders.put(NotificationMediaManager.class, mNotificationMediaManager::get);
- mProviders.put(NotificationRemoteInputManager.class,
- mNotificationRemoteInputManager::get);
- mProviders.put(SmartReplyConstants.class, mSmartReplyConstants::get);
- mProviders.put(NotificationListener.class, mNotificationListener::get);
- mProviders.put(NotificationLogger.class, mNotificationLogger::get);
- mProviders.put(KeyguardDismissUtil.class, mKeyguardDismissUtil::get);
- mProviders.put(SmartReplyController.class, mSmartReplyController::get);
- mProviders.put(RemoteInputQuickSettingsDisabler.class,
- mRemoteInputQuickSettingsDisabler::get);
- mProviders.put(PrivacyItemController.class, mPrivacyItemController::get);
- mProviders.put(ActivityManagerWrapper.class, mActivityManagerWrapper::get);
- mProviders.put(DevicePolicyManagerWrapper.class, mDevicePolicyManagerWrapper::get);
- mProviders.put(PackageManagerWrapper.class, mPackageManagerWrapper::get);
- mProviders.put(SensorPrivacyController.class, mSensorPrivacyController::get);
- mProviders.put(DockManager.class, mDockManager::get);
- mProviders.put(INotificationManager.class, mINotificationManager::get);
mProviders.put(SysUiState.class, mSysUiStateFlagsContainer::get);
- mProviders.put(AlarmManager.class, mAlarmManager::get);
- mProviders.put(KeyguardSecurityModel.class, mKeyguardSecurityModel::get);
- mProviders.put(DozeParameters.class, mDozeParameters::get);
- mProviders.put(IWallpaperManager.class, mWallpaperManager::get);
mProviders.put(CommandQueue.class, mCommandQueue::get);
- mProviders.put(DeviceConfigProxy.class, mDeviceConfigProxy::get);
- mProviders.put(TelephonyListenerManager.class, mTelephonyListenerManager::get);
-
- // TODO(b/118592525): to support multi-display , we start to add something which is
- // per-display, while others may be global. I think it's time to add
- // a new class maybe named DisplayDependency to solve per-display
- // Dependency problem.
- mProviders.put(AutoHideController.class, mAutoHideController::get);
-
- mProviders.put(RecordingController.class, mRecordingController::get);
-
- mProviders.put(MediaOutputDialogFactory.class, mMediaOutputDialogFactory::get);
-
- mProviders.put(SystemStatusAnimationScheduler.class,
- mSystemStatusAnimationSchedulerLazy::get);
- mProviders.put(PrivacyDotViewController.class, mPrivacyDotViewControllerLazy::get);
- mProviders.put(InternetDialogFactory.class, mInternetDialogFactory::get);
- mProviders.put(EdgeBackGestureHandler.Factory.class,
- mEdgeBackGestureHandlerFactoryLazy::get);
mProviders.put(UiEventLogger.class, mUiEventLogger::get);
mProviders.put(FeatureFlags.class, mFeatureFlagsLazy::get);
mProviders.put(StatusBarContentInsetsProvider.class, mContentInsetsProviderLazy::get);
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index 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/SideFpsController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt
index c1f6259..40f229b 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt
@@ -41,6 +41,8 @@
import android.view.Surface
import android.view.View
import android.view.View.AccessibilityDelegate
+import android.view.View.INVISIBLE
+import android.view.View.VISIBLE
import android.view.ViewPropertyAnimator
import android.view.WindowManager
import android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION
@@ -54,13 +56,13 @@
import com.android.internal.annotations.VisibleForTesting
import com.android.keyguard.KeyguardPINView
import com.android.systemui.Dumpable
-import com.android.systemui.res.R
import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor
import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
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.res.R
import com.android.systemui.util.boundsOnScreen
import com.android.systemui.util.concurrency.DelayableExecutor
import com.android.systemui.util.traceSection
@@ -229,6 +231,20 @@
}
}
+ /** Hide the arrow indicator. */
+ fun hideIndicator() {
+ val lottieAnimationView =
+ overlayView?.findViewById(R.id.sidefps_animation) as LottieAnimationView?
+ lottieAnimationView?.visibility = INVISIBLE
+ }
+
+ /** Show the arrow indicator. */
+ fun showIndicator() {
+ val lottieAnimationView =
+ overlayView?.findViewById(R.id.sidefps_animation) as LottieAnimationView?
+ lottieAnimationView?.visibility = VISIBLE
+ }
+
override fun dump(pw: PrintWriter, args: Array<out String>) {
pw.println("requests:")
for (requestSource in requests) {
@@ -247,6 +263,10 @@
pw.println(" displayId=${displayInfo.uniqueId}")
pw.println(" sensorType=${sensorProps?.sensorType}")
pw.println(" location=${sensorProps?.getLocation(displayInfo.uniqueId)}")
+ pw.println("lottieAnimationView:")
+ pw.println(
+ " visibility=${overlayView?.findViewById<View>(R.id.sidefps_animation)?.visibility}"
+ )
pw.println("overlayOffsets=$overlayOffsets")
pw.println("isReverseDefaultRotation=$isReverseDefaultRotation")
@@ -498,5 +518,5 @@
AUTO_SHOW,
/** Pin, pattern or password bouncer */
PRIMARY_BOUNCER,
- ALTERNATE_BOUNCER
+ ALTERNATE_BOUNCER,
}
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/communal/dagger/CommunalModule.kt b/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.kt
new file mode 100644
index 0000000..b8e2de4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.dagger
+
+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 dagger.Module
+
+@Module(
+ includes =
+ [
+ CommunalRepositoryModule::class,
+ CommunalTutorialRepositoryModule::class,
+ CommunalWidgetRepositoryModule::class,
+ ]
+)
+class CommunalModule
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/model/CommunalWidgetMetadata.kt b/packages/SystemUI/src/com/android/systemui/communal/data/model/CommunalWidgetMetadata.kt
new file mode 100644
index 0000000..f9c4f29
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/model/CommunalWidgetMetadata.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.data.model
+
+import com.android.systemui.communal.shared.CommunalContentSize
+
+/** Metadata for the default widgets */
+data class CommunalWidgetMetadata(
+ /* Widget provider component name */
+ val componentName: String,
+
+ /* Defines the order in which the widget will be rendered in the grid. */
+ val priority: Int,
+
+ /* Supported sizes */
+ val sizes: List<CommunalContentSize>
+)
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/data/repository/CommunalWidgetRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt
index e2a7d07..f13b62f 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt
@@ -27,13 +27,17 @@
import android.os.UserManager
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
+import com.android.systemui.communal.data.model.CommunalWidgetMetadata
import com.android.systemui.communal.shared.CommunalAppWidgetInfo
+import com.android.systemui.communal.shared.CommunalContentSize
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.core.Logger
import com.android.systemui.log.dagger.CommunalLog
+import com.android.systemui.res.R
import com.android.systemui.settings.UserTracker
import javax.inject.Inject
import kotlinx.coroutines.channels.awaitClose
@@ -45,15 +49,20 @@
interface CommunalWidgetRepository {
/** A flow of provider info for the stopwatch widget, or null if widget is unavailable. */
val stopwatchAppWidgetInfo: Flow<CommunalAppWidgetInfo?>
+
+ /** Widgets that are allowed to render in the glanceable hub */
+ val communalWidgetAllowlist: List<CommunalWidgetMetadata>
}
@SysUISingleton
class CommunalWidgetRepositoryImpl
@Inject
constructor(
+ @Application private val applicationContext: Context,
private val appWidgetManager: AppWidgetManager,
private val appWidgetHost: AppWidgetHost,
broadcastDispatcher: BroadcastDispatcher,
+ communalRepository: CommunalRepository,
private val packageManager: PackageManager,
private val userManager: UserManager,
private val userTracker: UserTracker,
@@ -64,12 +73,18 @@
const val TAG = "CommunalWidgetRepository"
const val WIDGET_LABEL = "Stopwatch"
}
+ override val communalWidgetAllowlist: List<CommunalWidgetMetadata>
private val logger = Logger(logBuffer, TAG)
// Whether the [AppWidgetHost] is listening for updates.
private var isHostListening = false
+ init {
+ communalWidgetAllowlist =
+ if (communalRepository.isCommunalEnabled) getWidgetAllowlist() else emptyList()
+ }
+
// Widgets that should be rendered in communal mode.
private val widgets: HashMap<Int, CommunalAppWidgetInfo> = hashMapOf()
@@ -129,6 +144,18 @@
return@map addWidget(providerInfo)
}
+ private fun getWidgetAllowlist(): List<CommunalWidgetMetadata> {
+ val componentNames =
+ applicationContext.resources.getStringArray(R.array.config_communalWidgetAllowlist)
+ return componentNames.mapIndexed { index, name ->
+ CommunalWidgetMetadata(
+ componentName = name,
+ priority = componentNames.size - index,
+ sizes = listOf(CommunalContentSize.HALF)
+ )
+ }
+ }
+
private fun startListening() {
if (isHostListening) {
return
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/shared/CommunalContentSize.kt b/packages/SystemUI/src/com/android/systemui/communal/shared/CommunalContentSize.kt
new file mode 100644
index 0000000..0bd7d86
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/shared/CommunalContentSize.kt
@@ -0,0 +1,8 @@
+package com.android.systemui.communal.shared
+
+/** Supported sizes for communal content in the layout grid. */
+enum class CommunalContentSize {
+ FULL,
+ HALF,
+ THIRD,
+}
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..ad02f62
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/sections/DefaultCommunalHubSection.kt
@@ -0,0 +1,64 @@
+package com.android.systemui.communal.ui.view.layout.sections
+
+import androidx.constraintlayout.widget.ConstraintLayout
+import androidx.constraintlayout.widget.ConstraintSet
+import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
+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(
+ private val viewModel: CommunalViewModel,
+) : KeyguardSection() {
+ private val communalHubViewId = R.id.communal_hub
+
+ override fun addViews(constraintLayout: ConstraintLayout) {
+ constraintLayout.addView(
+ ComposeFacade.createCommunalView(
+ context = constraintLayout.context,
+ viewModel = viewModel,
+ )
+ .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/communal/ui/viewmodel/CommunalViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
new file mode 100644
index 0000000..ddeb1d6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 com.android.systemui.dagger.SysUISingleton
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+
+@SysUISingleton
+class CommunalViewModel
+@Inject
+constructor(
+ tutorialInteractor: CommunalTutorialInteractor,
+) {
+ /** Whether communal hub should show tutorial content. */
+ val showTutorialContent: Flow<Boolean> = tutorialInteractor.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..9221832b 100644
--- a/packages/SystemUI/src/com/android/systemui/compose/BaseComposeFacade.kt
+++ b/packages/SystemUI/src/com/android/systemui/compose/BaseComposeFacade.kt
@@ -22,6 +22,7 @@
import android.view.WindowInsets
import androidx.activity.ComponentActivity
import androidx.lifecycle.LifecycleOwner
+import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
import com.android.systemui.people.ui.viewmodel.PeopleViewModel
import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel
import com.android.systemui.scene.shared.model.Scene
@@ -72,4 +73,10 @@
windowInsets: StateFlow<WindowInsets?>,
sceneByKey: Map<SceneKey, Scene>,
): View
+
+ /** Create a [View] to represent [viewModel] on screen. */
+ fun createCommunalView(
+ context: Context,
+ viewModel: CommunalViewModel,
+ ): 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/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index 4b6ad6d..f0d7592 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;
@@ -43,6 +42,7 @@
import com.android.systemui.classifier.FalsingModule;
import com.android.systemui.clipboardoverlay.dagger.ClipboardOverlayModule;
import com.android.systemui.common.ui.data.repository.CommonRepositoryModule;
+import com.android.systemui.communal.dagger.CommunalModule;
import com.android.systemui.complication.dagger.ComplicationComponent;
import com.android.systemui.controls.dagger.ControlsModule;
import com.android.systemui.dagger.qualifiers.Main;
@@ -56,6 +56,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;
@@ -170,6 +171,7 @@
ClipboardOverlayModule.class,
ClockRegistryModule.class,
CommonRepositoryModule.class,
+ CommunalModule.class,
ConnectivityModule.class,
ControlsModule.class,
CoroutinesModule.class,
@@ -181,6 +183,7 @@
FalsingModule.class,
FlagsModule.class,
FooterActionsModule.class,
+ KeyEventRepositoryModule.class,
KeyboardModule.class,
KeyguardBlueprintModule.class,
LetterboxModule.class,
@@ -298,9 +301,6 @@
abstract UdfpsDisplayModeProvider optionalUdfpsDisplayModeProvider();
@BindsOptionalOf
- abstract AlternateUdfpsTouchProvider optionalUdfpsTouchProvider();
-
- @BindsOptionalOf
abstract FingerprintInteractiveToAuthProvider optionalFingerprintInteractiveToAuthProvider();
@BindsOptionalOf
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 6bbd40c..1c2ff4b 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsClassicDebug.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsClassicDebug.java
@@ -31,12 +31,12 @@
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;
import androidx.annotation.Nullable;
+import com.android.systemui.FeatureFlags;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.util.settings.GlobalSettings;
@@ -56,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
@@ -81,6 +81,7 @@
private final Map<String, Boolean> mBooleanFlagCache = new ConcurrentHashMap<>();
private final Map<String, String> mStringFlagCache = new ConcurrentHashMap<>();
private final Map<String, Integer> mIntFlagCache = new ConcurrentHashMap<>();
+ private final FeatureFlags mGantryFlags;
private final Restarter mRestarter;
private final ServerFlagReader.ChangeListener mOnPropertiesChanged =
@@ -125,6 +126,7 @@
@Main Resources resources,
ServerFlagReader serverFlagReader,
@Named(ALL_FLAGS) Map<String, Flag<?>> allFlags,
+ FeatureFlags gantryFlags,
Restarter restarter) {
mFlagManager = flagManager;
mContext = context;
@@ -133,6 +135,7 @@
mSystemProperties = systemProperties;
mServerFlagReader = serverFlagReader;
mAllFlags = allFlags;
+ mGantryFlags = gantryFlags;
mRestarter = restarter;
}
@@ -260,9 +263,8 @@
if (!hasServerOverride
&& !defaultValue
&& result == null
- && !flag.getName().equals(Flags.TEAMFOOD.getName())
&& flag.getTeamfood()) {
- return isEnabled(Flags.TEAMFOOD);
+ return mGantryFlags.sysuiTeamfood();
}
return result == null ? mServerFlagReader.readServerOverride(
@@ -319,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) {
@@ -354,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);
}
@@ -537,7 +537,7 @@
@Override
public void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
pw.println("can override: true");
-
+ pw.println("teamfood: " + mGantryFlags.sysuiTeamfood());
pw.println("booleans: " + mBooleanFlagCache.size());
// Sort our flags for dumping
TreeMap<String, Boolean> dumpBooleanMap = new TreeMap<>(mBooleanFlagCache);
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index f6f24e0..081618e 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -36,7 +36,10 @@
* See [FeatureFlagsClassicDebug] for instructions on flipping the flags via adb.
*/
object Flags {
- @JvmField val TEAMFOOD = unreleasedFlag("teamfood")
+ // IGNORE ME!
+ // Because flags are static, we need an ever-present flag to reference in some of the internal
+ // code that ensure that other flags are referenced and available.
+ @JvmField val NULL_FLAG = unreleasedFlag("null_flag")
// 100 - notification
// TODO(b/297792660): Tracking Bug
@@ -401,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")
@@ -473,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")
@@ -494,10 +503,9 @@
@Keep
@JvmField
val WM_ENABLE_PARTIAL_SCREEN_SHARING =
- unreleasedFlag(
- name = "record_task_content",
+ releasedFlag(
+ name = "enable_record_task_content",
namespace = DeviceConfig.NAMESPACE_WINDOW_MANAGER,
- teamfood = true
)
// TODO(b/254512674): Tracking Bug
@@ -617,7 +625,7 @@
/** TODO(b/295143676): Tracking bug. When enable, captures a screenshot for each display. */
@JvmField
- val MULTI_DISPLAY_SCREENSHOT = unreleasedFlag("multi_display_screenshot")
+ val MULTI_DISPLAY_SCREENSHOT = unreleasedFlag("multi_display_screenshot", teamfood = true)
// 1400 - columbus
// TODO(b/254512756): Tracking Bug
@@ -667,8 +675,6 @@
@JvmField val NOTE_TASKS = releasedFlag("keycode_flag")
// 2200 - biometrics (udfps, sfps, BiometricPrompt, etc.)
- // TODO(b/259264861): Tracking Bug
- @JvmField val UDFPS_NEW_TOUCH_DETECTION = releasedFlag("udfps_new_touch_detection")
// 2300 - stylus
@JvmField val TRACK_STYLUS_EVER_USED = releasedFlag("track_stylus_ever_used")
@@ -786,8 +792,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/RefactorFlag.kt b/packages/SystemUI/src/com/android/systemui/flags/RefactorFlag.kt
new file mode 100644
index 0000000..7ccc26c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/flags/RefactorFlag.kt
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.flags
+
+import android.util.Log
+import com.android.systemui.Dependency
+
+/**
+ * This class promotes best practices for flag guarding System UI view refactors.
+ * * [isEnabled] allows changing an implementation.
+ * * [assertInLegacyMode] allows authors to flag code as being "dead" when the flag gets enabled and
+ * ensure that it is not being invoked accidentally in the post-flag refactor.
+ * * [isUnexpectedlyInLegacyMode] allows authors to guard new code with a "safe" alternative when
+ * invoked on flag-disabled builds, but with a check that should crash eng builds or tests when
+ * the expectation is violated.
+ *
+ * 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.
+ */
+class RefactorFlag
+private constructor(
+ private val injectedFlags: FeatureFlags?,
+ private val flagName: Any,
+ private val readFlagValue: (FeatureFlags) -> Boolean
+) {
+ constructor(
+ flags: FeatureFlags,
+ flag: UnreleasedFlag
+ ) : 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 {
+ @Suppress("DEPRECATION")
+ val featureFlags = injectedFlags ?: Dependency.get(FeatureFlags::class.java)
+ readFlagValue(featureFlags)
+ }
+
+ /**
+ * Called to ensure code is only run when the flag is disabled. This will throw an exception if
+ * the flag is enabled to ensure that the refactor author catches issues in testing.
+ *
+ * Example usage:
+ * ```
+ * public void setController(NotificationShelfController notificationShelfController) {
+ * mShelfRefactor.assertInLegacyMode();
+ * mController = notificationShelfController;
+ * }
+ * ````
+ */
+ fun assertInLegacyMode() =
+ check(!isEnabled) { "Legacy 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
+ * unintended behaviors caused by accidentally running new logic, while also crashing on an eng
+ * build to ensure that the refactor author catches issues in testing.
+ *
+ * Example usage:
+ * ```
+ * public void setShelfIcons(NotificationIconContainer icons) {
+ * if (mShelfRefactor.isUnexpectedlyInLegacyMode()) return;
+ * mShelfIcons = icons;
+ * }
+ * ```
+ */
+ fun isUnexpectedlyInLegacyMode(): Boolean {
+ if (!isEnabled) {
+ val message = "New code path expects $flagName to be enabled."
+ Log.wtf(TAG, message, Exception(message))
+ }
+ return !isEnabled
+ }
+
+ 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/flags/ViewRefactorFlag.kt b/packages/SystemUI/src/com/android/systemui/flags/ViewRefactorFlag.kt
deleted file mode 100644
index eaecda5..0000000
--- a/packages/SystemUI/src/com/android/systemui/flags/ViewRefactorFlag.kt
+++ /dev/null
@@ -1,99 +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.flags
-
-import android.util.Log
-import com.android.systemui.Dependency
-
-/**
- * This class promotes best practices for flag guarding System UI view refactors.
- * * [isEnabled] allows changing an implementation.
- * * [assertDisabled] allows authors to flag code as being "dead" when the flag gets enabled and
- * ensure that it is not being invoked accidentally in the post-flag refactor.
- * * [expectEnabled] allows authors to guard new code with a "safe" alternative when invoked on
- * 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,
- * 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.
- */
-class ViewRefactorFlag
-private constructor(
- private val injectedFlags: FeatureFlags?,
- private val flag: BooleanFlag,
- private val readFlagValue: (FeatureFlags) -> Boolean
-) {
- @JvmOverloads
- constructor(
- flags: FeatureFlags? = null,
- flag: UnreleasedFlag
- ) : this(flags, flag, { it.isEnabled(flag) })
-
- @JvmOverloads
- constructor(
- flags: FeatureFlags? = null,
- 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 {
- @Suppress("DEPRECATION")
- val featureFlags = injectedFlags ?: Dependency.get(FeatureFlags::class.java)
- readFlagValue(featureFlags)
- }
-
- /**
- * Called to ensure code is only run when the flag is disabled. This will throw an exception if
- * the flag is enabled to ensure that the refactor author catches issues in testing.
- *
- * Example usage:
- * ```
- * public void setController(NotificationShelfController notificationShelfController) {
- * mShelfRefactor.assertDisabled();
- * mController = notificationShelfController;
- * }
- * ````
- */
- fun assertDisabled() = check(!isEnabled) { "Code path not supported when $flag is enabled." }
-
- /**
- * Called to ensure code is only run when the flag is enabled. This protects users from the
- * unintended behaviors caused by accidentally running new logic, while also crashing on an eng
- * build to ensure that the refactor author catches issues in testing.
- *
- * Example usage:
- * ```
- * public void setShelfIcons(NotificationIconContainer icons) {
- * if (mShelfRefactor.expectEnabled()) {
- * mShelfIcons = icons;
- * }
- * }
- * ```
- */
- fun expectEnabled(): Boolean {
- if (!isEnabled) {
- val message = "Code path not supported when $flag is disabled."
- Log.wtf(TAG, message, Exception(message))
- }
- return isEnabled
- }
-
- private companion object {
- private const val TAG = "ViewRefactorFlag"
- }
-}
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/haptics/slider/SliderHapticFeedbackConfig.kt b/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderHapticFeedbackConfig.kt
index 20d99d1..7b33e11 100644
--- a/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderHapticFeedbackConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderHapticFeedbackConfig.kt
@@ -32,6 +32,8 @@
@FloatRange(from = 0.0, to = 1.0) val additionalVelocityMaxBump: Float = 0.15f,
/** Additional time delta to wait between drag texture vibrations */
@FloatRange(from = 0.0) val deltaMillisForDragInterval: Float = 0f,
+ /** Progress threshold beyond which a new drag texture is delivered */
+ @FloatRange(from = 0.0, to = 1.0) val deltaProgressForDragThreshold: Float = 0.015f,
/** Number of low ticks in a drag texture composition. This is not expected to change */
val numberOfLowTicks: Int = 5,
/** Maximum velocity allowed for vibration scaling. This is not expected to change. */
diff --git a/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderHapticFeedbackProvider.kt b/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderHapticFeedbackProvider.kt
index e6de156..f313fb3 100644
--- a/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderHapticFeedbackProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderHapticFeedbackProvider.kt
@@ -46,6 +46,8 @@
private val positionAccelerateInterpolator =
AccelerateInterpolator(config.progressInterpolatorFactor)
private var dragTextureLastTime = clock.elapsedRealtime()
+ var dragTextureLastProgress = -1f
+ private set
private val lowTickDurationMs =
vibratorHelper.getPrimitiveDurations(VibrationEffect.Composition.PRIMITIVE_LOW_TICK)[0]
private var hasVibratedAtLowerBookend = false
@@ -91,6 +93,9 @@
val elapsedSinceLastDrag = currentTime - dragTextureLastTime
if (elapsedSinceLastDrag < thresholdUntilNextDragCallMillis) return
+ val deltaProgress = abs(normalizedSliderProgress - dragTextureLastProgress)
+ if (deltaProgress < config.deltaProgressForDragThreshold) return
+
val velocityInterpolated =
velocityAccelerateInterpolator.getInterpolation(
min(absoluteVelocity / config.maxVelocityToScale, 1f)
@@ -116,11 +121,14 @@
}
vibratorHelper.vibrate(composition.compose(), VIBRATION_ATTRIBUTES_PIPELINING)
dragTextureLastTime = currentTime
+ dragTextureLastProgress = normalizedSliderProgress
}
override fun onHandleAcquiredByTouch() {}
- override fun onHandleReleasedFromTouch() {}
+ override fun onHandleReleasedFromTouch() {
+ dragTextureLastProgress = -1f
+ }
override fun onLowerBookend() {
if (!hasVibratedAtLowerBookend) {
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 3f2f67d..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 86bf368..a511713 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
@@ -20,6 +20,7 @@
import android.content.Context
import android.view.LayoutInflater
import android.view.View
+import com.android.internal.jank.InteractionJankMonitor
import com.android.keyguard.KeyguardStatusView
import com.android.keyguard.KeyguardStatusViewController
import com.android.keyguard.LockIconView
@@ -71,6 +72,7 @@
private val keyguardIndicationController: KeyguardIndicationController,
private val lockIconViewController: LockIconViewController,
private val shadeInteractor: ShadeInteractor,
+ private val interactionJankMonitor: InteractionJankMonitor
) : CoreStartable {
private var rootViewHandle: DisposableHandle? = null
@@ -140,6 +142,7 @@
keyguardStateController,
shadeInteractor,
{ keyguardStatusViewController!!.getClockController() },
+ interactionJankMonitor,
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
index 41bde91..019d428 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
@@ -38,8 +38,6 @@
import com.android.systemui.broadcast.BroadcastDispatcher;
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.CommunalWidgetRepositoryModule;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dagger.qualifiers.UiBackground;
@@ -95,8 +93,6 @@
KeyguardStatusViewComponent.class,
KeyguardUserSwitcherComponent.class},
includes = {
- CommunalRepositoryModule.class,
- CommunalWidgetRepositoryModule.class,
FalsingModule.class,
KeyguardDataQuickAffordanceModule.class,
KeyguardRepositoryModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepository.kt
index e8740a4..654f2d1 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepository.kt
@@ -90,7 +90,7 @@
* If the current user can use face auth to enter the device. This is true when the user has
* face auth enrolled, and is enabled in settings/device policy.
*/
- val isFaceAuthEnrolledAndEnabled: Flow<Boolean>
+ val isFaceAuthEnrolledAndEnabled: StateFlow<Boolean>
/**
* If the current user can use face auth to enter the device right now. This is true when
@@ -348,10 +348,11 @@
.and(isFingerprintBiometricAllowed)
.stateIn(scope, SharingStarted.Eagerly, false)
- override val isFaceAuthEnrolledAndEnabled: Flow<Boolean> =
+ override val isFaceAuthEnrolledAndEnabled: StateFlow<Boolean> =
isFaceAuthenticationEnabled
.and(isFaceEnrolled)
.and(mobileConnectionsRepository.isAnySimSecure.isFalse())
+ .stateIn(scope, SharingStarted.Eagerly, false)
override val isFaceAuthCurrentlyAllowed: Flow<Boolean> =
isFaceAuthEnrolledAndEnabled
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryModule.kt
index 2f80106..5659623 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryModule.kt
@@ -22,6 +22,7 @@
import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepository
import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepositoryImpl
import com.android.systemui.bouncer.domain.interactor.BouncerMessageAuditLogger
+import com.android.systemui.keyguard.ui.binder.SideFpsProgressBarViewBinder
import dagger.Binds
import dagger.Module
import dagger.multibindings.ClassKey
@@ -32,6 +33,11 @@
@Binds fun keyguardRepository(impl: KeyguardRepositoryImpl): KeyguardRepository
@Binds
+ @IntoMap
+ @ClassKey(SideFpsProgressBarViewBinder::class)
+ fun bindSideFpsProgressBarViewBinder(viewBinder: SideFpsProgressBarViewBinder): CoreStartable
+
+ @Binds
fun keyguardSurfaceBehindRepository(
impl: KeyguardSurfaceBehindRepositoryImpl
): KeyguardSurfaceBehindRepository
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt
index 38eb730..6e0aa4c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt
@@ -26,10 +26,11 @@
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.util.kotlin.Utils.Companion.toTriple
import com.android.systemui.util.kotlin.sample
+import javax.inject.Inject
+import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.launch
-import javax.inject.Inject
@SysUISingleton
class FromAodTransitionInteractor
@@ -86,15 +87,19 @@
}
}
}
-
override fun getDefaultAnimatorForTransitionsToState(toState: KeyguardState): ValueAnimator {
return ValueAnimator().apply {
interpolator = Interpolators.LINEAR
- duration = TRANSITION_DURATION_MS
+ duration =
+ when (toState) {
+ KeyguardState.LOCKSCREEN -> TO_LOCKSCREEN_DURATION
+ else -> DEFAULT_DURATION
+ }.inWholeMilliseconds
}
}
companion object {
- private const val TRANSITION_DURATION_MS = 500L
+ val TO_LOCKSCREEN_DURATION = 500.milliseconds
+ private val DEFAULT_DURATION = 500.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/FromGoneTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt
index ad51e74..c67153a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt
@@ -114,7 +114,8 @@
.collect { (isAsleep, lastStartedStep, isAodAvailable) ->
if (lastStartedStep.to == KeyguardState.GONE && isAsleep) {
startTransitionTo(
- if (isAodAvailable) KeyguardState.AOD else KeyguardState.DOZING
+ if (isAodAvailable) KeyguardState.AOD else KeyguardState.DOZING,
+ resetIfCancelled = true,
)
}
}
@@ -127,6 +128,7 @@
duration =
when (toState) {
KeyguardState.DREAMING -> TO_DREAMING_DURATION
+ KeyguardState.AOD -> TO_AOD_DURATION
else -> DEFAULT_DURATION
}.inWholeMilliseconds
}
@@ -134,5 +136,6 @@
companion object {
private val DEFAULT_DURATION = 500.milliseconds
val TO_DREAMING_DURATION = 933.milliseconds
+ val TO_AOD_DURATION = 1100.milliseconds
}
}
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..c39a4c9 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
@@ -33,6 +33,9 @@
import com.android.systemui.util.kotlin.Utils.Companion.toQuad
import com.android.systemui.util.kotlin.Utils.Companion.toTriple
import com.android.systemui.util.kotlin.sample
+import java.util.UUID
+import javax.inject.Inject
+import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
@@ -40,9 +43,6 @@
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.launch
-import java.util.UUID
-import javax.inject.Inject
-import kotlin.time.Duration.Companion.milliseconds
@SysUISingleton
class FromLockscreenTransitionInteractor
@@ -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)
}
}
@@ -362,6 +355,7 @@
when (toState) {
KeyguardState.DREAMING -> TO_DREAMING_DURATION
KeyguardState.OCCLUDED -> TO_OCCLUDED_DURATION
+ KeyguardState.AOD -> TO_AOD_DURATION
else -> DEFAULT_DURATION
}.inWholeMilliseconds
}
@@ -371,5 +365,6 @@
private val DEFAULT_DURATION = 400.milliseconds
val TO_DREAMING_DURATION = 933.milliseconds
val TO_OCCLUDED_DURATION = 450.milliseconds
+ val TO_AOD_DURATION = 500.milliseconds
}
}
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 89aca76..85b0f4fb 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
@@ -41,6 +41,9 @@
/** Whether face auth is in lock out state. */
fun isLockedOut(): Boolean
+ /** Whether face auth is enrolled and enabled for the current user */
+ fun isFaceAuthEnabledAndEnrolled(): Boolean
+
/**
* Register listener for use from code that cannot use [authenticationStatus] or
* [detectionStatus]
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
index 6e19fdb..b953b48 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
@@ -39,7 +39,6 @@
import com.android.systemui.keyguard.shared.model.DozeStateModel
import com.android.systemui.keyguard.shared.model.DozeStateModel.Companion.isDozeOff
import com.android.systemui.keyguard.shared.model.DozeTransitionModel
-import com.android.systemui.keyguard.shared.model.KeyguardRootViewVisibilityState
import com.android.systemui.keyguard.shared.model.StatusBarState
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.res.R
@@ -49,6 +48,8 @@
import com.android.systemui.shade.data.repository.ShadeRepository
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.util.kotlin.sample
+import javax.inject.Inject
+import javax.inject.Provider
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.delay
@@ -64,8 +65,6 @@
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.merge
import kotlinx.coroutines.flow.onStart
-import javax.inject.Inject
-import javax.inject.Provider
/**
* Encapsulates business-logic related to the keyguard but not to a more specific part within it.
@@ -148,9 +147,7 @@
.combine(dozeTransitionModel) { isDreaming, dozeTransitionModel ->
isDreaming && isDozeOff(dozeTransitionModel.to)
}
- .sample(powerInteractor.isAwake) { isAbleToDream, isAwake ->
- isAbleToDream && isAwake
- }
+ .sample(powerInteractor.isAwake) { isAbleToDream, isAwake -> isAbleToDream && isAwake }
.flatMapLatest { isAbleToDream ->
flow {
delay(50)
@@ -217,11 +214,8 @@
val faceSensorLocation: Flow<Point?> = repository.faceSensorLocation
/** Notifies when a new configuration is set */
- val configurationChange: Flow<Unit> = configurationRepository.onAnyConfigurationChange
-
- /** Represents the current state of the KeyguardRootView visibility */
- val keyguardRootViewVisibilityState: Flow<KeyguardRootViewVisibilityState> =
- repository.keyguardRootViewVisibility
+ val configurationChange: Flow<Unit> =
+ configurationRepository.onAnyConfigurationChange.onStart { emit(Unit) }
/** The position of the keyguard clock. */
val clockPosition: Flow<Position> = repository.clockPosition
@@ -235,12 +229,17 @@
R.dimen.keyguard_translate_distance_on_swipe_up
)
shadeRepository.shadeModel.map {
- // On swipe up, translate the keyguard to reveal the bouncer
- MathUtils.lerp(
- translationDistance,
- 0,
- Interpolators.FAST_OUT_LINEAR_IN.getInterpolation(it.expansionAmount)
- )
+ if (it.expansionAmount == 0f) {
+ // Reset the translation value
+ 0f
+ } else {
+ // On swipe up, translate the keyguard to reveal the bouncer
+ MathUtils.lerp(
+ translationDistance,
+ 0,
+ Interpolators.FAST_OUT_LINEAR_IN.getInterpolation(it.expansionAmount)
+ )
+ }
}
}
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 ac012f8..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
@@ -29,17 +29,14 @@
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
import javax.inject.Inject
-import kotlinx.coroutines.ExperimentalCoroutinesApi
/** Handles key events arriving when the keyguard is showing or device is dozing. */
-@ExperimentalCoroutinesApi
@SysUISingleton
class KeyguardKeyEventInteractor
@Inject
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,
@@ -58,11 +55,7 @@
if (event.handleAction()) {
when (event.keyCode) {
KeyEvent.KEYCODE_MENU -> return dispatchMenuKeyEvent()
- KeyEvent.KEYCODE_SPACE,
- KeyEvent.KEYCODE_ENTER ->
- if (isDeviceAwake()) {
- return collapseShadeLockedOrShowPrimaryBouncer()
- }
+ KeyEvent.KEYCODE_SPACE -> return dispatchSpaceEvent()
}
}
return false
@@ -98,24 +91,16 @@
(statusBarStateController.state != StatusBarState.SHADE) &&
statusBarKeyguardViewManager.shouldDismissOnMenuPressed()
if (shouldUnlockOnMenuPressed) {
- return collapseShadeLockedOrShowPrimaryBouncer()
+ shadeController.animateCollapseShadeForced()
+ return true
}
return false
}
- private fun collapseShadeLockedOrShowPrimaryBouncer(): Boolean {
- when (statusBarStateController.state) {
- StatusBarState.SHADE -> return false
- StatusBarState.SHADE_LOCKED -> {
- shadeController.animateCollapseShadeForced()
- return true
- }
- StatusBarState.KEYGUARD -> {
- if (!statusBarKeyguardViewManager.primaryBouncerIsShowing()) {
- statusBarKeyguardViewManager.showPrimaryBouncer(true)
- return true
- }
- }
+ private fun dispatchSpaceEvent(): Boolean {
+ if (isDeviceAwake() && statusBarStateController.state != StatusBarState.SHADE) {
+ shadeController.animateCollapseShadeForced()
+ return true
}
return false
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/NoopKeyguardFaceAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/NoopKeyguardFaceAuthInteractor.kt
index f38bb2b..fbadde6 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
@@ -43,6 +43,7 @@
override fun isLockedOut(): Boolean = false
override fun isEnabled() = false
+ override fun isFaceAuthEnabledAndEnrolled(): Boolean = false
override fun registerListener(listener: FaceAuthenticationListener) {}
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 797dec2..2641846 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
@@ -32,6 +32,7 @@
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository
import com.android.systemui.keyguard.data.repository.DeviceEntryFaceAuthRepository
import com.android.systemui.keyguard.data.repository.DeviceEntryFingerprintAuthRepository
import com.android.systemui.keyguard.shared.model.ErrorFaceAuthenticationStatus
@@ -81,6 +82,7 @@
private val facePropertyRepository: FacePropertyRepository,
private val faceWakeUpTriggersConfig: FaceWakeUpTriggersConfig,
private val powerInteractor: PowerInteractor,
+ private val biometricSettingsRepository: BiometricSettingsRepository,
) : CoreStartable, KeyguardFaceAuthInteractor {
private val listeners: MutableList<FaceAuthenticationListener> = mutableListOf()
@@ -149,7 +151,10 @@
.onEach {
if (it) {
faceAuthenticationLogger.faceLockedOut("Fingerprint locked out")
- repository.setLockedOut(true)
+ // We don't care about this if face auth is not enabled.
+ if (isFaceAuthEnabledAndEnrolled()) {
+ repository.setLockedOut(true)
+ }
}
}
.launchIn(applicationScope)
@@ -263,6 +268,9 @@
}
}
+ override fun isFaceAuthEnabledAndEnrolled(): Boolean =
+ biometricSettingsRepository.isFaceAuthEnrolledAndEnabled.value
+
private fun observeFaceAuthStateUpdates() {
authenticationStatus
.onEach { authStatusUpdate ->
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/FingerprintAuthenticationModels.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/FingerprintAuthenticationModels.kt
index ae18681..3c143fe 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/FingerprintAuthenticationModels.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/FingerprintAuthenticationModels.kt
@@ -16,6 +16,8 @@
package com.android.systemui.keyguard.shared.model
+import android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_GOOD
+import android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_START
import android.hardware.fingerprint.FingerprintManager
import android.os.SystemClock.elapsedRealtime
@@ -39,7 +41,12 @@
/** Fingerprint acquired message. */
data class AcquiredFingerprintAuthenticationStatus(val acquiredInfo: Int) :
- FingerprintAuthenticationStatus()
+ FingerprintAuthenticationStatus() {
+
+ val fingerprintCaptureStarted: Boolean = acquiredInfo == FINGERPRINT_ACQUIRED_START
+
+ val fingerprintCaptureCompleted: Boolean = acquiredInfo == FINGERPRINT_ACQUIRED_GOOD
+}
/** Fingerprint authentication failed message. */
object FailFingerprintAuthenticationStatus : FingerprintAuthenticationStatus()
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/KeyguardRootViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
index ac4ad39..c72e6ce 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
@@ -20,21 +20,24 @@
import android.view.View
import android.view.View.OnLayoutChangeListener
import android.view.ViewGroup
+import android.view.ViewGroup.OnHierarchyChangeListener
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
-import com.android.app.animation.Interpolators
+import com.android.internal.jank.InteractionJankMonitor
+import com.android.internal.jank.InteractionJankMonitor.CUJ_SCREEN_OFF_SHOW_AOD
+import com.android.keyguard.KeyguardClockSwitch.MISSING_CLOCK_ID
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.common.shared.model.Text
import com.android.systemui.common.shared.model.TintedIcon
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
import com.android.systemui.keyguard.ui.viewmodel.OccludingAppDeviceEntryMessageViewModel
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.plugins.ClockController
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.policy.KeyguardStateController
import com.android.systemui.temporarydisplay.ViewPriority
import com.android.systemui.temporarydisplay.chipbar.ChipbarCoordinator
@@ -48,8 +51,6 @@
@ExperimentalCoroutinesApi
object KeyguardRootViewBinder {
- private var onLayoutChangeListener: OnLayoutChange? = null
-
@JvmStatic
fun bind(
view: ViewGroup,
@@ -60,7 +61,14 @@
keyguardStateController: KeyguardStateController,
shadeInteractor: ShadeInteractor,
clockControllerProvider: Provider<ClockController>?,
+ interactionJankMonitor: InteractionJankMonitor?,
): DisposableHandle {
+ var onLayoutChangeListener: OnLayoutChange? = null
+ val childViews = mutableMapOf<Int, View?>()
+ val statusViewId = R.id.keyguard_status_view
+ val burnInLayerId = R.id.burn_in_layer
+ val aodNotificationIconContainerId = R.id.aod_notification_icon_container
+ val largeClockId = R.id.lockscreen_clock_view_large
val disposableHandle =
view.repeatWhenAttached {
repeatOnLifecycle(Lifecycle.State.CREATED) {
@@ -86,36 +94,74 @@
if (featureFlags.isEnabled(Flags.MIGRATE_KEYGUARD_STATUS_VIEW)) {
launch {
- viewModel.translationY.collect { y ->
- val burnInLayer = view.requireViewById<View>(R.id.burn_in_layer)
- burnInLayer.translationY = y
+ viewModel.burnInLayerVisibility.collect { visibility ->
+ childViews[burnInLayerId]?.visibility = visibility
+ // Reset alpha only for the icons, as they currently have their
+ // own animator
+ childViews[aodNotificationIconContainerId]?.alpha = 0f
}
}
- }
- if (featureFlags.isEnabled(Flags.MIGRATE_KEYGUARD_STATUS_VIEW)) {
+ launch {
+ viewModel.burnInLayerAlpha.collect { alpha ->
+ childViews[statusViewId]?.alpha = alpha
+ childViews[aodNotificationIconContainerId]?.alpha = alpha
+ }
+ }
+
+ launch {
+ viewModel.lockscreenStateAlpha.collect { alpha ->
+ childViews[statusViewId]?.alpha = alpha
+ }
+ }
+
+ launch {
+ viewModel.translationY.collect { y ->
+ childViews[burnInLayerId]?.translationY = y
+ }
+ }
+
launch {
viewModel.translationX.collect { x ->
- val burnInLayer = view.requireViewById<View>(R.id.burn_in_layer)
- burnInLayer.translationX = x
+ childViews[burnInLayerId]?.translationX = x
}
}
- }
- if (featureFlags.isEnabled(Flags.MIGRATE_KEYGUARD_STATUS_VIEW)) {
launch {
viewModel.scale.collect { (scale, scaleClockOnly) ->
if (scaleClockOnly) {
- val largeClock =
- view.findViewById<View?>(R.id.lockscreen_clock_view_large)
- largeClock?.let {
+ childViews[largeClockId]?.let {
it.scaleX = scale
it.scaleY = scale
}
} else {
- val burnInLayer = view.requireViewById<View>(R.id.burn_in_layer)
- burnInLayer.scaleX = scale
- burnInLayer.scaleY = scale
+ childViews[burnInLayerId]?.scaleX = scale
+ childViews[burnInLayerId]?.scaleY = scale
+ }
+ }
+ }
+
+ interactionJankMonitor?.let { jankMonitor ->
+ launch {
+ viewModel.goneToAodTransition.collect {
+ when (it.transitionState) {
+ TransitionState.STARTED -> {
+ val clockId =
+ clockControllerProvider?.get()?.config?.id
+ ?: MISSING_CLOCK_ID
+ val builder =
+ InteractionJankMonitor.Configuration.Builder
+ .withView(CUJ_SCREEN_OFF_SHOW_AOD, view)
+ .setTag(clockId)
+
+ jankMonitor.begin(builder)
+ }
+ TransitionState.CANCELED ->
+ jankMonitor.cancel(CUJ_SCREEN_OFF_SHOW_AOD)
+ TransitionState.FINISHED ->
+ jankMonitor.end(CUJ_SCREEN_OFF_SHOW_AOD)
+ TransitionState.RUNNING -> Unit
+ }
}
}
}
@@ -132,54 +178,32 @@
}
}
}
-
- repeatOnLifecycle(Lifecycle.State.STARTED) {
- if (featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) {
- launch {
- viewModel.keyguardRootViewVisibilityState.collect { visibilityState ->
- view.animate().cancel()
- val goingToFullShade = visibilityState.goingToFullShade
- val statusBarState = visibilityState.statusBarState
- val isOcclusionTransitionRunning =
- visibilityState.occlusionTransitionRunning
- if (goingToFullShade) {
- view
- .animate()
- .alpha(0f)
- .setStartDelay(
- keyguardStateController.keyguardFadingAwayDelay
- )
- .setDuration(
- keyguardStateController.shortenedFadingAwayDuration
- )
- .setInterpolator(Interpolators.ALPHA_OUT)
- .withEndAction { view.visibility = View.GONE }
- .start()
- } else if (
- statusBarState == StatusBarState.KEYGUARD ||
- statusBarState == StatusBarState.SHADE_LOCKED
- ) {
- view.visibility = View.VISIBLE
- if (!isOcclusionTransitionRunning) {
- view.alpha = 1f
- }
- } else {
- view.visibility = View.GONE
- }
- }
- }
- }
- }
}
viewModel.clockControllerProvider = clockControllerProvider
onLayoutChangeListener = OnLayoutChange(viewModel)
view.addOnLayoutChangeListener(onLayoutChangeListener)
+ // Views will be added or removed after the call to bind(). This is needed to avoid many
+ // calls to findViewById
+ view.setOnHierarchyChangeListener(
+ object : OnHierarchyChangeListener {
+ override fun onChildViewAdded(parent: View, child: View) {
+ childViews.put(child.id, view)
+ }
+
+ override fun onChildViewRemoved(parent: View, child: View) {
+ childViews.remove(child.id)
+ }
+ }
+ )
+
return object : DisposableHandle {
override fun dispose() {
disposableHandle.dispose()
view.removeOnLayoutChangeListener(onLayoutChangeListener)
+ view.setOnHierarchyChangeListener(null)
+ childViews.clear()
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/PreviewKeyguardBlueprintViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/PreviewKeyguardBlueprintViewBinder.kt
index f3586ba..2feaa2e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/PreviewKeyguardBlueprintViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/PreviewKeyguardBlueprintViewBinder.kt
@@ -24,6 +24,7 @@
import androidx.lifecycle.repeatOnLifecycle
import com.android.systemui.keyguard.ui.viewmodel.KeyguardBlueprintViewModel
import com.android.systemui.lifecycle.repeatWhenAttached
+import kotlinx.coroutines.DisposableHandle
import kotlinx.coroutines.launch
/**
@@ -46,8 +47,8 @@
constraintLayout: ConstraintLayout,
viewModel: KeyguardBlueprintViewModel,
finishedAddViewCallback: () -> Unit
- ) {
- constraintLayout.repeatWhenAttached {
+ ): DisposableHandle {
+ return constraintLayout.repeatWhenAttached {
repeatOnLifecycle(Lifecycle.State.CREATED) {
launch {
viewModel.blueprint.collect { blueprint ->
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/SideFpsProgressBarViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/SideFpsProgressBarViewBinder.kt
new file mode 100644
index 0000000..1acea5c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/SideFpsProgressBarViewBinder.kt
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 com.android.systemui.CoreStartable
+import com.android.systemui.biometrics.SideFpsController
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.keyguard.ui.view.SideFpsProgressBar
+import com.android.systemui.keyguard.ui.viewmodel.SideFpsProgressBarViewModel
+import com.android.systemui.util.kotlin.Quint
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.collectLatest
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.launch
+
+@SysUISingleton
+class SideFpsProgressBarViewBinder
+@Inject
+constructor(
+ private val viewModel: SideFpsProgressBarViewModel,
+ private val view: SideFpsProgressBar,
+ @Application private val applicationScope: CoroutineScope,
+ private val sfpsController: dagger.Lazy<SideFpsController>,
+) : CoreStartable {
+
+ override fun start() {
+ applicationScope.launch {
+ viewModel.isProlongedTouchRequiredForAuthentication.collectLatest { enabled ->
+ if (enabled) {
+ launch {
+ combine(
+ viewModel.isVisible,
+ viewModel.sensorLocation,
+ viewModel.shouldRotate90Degrees,
+ viewModel.isFingerprintAuthRunning,
+ viewModel.sensorWidth,
+ ::Quint
+ )
+ .collectLatest {
+ (visible, location, shouldRotate, fpDetectRunning, sensorWidth) ->
+ view.updateView(visible, location, shouldRotate, sensorWidth)
+ // We have to hide the SFPS indicator as the progress bar will
+ // be shown at the same location
+ if (visible) {
+ sfpsController.get().hideIndicator()
+ } else if (fpDetectRunning) {
+ sfpsController.get().showIndicator()
+ }
+ }
+ }
+ launch { viewModel.progress.collectLatest { view.setProgress(it) } }
+ } else {
+ view.hideOverlay()
+ }
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsKeyguardViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsKeyguardViewBinder.kt
index a0e0da4..475d26f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsKeyguardViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsKeyguardViewBinder.kt
@@ -23,7 +23,6 @@
import androidx.asynclayoutinflater.view.AsyncLayoutInflater
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
-import com.android.systemui.res.R
import com.android.systemui.biometrics.UdfpsKeyguardView
import com.android.systemui.biometrics.ui.binder.UdfpsKeyguardInternalViewBinder
import com.android.systemui.keyguard.ui.viewmodel.BackgroundViewModel
@@ -32,6 +31,7 @@
import com.android.systemui.keyguard.ui.viewmodel.UdfpsKeyguardInternalViewModel
import com.android.systemui.keyguard.ui.viewmodel.UdfpsKeyguardViewModel
import com.android.systemui.lifecycle.repeatWhenAttached
+import com.android.systemui.res.R
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.launch
@@ -52,8 +52,6 @@
fingerprintViewModel: FingerprintViewModel,
backgroundViewModel: BackgroundViewModel,
) {
- view.useExpandedOverlay(viewModel.useExpandedOverlay())
-
val layoutInflaterFinishListener =
AsyncLayoutInflater.OnInflateFinishedListener { inflatedInternalView, _, parent ->
UdfpsKeyguardInternalViewBinder.bind(
@@ -63,25 +61,21 @@
fingerprintViewModel,
backgroundViewModel,
)
- if (viewModel.useExpandedOverlay()) {
- val lp = inflatedInternalView.layoutParams as FrameLayout.LayoutParams
- lp.width = viewModel.sensorBounds.width()
- lp.height = viewModel.sensorBounds.height()
- val relativeToView =
- getBoundsRelativeToView(
- inflatedInternalView,
- RectF(viewModel.sensorBounds),
- )
- lp.setMarginsRelative(
- relativeToView.left.toInt(),
- relativeToView.top.toInt(),
- relativeToView.right.toInt(),
- relativeToView.bottom.toInt(),
+ val lp = inflatedInternalView.layoutParams as FrameLayout.LayoutParams
+ lp.width = viewModel.sensorBounds.width()
+ lp.height = viewModel.sensorBounds.height()
+ val relativeToView =
+ getBoundsRelativeToView(
+ inflatedInternalView,
+ RectF(viewModel.sensorBounds),
)
- parent!!.addView(inflatedInternalView, lp)
- } else {
- parent!!.addView(inflatedInternalView)
- }
+ lp.setMarginsRelative(
+ relativeToView.left.toInt(),
+ relativeToView.top.toInt(),
+ relativeToView.right.toInt(),
+ relativeToView.bottom.toInt(),
+ )
+ parent!!.addView(inflatedInternalView, lp)
}
val inflater = AsyncLayoutInflater(view.context)
inflater.inflate(R.layout.udfps_keyguard_view_internal, view, layoutInflaterFinishListener)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
index c6d8ec7..5a4bbef 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,14 +27,15 @@
import android.os.Bundle
import android.os.Handler
import android.os.IBinder
+import android.view.ContextThemeWrapper
import android.view.Display
import android.view.Display.DEFAULT_DISPLAY
+import android.view.DisplayInfo
import android.view.LayoutInflater
import android.view.SurfaceControlViewHost
import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
-import android.view.WindowManager.LayoutParams.TYPE_KEYGUARD
import android.widget.FrameLayout
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.view.isInvisible
@@ -129,7 +130,7 @@
bundle.getBoolean(ClockPreviewConstants.KEY_HIDE_CLOCK, false)
private val wallpaperColors: WallpaperColors? = bundle.getParcelable(KEY_COLORS)
private val displayId = bundle.getInt(KEY_DISPLAY_ID, DEFAULT_DISPLAY)
- private val display: Display = displayManager.getDisplay(displayId)
+ private val display: Display? = displayManager.getDisplay(displayId)
private var host: SurfaceControlViewHost
@@ -179,7 +180,11 @@
fun render() {
mainHandler.post {
- val previewContext = context.createDisplayContext(display)
+ val previewContext =
+ display?.let {
+ ContextThemeWrapper(context.createDisplayContext(it), context.getTheme())
+ }
+ ?: context
val rootView = FrameLayout(previewContext)
@@ -189,16 +194,18 @@
setUpBottomArea(rootView)
}
- val windowContext = context.createWindowContext(display, TYPE_KEYGUARD, null)
- val windowManagerOfDisplay = windowContext.getSystemService(WindowManager::class.java)
+ var displayInfo: DisplayInfo? = null
+ display?.let {
+ displayInfo = DisplayInfo()
+ it.getDisplayInfo(displayInfo)
+ }
rootView.measure(
View.MeasureSpec.makeMeasureSpec(
- windowManagerOfDisplay?.currentWindowMetrics?.bounds?.width()
- ?: windowManager.currentWindowMetrics.bounds.width(),
+ displayInfo?.logicalWidth ?: windowManager.currentWindowMetrics.bounds.width(),
View.MeasureSpec.EXACTLY
),
View.MeasureSpec.makeMeasureSpec(
- windowManagerOfDisplay?.currentWindowMetrics?.bounds?.height()
+ displayInfo?.logicalHeight
?: windowManager.currentWindowMetrics.bounds.height(),
View.MeasureSpec.EXACTLY
),
@@ -320,7 +327,7 @@
@OptIn(ExperimentalCoroutinesApi::class)
private fun setupKeyguardRootView(previewContext: Context, rootView: FrameLayout) {
- val keyguardRootView = KeyguardRootView(previewContext, null).apply { removeAllViews() }
+ val keyguardRootView = KeyguardRootView(previewContext, null)
disposables.add(
KeyguardRootViewBinder.bind(
keyguardRootView,
@@ -331,6 +338,7 @@
keyguardStateController,
shadeInteractor,
null, // clock provider only needed for burn in
+ null, // jank monitor not required for preview mode
)
)
rootView.addView(
@@ -340,26 +348,29 @@
FrameLayout.LayoutParams.MATCH_PARENT,
),
)
- PreviewKeyguardBlueprintViewBinder.bind(keyguardRootView, keyguardBlueprintViewModel) {
- if (featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) {
- setupShortcuts(keyguardRootView)
- }
- setUpUdfps(previewContext, rootView)
- if (!shouldHideClock) {
- setUpClock(previewContext, rootView)
- KeyguardPreviewClockViewBinder.bind(
- largeClockHostView,
- smallClockHostView,
- clockViewModel,
- )
- }
+ disposables.add(
+ PreviewKeyguardBlueprintViewBinder.bind(keyguardRootView, keyguardBlueprintViewModel) {
+ if (featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) {
+ setupShortcuts(keyguardRootView)
+ }
+ setUpUdfps(previewContext, rootView)
- setUpSmartspace(previewContext, rootView)
- smartSpaceView?.let {
- KeyguardPreviewSmartspaceViewBinder.bind(it, smartspaceViewModel)
+ if (!shouldHideClock) {
+ setUpClock(previewContext, rootView)
+ KeyguardPreviewClockViewBinder.bind(
+ largeClockHostView,
+ smallClockHostView,
+ clockViewModel,
+ )
+ }
+
+ setUpSmartspace(previewContext, rootView)
+ smartSpaceView?.let {
+ KeyguardPreviewSmartspaceViewBinder.bind(it, smartspaceViewModel)
+ }
}
- }
+ )
}
private fun setupShortcuts(keyguardRootView: ConstraintLayout) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/SideFpsProgressBar.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/SideFpsProgressBar.kt
new file mode 100644
index 0000000..f7ab1ee
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/SideFpsProgressBar.kt
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.view
+
+import android.graphics.PixelFormat
+import android.graphics.Point
+import android.view.Gravity
+import android.view.LayoutInflater
+import android.view.View
+import android.view.WindowManager
+import android.widget.ProgressBar
+import com.android.systemui.biometrics.Utils
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.res.R
+import javax.inject.Inject
+
+private const val TAG = "SideFpsProgressBar"
+
+const val progressBarHeight = 100
+
+@SysUISingleton
+class SideFpsProgressBar
+@Inject
+constructor(
+ private val layoutInflater: LayoutInflater,
+ private val windowManager: WindowManager,
+) {
+ private var progressBarWidth = 200
+ fun updateView(
+ visible: Boolean,
+ location: Point,
+ shouldRotate90Degrees: Boolean,
+ progressBarWidth: Int
+ ) {
+ if (visible) {
+ this.progressBarWidth = progressBarWidth
+ createAndShowOverlay(location, shouldRotate90Degrees)
+ } else {
+ hideOverlay()
+ }
+ }
+
+ fun hideOverlay() {
+ overlayView = null
+ }
+
+ private val overlayViewParams =
+ WindowManager.LayoutParams(
+ progressBarHeight,
+ progressBarWidth,
+ WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
+ Utils.FINGERPRINT_OVERLAY_LAYOUT_PARAM_FLAGS,
+ PixelFormat.TRANSPARENT
+ )
+ .apply {
+ title = TAG
+ fitInsetsTypes = 0 // overrides default, avoiding status bars during layout
+ gravity = Gravity.TOP or Gravity.LEFT
+ layoutInDisplayCutoutMode =
+ WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
+ privateFlags =
+ WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY or
+ WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION
+ }
+
+ private var overlayView: View? = null
+ set(value) {
+ field?.let { oldView -> windowManager.removeView(oldView) }
+ field = value
+ field?.let { newView -> windowManager.addView(newView, overlayViewParams) }
+ }
+
+ private fun createAndShowOverlay(
+ fingerprintSensorLocation: Point,
+ shouldRotate90Degrees: Boolean
+ ) {
+ if (overlayView == null) {
+ overlayView = layoutInflater.inflate(R.layout.sidefps_progress_bar, null, false)
+ }
+ overlayViewParams.x = fingerprintSensorLocation.x
+ overlayViewParams.y = fingerprintSensorLocation.y
+ if (shouldRotate90Degrees) {
+ overlayView?.rotation = 270.0f
+ overlayViewParams.width = progressBarHeight
+ overlayViewParams.height = progressBarWidth
+ } else {
+ overlayView?.rotation = 0.0f
+ overlayViewParams.width = progressBarWidth
+ overlayViewParams.height = progressBarHeight
+ }
+ windowManager.updateViewLayout(overlayView, overlayViewParams)
+ }
+
+ fun setProgress(value: Float) {
+ overlayView
+ ?.findViewById<ProgressBar?>(R.id.side_fps_progress_bar)
+ ?.setProgress((value * 100).toInt(), false)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprint.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprint.kt
index e8df1a6..d8e4396 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprint.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprint.kt
@@ -17,6 +17,7 @@
package com.android.systemui.keyguard.ui.view.layout.blueprints
+import com.android.systemui.communal.ui.view.layout.sections.CommunalTutorialIndicatorSection
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.shared.model.KeyguardBlueprint
import com.android.systemui.keyguard.ui.view.layout.sections.AodBurnInSection
@@ -53,6 +54,7 @@
splitShadeGuidelines: SplitShadeGuidelines,
aodNotificationIconsSection: AodNotificationIconsSection,
aodBurnInSection: AodBurnInSection,
+ communalTutorialIndicatorSection: CommunalTutorialIndicatorSection,
) : KeyguardBlueprint {
override val id: String = DEFAULT
@@ -69,6 +71,7 @@
splitShadeGuidelines,
aodNotificationIconsSection,
aodBurnInSection,
+ communalTutorialIndicatorSection,
)
companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModel.kt
new file mode 100644
index 0000000..024707a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModel.kt
@@ -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.systemui.keyguard.ui.viewmodel
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.domain.interactor.FromAodTransitionInteractor.Companion.TO_LOCKSCREEN_DURATION
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import javax.inject.Inject
+import kotlin.time.Duration.Companion.milliseconds
+import kotlinx.coroutines.flow.Flow
+
+/**
+ * Breaks down AOD->LOCKSCREEN transition into discrete steps for corresponding views to consume.
+ */
+@SysUISingleton
+class AodToLockscreenTransitionViewModel
+@Inject
+constructor(
+ private val interactor: KeyguardTransitionInteractor,
+) {
+
+ private val transitionAnimation =
+ KeyguardTransitionAnimationFlow(
+ transitionDuration = TO_LOCKSCREEN_DURATION,
+ transitionFlow = interactor.aodToLockscreenTransition,
+ )
+
+ /** Ensure alpha is set to be visible */
+ val lockscreenAlpha: Flow<Float> =
+ transitionAnimation.createFlow(
+ duration = 500.milliseconds,
+ onStart = { 1f },
+ onStep = { 1f },
+ )
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModel.kt
new file mode 100644
index 0000000..601dbcc
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModel.kt
@@ -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.systemui.keyguard.ui.viewmodel
+
+import com.android.app.animation.Interpolators.EMPHASIZED_DECELERATE
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.domain.interactor.FromGoneTransitionInteractor.Companion.TO_AOD_DURATION
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import javax.inject.Inject
+import kotlin.time.Duration.Companion.milliseconds
+import kotlinx.coroutines.flow.Flow
+
+/** Breaks down GONE->AOD transition into discrete steps for corresponding views to consume. */
+@SysUISingleton
+class GoneToAodTransitionViewModel
+@Inject
+constructor(
+ private val interactor: KeyguardTransitionInteractor,
+) {
+
+ private val transitionAnimation =
+ KeyguardTransitionAnimationFlow(
+ transitionDuration = TO_AOD_DURATION,
+ transitionFlow = interactor.goneToAodTransition,
+ )
+
+ /** y-translation from the top of the screen for AOD */
+ fun enterFromTopTranslationY(translatePx: Int): Flow<Float> {
+ return transitionAnimation.createFlow(
+ startTime = 600.milliseconds,
+ duration = 500.milliseconds,
+ onStart = { translatePx },
+ onStep = { translatePx + it * -translatePx },
+ onFinish = { 0f },
+ onCancel = { 0f },
+ interpolator = EMPHASIZED_DECELERATE,
+ )
+ }
+
+ /** alpha animation upon entering AOD */
+ val enterFromTopAnimationAlpha: Flow<Float> =
+ transitionAnimation.createFlow(
+ startTime = 600.milliseconds,
+ duration = 500.milliseconds,
+ onStart = { 0f },
+ onStep = { it },
+ )
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
index 89835fe..1f98082 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
@@ -17,14 +17,19 @@
package com.android.systemui.keyguard.ui.viewmodel
+import android.content.Context
import android.util.MathUtils
+import android.view.View.VISIBLE
import com.android.app.animation.Interpolators
import com.android.systemui.common.shared.model.SharedNotificationContainerPosition
import com.android.systemui.keyguard.domain.interactor.BurnInInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.BurnInModel
-import com.android.systemui.keyguard.shared.model.KeyguardRootViewVisibilityState
+import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
+import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
import com.android.systemui.plugins.ClockController
+import com.android.systemui.res.R
import javax.inject.Inject
import javax.inject.Provider
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -32,17 +37,23 @@
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.merge
+import kotlinx.coroutines.flow.onStart
@OptIn(ExperimentalCoroutinesApi::class)
class KeyguardRootViewModel
@Inject
constructor(
+ private val context: Context,
private val keyguardInteractor: KeyguardInteractor,
private val burnInInteractor: BurnInInteractor,
+ private val goneToAodTransitionViewModel: GoneToAodTransitionViewModel,
+ private val aodToLockscreenTransitionViewModel: AodToLockscreenTransitionViewModel,
+ private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
) {
data class PreviewMode(val isInPreviewMode: Boolean = false)
@@ -56,9 +67,12 @@
public var clockControllerProvider: Provider<ClockController>? = null
- /** Represents the current state of the KeyguardRootView visibility */
- val keyguardRootViewVisibilityState: Flow<KeyguardRootViewVisibilityState> =
- keyguardInteractor.keyguardRootViewVisibilityState
+ val burnInLayerVisibility: Flow<Int> =
+ keyguardTransitionInteractor.startedKeyguardState
+ .filter { it == AOD || it == LOCKSCREEN }
+ .map { VISIBLE }
+
+ val goneToAodTransition = keyguardTransitionInteractor.goneToAodTransition
/** An observable for the alpha level for the entire keyguard root view. */
val alpha: Flow<Float> =
@@ -70,9 +84,14 @@
}
}
- private val burnIn: Flow<BurnInModel> =
- combine(keyguardInteractor.dozeAmount, burnInInteractor.keyguardBurnIn) { dozeAmount, burnIn
- ->
+ private fun burnIn(): Flow<BurnInModel> {
+ val dozingAmount: Flow<Float> =
+ merge(
+ keyguardTransitionInteractor.goneToAodTransition.map { it.value },
+ keyguardTransitionInteractor.dozeAmountTransition.map { it.value },
+ )
+
+ return combine(dozingAmount, burnInInteractor.keyguardBurnIn) { dozeAmount, burnIn ->
val interpolation = Interpolators.FAST_OUT_SLOW_IN.getInterpolation(dozeAmount)
val useScaleOnly =
clockControllerProvider?.get()?.config?.useAlternateSmartspaceAODTransition ?: false
@@ -91,13 +110,61 @@
)
}
}
+ }
+
+ /** Specific alpha value for elements visible during [KeyguardState.LOCKSCREEN] */
+ val lockscreenStateAlpha: Flow<Float> = aodToLockscreenTransitionViewModel.lockscreenAlpha
+
+ /** For elements that appear and move during the animation -> AOD */
+ val burnInLayerAlpha: Flow<Float> =
+ previewMode.flatMapLatest {
+ if (it.isInPreviewMode) {
+ flowOf(1f)
+ } else {
+ goneToAodTransitionViewModel.enterFromTopAnimationAlpha
+ }
+ }
val translationY: Flow<Float> =
- merge(keyguardInteractor.keyguardTranslationY, burnIn.map { it.translationY.toFloat() })
+ previewMode.flatMapLatest {
+ if (it.isInPreviewMode) {
+ flowOf(0f)
+ } else {
+ keyguardInteractor.configurationChange.flatMapLatest { _ ->
+ val enterFromTopAmount =
+ context.resources.getDimensionPixelSize(
+ R.dimen.keyguard_enter_from_top_translation_y
+ )
+ combine(
+ keyguardInteractor.keyguardTranslationY.onStart { emit(0f) },
+ burnIn().map { it.translationY.toFloat() }.onStart { emit(0f) },
+ goneToAodTransitionViewModel
+ .enterFromTopTranslationY(enterFromTopAmount)
+ .onStart { emit(0f) },
+ ) { keyguardTransitionY, burnInTranslationY, goneToAodTransitionTranslationY ->
+ // All 3 values need to be combined for a smooth translation
+ keyguardTransitionY + burnInTranslationY + goneToAodTransitionTranslationY
+ }
+ }
+ }
+ }
- val translationX: Flow<Float> = burnIn.map { it.translationX.toFloat() }
+ val translationX: Flow<Float> =
+ previewMode.flatMapLatest {
+ if (it.isInPreviewMode) {
+ flowOf(0f)
+ } else {
+ burnIn().map { it.translationX.toFloat() }
+ }
+ }
- val scale: Flow<Pair<Float, Boolean>> = burnIn.map { Pair(it.scale, it.scaleClockOnly) }
+ val scale: Flow<Pair<Float, Boolean>> =
+ previewMode.flatMapLatest { previewMode ->
+ burnIn().map {
+ val scale = if (previewMode.isInPreviewMode) 1f else it.scale
+ Pair(scale, it.scaleClockOnly)
+ }
+ }
/**
* Puts this view-model in "preview mode", which means it's being used for UI that is rendering
@@ -105,11 +172,14 @@
* lock screen.
*/
fun enablePreviewMode() {
- val newPreviewMode = PreviewMode(true)
- previewMode.value = newPreviewMode
+ previewMode.value = PreviewMode(true)
}
fun onSharedNotificationContainerPositionChanged(top: Float, bottom: Float) {
+ // Notifications should not be visible in preview mode
+ if (previewMode.value.isInPreviewMode) {
+ return
+ }
keyguardInteractor.sharedNotificationContainerPosition.value =
SharedNotificationContainerPosition(top, bottom)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/SideFpsProgressBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/SideFpsProgressBarViewModel.kt
new file mode 100644
index 0000000..2c3b431
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/SideFpsProgressBarViewModel.kt
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.animation.ValueAnimator
+import android.graphics.Point
+import androidx.core.animation.doOnEnd
+import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor
+import com.android.systemui.biometrics.domain.interactor.SideFpsSensorInteractor
+import com.android.systemui.biometrics.shared.model.isDefaultOrientation
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.keyguard.data.repository.DeviceEntryFingerprintAuthRepository
+import com.android.systemui.keyguard.shared.model.AcquiredFingerprintAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.ErrorFingerprintAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.FailFingerprintAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.collectLatest
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.launch
+
+@SysUISingleton
+class SideFpsProgressBarViewModel
+@Inject
+constructor(
+ private val fpAuthRepository: DeviceEntryFingerprintAuthRepository,
+ private val sfpsSensorInteractor: SideFpsSensorInteractor,
+ displayStateInteractor: DisplayStateInteractor,
+ @Application private val applicationScope: CoroutineScope,
+) {
+ private val _progress = MutableStateFlow(0.0f)
+ private val _visible = MutableStateFlow(false)
+ private var _animator: ValueAnimator? = null
+
+ private fun onFingerprintCaptureCompleted() {
+ _visible.value = false
+ _progress.value = 0.0f
+ }
+
+ val isVisible: Flow<Boolean> = _visible.asStateFlow()
+
+ val progress: Flow<Float> = _progress.asStateFlow()
+
+ val sensorWidth: Flow<Int> = sfpsSensorInteractor.sensorLocation.map { it.width }
+
+ val sensorLocation: Flow<Point> =
+ sfpsSensorInteractor.sensorLocation.map { Point(it.left, it.top) }
+
+ val isFingerprintAuthRunning: Flow<Boolean> = fpAuthRepository.isRunning
+
+ val shouldRotate90Degrees: Flow<Boolean> =
+ combine(displayStateInteractor.currentRotation, sfpsSensorInteractor.sensorLocation, ::Pair)
+ .map { (rotation, sensorLocation) ->
+ if (rotation.isDefaultOrientation()) {
+ sensorLocation.isSensorVerticalInDefaultOrientation
+ } else {
+ !sensorLocation.isSensorVerticalInDefaultOrientation
+ }
+ }
+
+ val isProlongedTouchRequiredForAuthentication: Flow<Boolean> =
+ sfpsSensorInteractor.isProlongedTouchRequiredForAuthentication
+
+ init {
+ applicationScope.launch {
+ combine(
+ sfpsSensorInteractor.isProlongedTouchRequiredForAuthentication,
+ sfpsSensorInteractor.authenticationDuration,
+ ::Pair
+ )
+ .collectLatest { (enabled, authDuration) ->
+ if (!enabled) return@collectLatest
+
+ launch {
+ fpAuthRepository.authenticationStatus.collectLatest { authStatus ->
+ when (authStatus) {
+ is AcquiredFingerprintAuthenticationStatus -> {
+ if (authStatus.fingerprintCaptureStarted) {
+
+ _visible.value = true
+ _animator?.cancel()
+ _animator =
+ ValueAnimator.ofFloat(0.0f, 1.0f)
+ .setDuration(authDuration)
+ .apply {
+ addUpdateListener {
+ _progress.value = it.animatedValue as Float
+ }
+ addListener(
+ doOnEnd {
+ if (_progress.value == 0.0f) {
+ _visible.value = false
+ }
+ }
+ )
+ }
+ _animator?.start()
+ } else if (authStatus.fingerprintCaptureCompleted) {
+ onFingerprintCaptureCompleted()
+ } else {
+ // Abandoned FP Auth attempt
+ _animator?.reverse()
+ }
+ }
+ is ErrorFingerprintAuthenticationStatus ->
+ onFingerprintCaptureCompleted()
+ is FailFingerprintAuthenticationStatus ->
+ onFingerprintCaptureCompleted()
+ is SuccessFingerprintAuthenticationStatus ->
+ onFingerprintCaptureCompleted()
+ else -> Unit
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsKeyguardViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsKeyguardViewModel.kt
index 929f27f..dca151d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsKeyguardViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsKeyguardViewModel.kt
@@ -17,20 +17,10 @@
package com.android.systemui.keyguard.ui.viewmodel
import android.graphics.Rect
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
import javax.inject.Inject
import kotlinx.coroutines.ExperimentalCoroutinesApi
@ExperimentalCoroutinesApi
-class UdfpsKeyguardViewModel
-@Inject
-constructor(
- private val featureFlags: FeatureFlags,
-) {
+class UdfpsKeyguardViewModel @Inject constructor() {
var sensorBounds: Rect = Rect()
-
- fun useExpandedOverlay(): Boolean {
- return featureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java b/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java
index 3d4fca1..1b3b473 100644
--- a/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java
+++ b/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java
@@ -51,11 +51,12 @@
Uri uri;
boolean looping;
AudioAttributes attributes;
+ float volume;
long requestTime;
public String toString() {
return "{ code=" + code + " looping=" + looping + " attributes=" + attributes
- + " uri=" + uri + " }";
+ + " volume=" + volume + " uri=" + uri + " }";
}
}
@@ -101,6 +102,7 @@
player.setAudioAttributes(mCmd.attributes);
player.setDataSource(mCmd.context, mCmd.uri);
player.setLooping(mCmd.looping);
+ player.setVolume(mCmd.volume);
player.setOnCompletionListener(NotificationPlayer.this);
player.setOnErrorListener(NotificationPlayer.this);
player.prepare();
@@ -401,10 +403,11 @@
* (see {@link MediaPlayer#setLooping(boolean)})
* @param stream the AudioStream to use.
* (see {@link MediaPlayer#setAudioStreamType(int)})
+ * @param volume the volume for the audio with values in range [0.0, 1.0]
* @deprecated use {@link #play(Context, Uri, boolean, AudioAttributes)} instead.
*/
@Deprecated
- public void play(Context context, Uri uri, boolean looping, int stream) {
+ public void play(Context context, Uri uri, boolean looping, int stream, float volume) {
if (DEBUG) { Log.d(mTag, "play uri=" + uri.toString()); }
PlayerBase.deprecateStreamTypeForPlayback(stream, "NotificationPlayer", "play");
Command cmd = new Command();
@@ -414,6 +417,7 @@
cmd.uri = uri;
cmd.looping = looping;
cmd.attributes = new AudioAttributes.Builder().setInternalLegacyStreamType(stream).build();
+ cmd.volume = volume;
synchronized (mCmdQueue) {
enqueueLocked(cmd);
mState = PLAY;
@@ -432,8 +436,10 @@
* (see {@link MediaPlayer#setLooping(boolean)})
* @param attributes the AudioAttributes to use.
* (see {@link MediaPlayer#setAudioAttributes(AudioAttributes)})
+ * @param volume the volume for the audio with values in range [0.0, 1.0]
*/
- public void play(Context context, Uri uri, boolean looping, AudioAttributes attributes) {
+ public void play(Context context, Uri uri, boolean looping, AudioAttributes attributes,
+ float volume) {
if (DEBUG) { Log.d(mTag, "play uri=" + uri.toString()); }
Command cmd = new Command();
cmd.requestTime = SystemClock.uptimeMillis();
@@ -442,6 +448,7 @@
cmd.uri = uri;
cmd.looping = looping;
cmd.attributes = attributes;
+ cmd.volume = volume;
synchronized (mCmdQueue) {
enqueueLocked(cmd);
mState = PLAY;
diff --git a/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java b/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java
index 80be766..7a48836 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..a53f0f1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/MediaProjectionMetricsLogger.kt
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.MEDIA_PROJECTION_STATE_CHANGED__CREATION_SOURCE__CREATION_SOURCE_APP as METRICS_CREATION_SOURCE_APP
+import com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__CREATION_SOURCE__CREATION_SOURCE_CAST as METRICS_CREATION_SOURCE_CAST
+import com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__CREATION_SOURCE__CREATION_SOURCE_SYSTEM_UI_SCREEN_RECORDER as METRICS_CREATION_SOURCE_SYSTEM_UI_SCREEN_RECORDER
+import com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__CREATION_SOURCE__CREATION_SOURCE_UNKNOWN as METRICS_CREATION_SOURCE_UNKNOWN
+import com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_INITIATED
+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 notifyProjectionInitiated(sessionCreationSource: SessionCreationSource) {
+ notifyToServer(
+ MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_INITIATED,
+ sessionCreationSource
+ )
+ }
+
+ /**
+ * Request to log that the permission request moved to the given state.
+ *
+ * Should not be used for the initialization state, since that should use {@link
+ * MediaProjectionMetricsLogger#notifyProjectionInitiated(Int)} and pass the
+ * sessionCreationSource.
+ */
+ fun notifyPermissionProgress(state: Int) {
+ // TODO validate state is valid
+ notifyToServer(state, SessionCreationSource.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: SessionCreationSource) {
+ Log.v(TAG, "FOO notifyToServer of state $state and source $sessionCreationSource")
+ try {
+ service.notifyPermissionRequestStateChange(
+ Process.myUid(),
+ state,
+ sessionCreationSource.toMetricsConstant()
+ )
+ } catch (e: RemoteException) {
+ Log.e(
+ TAG,
+ "Error notifying server of permission flow state $state from source " +
+ "$sessionCreationSource",
+ e
+ )
+ }
+ }
+
+ companion object {
+ const val TAG = "MediaProjectionMetricsLogger"
+ }
+}
+
+enum class SessionCreationSource {
+ APP,
+ CAST,
+ SYSTEM_UI_SCREEN_RECORDER,
+ UNKNOWN;
+
+ fun toMetricsConstant(): Int =
+ when (this) {
+ APP -> METRICS_CREATION_SOURCE_APP
+ CAST -> METRICS_CREATION_SOURCE_CAST
+ SYSTEM_UI_SCREEN_RECORDER -> METRICS_CREATION_SOURCE_SYSTEM_UI_SCREEN_RECORDER
+ UNKNOWN -> METRICS_CREATION_SOURCE_UNKNOWN
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorActivity.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorActivity.kt
index b5d3e91..0bbcfd9 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorActivity.kt
@@ -87,14 +87,15 @@
override fun getLayoutResource() = R.layout.media_projection_app_selector
- public override fun onCreate(bundle: Bundle?) {
+ public override fun onCreate(savedInstanceState: Bundle?) {
lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE)
component =
componentFactory.create(
hostUserHandle = hostUserHandle,
callingPackage = callingPackage,
view = this,
- resultHandler = this
+ resultHandler = this,
+ isFirstStart = savedInstanceState == null
)
component.lifecycleObservers.forEach { lifecycle.addObserver(it) }
@@ -113,7 +114,7 @@
reviewGrantedConsentRequired =
intent.getBooleanExtra(EXTRA_USER_REVIEW_GRANTED_CONSENT, false)
- super.onCreate(bundle)
+ super.onCreate(savedInstanceState)
controller.init()
// we override AppList's AccessibilityDelegate set in ResolverActivity.onCreate because in
// our case this delegate must extend RecyclerViewAccessibilityDelegate, otherwise
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..8c6f307 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
@@ -142,6 +146,9 @@
@BindsInstance @MediaProjectionAppSelector callingPackage: String?,
@BindsInstance view: MediaProjectionAppSelectorView,
@BindsInstance resultHandler: MediaProjectionAppSelectorResultHandler,
+ // Whether the app selector is starting for the first time. False when it is re-starting
+ // due to a config change.
+ @BindsInstance @MediaProjectionAppSelector isFirstStart: Boolean,
): MediaProjectionAppSelectorComponent
}
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..69132d3 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorController.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorController.kt
@@ -18,12 +18,19 @@
import android.content.ComponentName
import android.os.UserHandle
+import com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_APP_SELECTOR_DISPLAYED as STATE_APP_SELECTOR_DISPLAYED
+import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger
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,16 +43,30 @@
@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,
+ @MediaProjectionAppSelector private val isFirstStart: Boolean,
+ private val logger: MediaProjectionMetricsLogger,
) {
fun init() {
+ // Only log during the first start of the app selector.
+ // Don't log when the app selector restarts due to a config change.
+ if (isFirstStart) {
+ logger.notifyPermissionProgress(STATE_APP_SELECTOR_DISPLAYED)
+ }
+
scope.launch {
val recentTasks = recentTaskListProvider.loadRecentTasks()
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 +75,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..e9b4582 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
@@ -22,8 +22,10 @@
data class RecentTask(
val taskId: Int,
+ val displayId: Int,
@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..730aa62 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,17 +48,24 @@
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(
it.taskId,
+ it.displayId,
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/MediaProjectionRecentsViewController.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionRecentsViewController.kt
index fd1a683..ba837db 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionRecentsViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionRecentsViewController.kt
@@ -130,10 +130,10 @@
view.width,
view.height
)
- activityOptions.setPendingIntentBackgroundActivityStartMode(
+ activityOptions.pendingIntentBackgroundActivityStartMode =
MODE_BACKGROUND_ACTIVITY_START_ALLOWED
- )
activityOptions.launchCookie = launchCookie
+ activityOptions.launchDisplayId = task.displayId
activityTaskManager.startActivityFromRecents(task.taskId, activityOptions.toBundle())
resultHandler.returnSelectedApp(launchCookie)
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 d08d040..fa418fc 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
@@ -53,7 +53,9 @@
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
+import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger;
import com.android.systemui.mediaprojection.MediaProjectionServiceHelper;
+import com.android.systemui.mediaprojection.SessionCreationSource;
import com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelectorActivity;
import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDevicePolicyResolver;
import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDisabledDialog;
@@ -74,6 +76,7 @@
private final FeatureFlags mFeatureFlags;
private final Lazy<ScreenCaptureDevicePolicyResolver> mScreenCaptureDevicePolicyResolver;
private final StatusBarManager mStatusBarManager;
+ private final MediaProjectionMetricsLogger mMediaProjectionMetricsLogger;
private String mPackageName;
private int mUid;
@@ -90,15 +93,17 @@
@Inject
public MediaProjectionPermissionActivity(FeatureFlags featureFlags,
Lazy<ScreenCaptureDevicePolicyResolver> screenCaptureDevicePolicyResolver,
- StatusBarManager statusBarManager) {
+ StatusBarManager statusBarManager,
+ MediaProjectionMetricsLogger mediaProjectionMetricsLogger) {
mFeatureFlags = featureFlags;
mScreenCaptureDevicePolicyResolver = screenCaptureDevicePolicyResolver;
mStatusBarManager = statusBarManager;
+ mMediaProjectionMetricsLogger = mediaProjectionMetricsLogger;
}
@Override
- public void onCreate(Bundle icicle) {
- super.onCreate(icicle);
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
final Intent launchingIntent = getIntent();
mReviewGrantedConsentRequired = launchingIntent.getBooleanExtra(
@@ -133,6 +138,10 @@
try {
if (MediaProjectionServiceHelper.hasProjectionPermission(mUid, mPackageName)) {
+ if (savedInstanceState == null) {
+ mMediaProjectionMetricsLogger.notifyProjectionInitiated(
+ SessionCreationSource.APP);
+ }
final IMediaProjection projection =
MediaProjectionServiceHelper.createOrReuseProjection(mUid, mPackageName,
mReviewGrantedConsentRequired);
@@ -231,6 +240,13 @@
mDialog = dialogBuilder.create();
}
+ if (savedInstanceState == null) {
+ mMediaProjectionMetricsLogger.notifyProjectionInitiated(
+ appName == null
+ ? SessionCreationSource.CAST
+ : SessionCreationSource.APP);
+ }
+
setUpDialog(mDialog);
mDialog.show();
}
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/gestural/BackPanelController.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
index c4749e0..c77f3f4 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
@@ -231,7 +231,6 @@
animation.removeEndListener(this)
if (!canceled) {
-
// The delay between finishing this animation and starting the runnable
val delay = max(0, runnableDelay - elapsedTimeSinceEntry)
@@ -461,7 +460,6 @@
}
private fun handleMoveEvent(event: MotionEvent) {
-
val x = event.x
val y = event.y
@@ -927,17 +925,7 @@
GestureState.ACTIVE -> {
previousXTranslationOnActiveOffset = previousXTranslation
updateRestingArrowDimens()
- if (featureFlags.isEnabled(ONE_WAY_HAPTICS_API_MIGRATION)) {
- vibratorHelper.performHapticFeedback(
- mView,
- HapticFeedbackConstants.GESTURE_THRESHOLD_ACTIVATE
- )
- } else {
- vibratorHelper.cancel()
- mainHandler.postDelayed(10L) {
- vibratorHelper.vibrate(VIBRATE_ACTIVATED_EFFECT)
- }
- }
+ performActivatedHapticFeedback()
val popVelocity =
if (previousState == GestureState.INACTIVE) {
POP_ON_INACTIVE_TO_ACTIVE_VELOCITY
@@ -958,25 +946,24 @@
mView.popOffEdge(POP_ON_INACTIVE_VELOCITY)
- if (featureFlags.isEnabled(ONE_WAY_HAPTICS_API_MIGRATION)) {
- vibratorHelper.performHapticFeedback(
- mView,
- HapticFeedbackConstants.GESTURE_THRESHOLD_DEACTIVATE
- )
- } else {
- vibratorHelper.vibrate(VIBRATE_DEACTIVATED_EFFECT)
- }
+ performDeactivatedHapticFeedback()
updateRestingArrowDimens()
}
GestureState.FLUNG -> {
+ // Typically a vibration is only played while transitioning to ACTIVE. However there
+ // are instances where a fling to trigger back occurs while not in that state.
+ // (e.g. A fling is detected before crossing the trigger threshold.)
+ if (previousState != GestureState.ACTIVE) {
+ performActivatedHapticFeedback()
+ }
mainHandler.postDelayed(POP_ON_FLING_DELAY) {
mView.popScale(POP_ON_FLING_VELOCITY)
}
- updateRestingArrowDimens()
mainHandler.postDelayed(
onEndSetCommittedStateListener.runnable,
MIN_DURATION_FLING_ANIMATION
)
+ updateRestingArrowDimens()
}
GestureState.COMMITTED -> {
// In most cases, animating between states is handled via `updateRestingArrowDimens`
@@ -1011,6 +998,31 @@
}
}
+ private fun performDeactivatedHapticFeedback() {
+ if (featureFlags.isEnabled(ONE_WAY_HAPTICS_API_MIGRATION)) {
+ vibratorHelper.performHapticFeedback(
+ mView,
+ HapticFeedbackConstants.GESTURE_THRESHOLD_DEACTIVATE
+ )
+ } else {
+ vibratorHelper.vibrate(VIBRATE_DEACTIVATED_EFFECT)
+ }
+ }
+
+ private fun performActivatedHapticFeedback() {
+ if (featureFlags.isEnabled(ONE_WAY_HAPTICS_API_MIGRATION)) {
+ vibratorHelper.performHapticFeedback(
+ mView,
+ HapticFeedbackConstants.GESTURE_THRESHOLD_ACTIVATE
+ )
+ } else {
+ vibratorHelper.cancel()
+ mainHandler.postDelayed(10L) {
+ vibratorHelper.vibrate(VIBRATE_ACTIVATED_EFFECT)
+ }
+ }
+ }
+
private fun convertVelocityToAnimationFactor(
valueOnFastVelocity: Float,
valueOnSlowVelocity: Float,
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java b/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java
index 733383e..58c4f0d 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java
@@ -96,43 +96,9 @@
import java.util.regex.Pattern;
import java.util.stream.Collectors;
-/** Functions that help creating the People tile layouts. */
-public class PeopleTileViewHelper {
- /** Turns on debugging information about People Space. */
- private static final boolean DEBUG = PeopleSpaceUtils.DEBUG;
- private static final String TAG = "PeopleTileView";
-
- private static final int DAYS_IN_A_WEEK = 7;
- private static final int ONE_DAY = 1;
-
- public static final int LAYOUT_SMALL = 0;
- public static final int LAYOUT_MEDIUM = 1;
- public static final int LAYOUT_LARGE = 2;
-
- private static final int MIN_CONTENT_MAX_LINES = 2;
- private static final int NAME_MAX_LINES_WITHOUT_LAST_INTERACTION = 3;
- private static final int NAME_MAX_LINES_WITH_LAST_INTERACTION = 1;
-
- private static final int FIXED_HEIGHT_DIMENS_FOR_LARGE_NOTIF_CONTENT = 16 + 22 + 8 + 16;
- private static final int FIXED_HEIGHT_DIMENS_FOR_LARGE_STATUS_CONTENT = 16 + 16 + 24 + 4 + 16;
- private static final int MIN_MEDIUM_VERTICAL_PADDING = 4;
- private static final int MAX_MEDIUM_PADDING = 16;
- private static final int FIXED_HEIGHT_DIMENS_FOR_MEDIUM_CONTENT_BEFORE_PADDING = 8 + 4;
- private static final int FIXED_HEIGHT_DIMENS_FOR_SMALL_VERTICAL = 6 + 4 + 8;
- private static final int FIXED_WIDTH_DIMENS_FOR_SMALL_VERTICAL = 4 + 4;
- private static final int FIXED_HEIGHT_DIMENS_FOR_SMALL_HORIZONTAL = 6 + 4;
- private static final int FIXED_WIDTH_DIMENS_FOR_SMALL_HORIZONTAL = 8 + 8;
-
- private static final int MESSAGES_COUNT_OVERFLOW = 6;
-
- private static final CharSequence EMOJI_CAKE = "\ud83c\udf82";
-
- private static final Pattern DOUBLE_EXCLAMATION_PATTERN = Pattern.compile("[!][!]+");
- private static final Pattern DOUBLE_QUESTION_PATTERN = Pattern.compile("[?][?]+");
- private static final Pattern ANY_DOUBLE_MARK_PATTERN = Pattern.compile("[!?][!?]+");
- private static final Pattern MIXED_MARK_PATTERN = Pattern.compile("![?].*|.*[?]!");
-
- static final String BRIEF_PAUSE_ON_TALKBACK = "\n\n";
+/** Variables and functions that is related to Emoji. */
+class EmojiHelper {
+ static final CharSequence EMOJI_CAKE = "\ud83c\udf82";
// This regex can be used to match Unicode emoji characters and character sequences. It's from
// the official Unicode site (https://unicode.org/reports/tr51/#EBNF_and_Regex) with minor
@@ -165,7 +131,44 @@
+ "\\p{Emoji}(\\p{EMod}|\\x{FE0F}\\x{20E3}?|[\\x{E0020}-\\x{E007E}]+\\x{E007F})"
+ "?)*";
- private static final Pattern EMOJI_PATTERN = Pattern.compile(UNICODE_EMOJI_REGEX);
+ static final Pattern EMOJI_PATTERN = Pattern.compile(UNICODE_EMOJI_REGEX);
+}
+
+/** Functions that help creating the People tile layouts. */
+public class PeopleTileViewHelper {
+ /** Turns on debugging information about People Space. */
+ private static final boolean DEBUG = PeopleSpaceUtils.DEBUG;
+ private static final String TAG = "PeopleTileView";
+
+ private static final int DAYS_IN_A_WEEK = 7;
+ private static final int ONE_DAY = 1;
+
+ public static final int LAYOUT_SMALL = 0;
+ public static final int LAYOUT_MEDIUM = 1;
+ public static final int LAYOUT_LARGE = 2;
+
+ private static final int MIN_CONTENT_MAX_LINES = 2;
+ private static final int NAME_MAX_LINES_WITHOUT_LAST_INTERACTION = 3;
+ private static final int NAME_MAX_LINES_WITH_LAST_INTERACTION = 1;
+
+ private static final int FIXED_HEIGHT_DIMENS_FOR_LARGE_NOTIF_CONTENT = 16 + 22 + 8 + 16;
+ private static final int FIXED_HEIGHT_DIMENS_FOR_LARGE_STATUS_CONTENT = 16 + 16 + 24 + 4 + 16;
+ private static final int MIN_MEDIUM_VERTICAL_PADDING = 4;
+ private static final int MAX_MEDIUM_PADDING = 16;
+ private static final int FIXED_HEIGHT_DIMENS_FOR_MEDIUM_CONTENT_BEFORE_PADDING = 8 + 4;
+ private static final int FIXED_HEIGHT_DIMENS_FOR_SMALL_VERTICAL = 6 + 4 + 8;
+ private static final int FIXED_WIDTH_DIMENS_FOR_SMALL_VERTICAL = 4 + 4;
+ private static final int FIXED_HEIGHT_DIMENS_FOR_SMALL_HORIZONTAL = 6 + 4;
+ private static final int FIXED_WIDTH_DIMENS_FOR_SMALL_HORIZONTAL = 8 + 8;
+
+ private static final int MESSAGES_COUNT_OVERFLOW = 6;
+
+ private static final Pattern DOUBLE_EXCLAMATION_PATTERN = Pattern.compile("[!][!]+");
+ private static final Pattern DOUBLE_QUESTION_PATTERN = Pattern.compile("[?][?]+");
+ private static final Pattern ANY_DOUBLE_MARK_PATTERN = Pattern.compile("[!?][!?]+");
+ private static final Pattern MIXED_MARK_PATTERN = Pattern.compile("![?].*|.*[?]!");
+
+ static final String BRIEF_PAUSE_ON_TALKBACK = "\n\n";
public static final String EMPTY_STRING = "";
@@ -831,7 +834,7 @@
if (status.getActivity() == ACTIVITY_BIRTHDAY
|| status.getActivity() == ACTIVITY_UPCOMING_BIRTHDAY) {
- setEmojiBackground(views, EMOJI_CAKE);
+ setEmojiBackground(views, EmojiHelper.EMOJI_CAKE);
}
Icon statusIcon = status.getIcon();
@@ -1072,7 +1075,7 @@
/** Returns emoji if {@code message} has two of the same emoji in sequence. */
@VisibleForTesting
CharSequence getDoubleEmoji(CharSequence message) {
- Matcher unicodeEmojiMatcher = EMOJI_PATTERN.matcher(message);
+ Matcher unicodeEmojiMatcher = EmojiHelper.EMOJI_PATTERN.matcher(message);
// Stores the start and end indices of each matched emoji.
List<Pair<Integer, Integer>> emojiIndices = new ArrayList<>();
// Stores each emoji text.
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/QSImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSImpl.java
index 202254b..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;
@@ -934,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/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/external/TileLifecycleManager.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
index 2469a98..78f2da5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
@@ -17,6 +17,7 @@
import static android.service.quicksettings.TileService.START_ACTIVITY_NEEDS_PENDING_INTENT;
+import android.app.ActivityManager;
import android.app.compat.CompatChanges;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -35,6 +36,7 @@
import android.service.quicksettings.IQSService;
import android.service.quicksettings.IQSTileService;
import android.service.quicksettings.TileService;
+import android.text.format.DateUtils;
import android.util.ArraySet;
import android.util.Log;
@@ -81,7 +83,8 @@
// Bind retry control.
private static final int MAX_BIND_RETRIES = 5;
- private static final int DEFAULT_BIND_RETRY_DELAY = 1000;
+ private static final long DEFAULT_BIND_RETRY_DELAY = 5 * DateUtils.SECOND_IN_MILLIS;
+ private static final long LOW_MEMORY_BIND_RETRY_DELAY = 20 * DateUtils.SECOND_IN_MILLIS;
// Shared prefs that hold tile lifecycle info.
private static final String TILES = "tiles_prefs";
@@ -94,6 +97,7 @@
private final IBinder mToken = new Binder();
private final PackageManagerAdapter mPackageManagerAdapter;
private final BroadcastDispatcher mBroadcastDispatcher;
+ private final ActivityManager mActivityManager;
private Set<Integer> mQueuedMessages = new ArraySet<>();
@Nullable
@@ -102,7 +106,8 @@
private IBinder mClickBinder;
private int mBindTryCount;
- private int mBindRetryDelay = DEFAULT_BIND_RETRY_DELAY;
+ private long mBindRetryDelay = DEFAULT_BIND_RETRY_DELAY;
+ private AtomicBoolean isDeathRebindScheduled = new AtomicBoolean(false);
private AtomicBoolean mBound = new AtomicBoolean(false);
private AtomicBoolean mPackageReceiverRegistered = new AtomicBoolean(false);
private AtomicBoolean mUserReceiverRegistered = new AtomicBoolean(false);
@@ -115,7 +120,7 @@
@AssistedInject
TileLifecycleManager(@Main Handler handler, Context context, IQSService service,
PackageManagerAdapter packageManagerAdapter, BroadcastDispatcher broadcastDispatcher,
- @Assisted Intent intent, @Assisted UserHandle user,
+ @Assisted Intent intent, @Assisted UserHandle user, ActivityManager activityManager,
@Background DelayableExecutor executor) {
mContext = context;
mHandler = handler;
@@ -126,6 +131,7 @@
mExecutor = executor;
mPackageManagerAdapter = packageManagerAdapter;
mBroadcastDispatcher = broadcastDispatcher;
+ mActivityManager = activityManager;
if (DEBUG) Log.d(TAG, "Creating " + mIntent + " " + mUser);
}
@@ -152,10 +158,6 @@
}
}
- public void setBindRetryDelay(int delayMs) {
- mBindRetryDelay = delayMs;
- }
-
public boolean isActiveTile() {
try {
ServiceInfo info = mPackageManagerAdapter.getServiceInfo(mIntent.getComponent(),
@@ -250,19 +252,15 @@
private boolean bindServices() {
String packageName = mIntent.getComponent().getPackageName();
+ int flags = Context.BIND_AUTO_CREATE
+ | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE
+ | Context.BIND_WAIVE_PRIORITY;
if (CompatChanges.isChangeEnabled(START_ACTIVITY_NEEDS_PENDING_INTENT, packageName,
mUser)) {
- return mContext.bindServiceAsUser(mIntent, this,
- Context.BIND_AUTO_CREATE
- | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE
- | Context.BIND_WAIVE_PRIORITY,
- mUser);
+ return mContext.bindServiceAsUser(mIntent, this, flags, mUser);
}
return mContext.bindServiceAsUser(mIntent, this,
- Context.BIND_AUTO_CREATE
- | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE
- | Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS
- | Context.BIND_WAIVE_PRIORITY,
+ flags | Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS,
mUser);
}
@@ -352,10 +350,34 @@
if (!mBound.get()) return;
if (DEBUG) Log.d(TAG, "handleDeath");
if (checkComponentState()) {
- mExecutor.executeDelayed(() -> setBindService(true), mBindRetryDelay);
+ if (isDeathRebindScheduled.compareAndSet(false, true)) {
+ mExecutor.executeDelayed(() -> {
+ setBindService(true);
+ isDeathRebindScheduled.set(false);
+ }, getRebindDelay());
+ }
}
}
+ /**
+ * @return the delay to automatically rebind after a service died. It provides a longer delay if
+ * the device is a low memory state because the service is likely to get killed again by the
+ * system. In this case we want to rebind later and not to cause a loop of a frequent rebinds.
+ */
+ private long getRebindDelay() {
+ final ActivityManager.MemoryInfo info = new ActivityManager.MemoryInfo();
+ mActivityManager.getMemoryInfo(info);
+
+ final long delay;
+ if (info.lowMemory) {
+ delay = LOW_MEMORY_BIND_RETRY_DELAY;
+ } else {
+ delay = mBindRetryDelay;
+ }
+ Log.i(TAG, "Rebinding with a delay=" + delay);
+ return delay;
+ }
+
private boolean checkComponentState() {
if (!isPackageAvailable() || !isComponentAvailable()) {
startPackageListening();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java
index 941a9d6..3ee4a1b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java
@@ -75,13 +75,12 @@
private boolean mStarted = false;
TileServiceManager(TileServices tileServices, Handler handler, ComponentName component,
- BroadcastDispatcher broadcastDispatcher, UserTracker userTracker,
- CustomTileAddedRepository customTileAddedRepository, DelayableExecutor executor) {
+ UserTracker userTracker, TileLifecycleManager.Factory tileLifecycleManagerFactory,
+ CustomTileAddedRepository customTileAddedRepository) {
this(tileServices, handler, userTracker, customTileAddedRepository,
- new TileLifecycleManager(handler, tileServices.getContext(), tileServices,
- new PackageManagerAdapter(tileServices.getContext()), broadcastDispatcher,
+ tileLifecycleManagerFactory.create(
new Intent(TileService.ACTION_QS_TILE).setComponent(component),
- userTracker.getUserHandle(), executor));
+ userTracker.getUserHandle()));
}
@VisibleForTesting
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java
index acee8e9..c3744df 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java
@@ -81,6 +81,7 @@
private final UserTracker mUserTracker;
private final StatusBarIconController mStatusBarIconController;
private final PanelInteractor mPanelInteractor;
+ private final TileLifecycleManager.Factory mTileLifecycleManagerFactory;
private final CustomTileAddedRepository mCustomTileAddedRepository;
private final DelayableExecutor mBackgroundExecutor;
@@ -96,6 +97,7 @@
CommandQueue commandQueue,
StatusBarIconController statusBarIconController,
PanelInteractor panelInteractor,
+ TileLifecycleManager.Factory tileLifecycleManagerFactory,
CustomTileAddedRepository customTileAddedRepository,
@Background DelayableExecutor backgroundExecutor) {
mHost = host;
@@ -109,6 +111,7 @@
mStatusBarIconController = statusBarIconController;
mCommandQueue.addCallback(mRequestListeningCallback);
mPanelInteractor = panelInteractor;
+ mTileLifecycleManagerFactory = tileLifecycleManagerFactory;
mCustomTileAddedRepository = customTileAddedRepository;
mBackgroundExecutor = backgroundExecutor;
}
@@ -137,8 +140,8 @@
protected TileServiceManager onCreateTileService(ComponentName component,
BroadcastDispatcher broadcastDispatcher) {
- return new TileServiceManager(this, mHandlerProvider.get(), component,
- broadcastDispatcher, mUserTracker, mCustomTileAddedRepository, mBackgroundExecutor);
+ return new TileServiceManager(this, mHandlerProvider.get(), component, mUserTracker,
+ mTileLifecycleManagerFactory, mCustomTileAddedRepository);
}
public void freeService(CustomTileInterface tile, TileServiceManager service) {
@@ -323,7 +326,7 @@
if (info.applicationInfo.isSystemApp()) {
final StatusBarIcon statusIcon = icon != null
? new StatusBarIcon(userHandle, packageName, icon, 0, 0,
- contentDescription)
+ contentDescription)
: null;
final String slot = getStatusBarIconSlotName(componentName);
mMainHandler.post(new Runnable() {
@@ -356,11 +359,11 @@
synchronized (mServices) {
mTokenMap.forEach((iBinder, customTile) ->
sb.append(iBinder.toString())
- .append(":")
- .append(customTile.getComponent().flattenToShortString())
- .append(":")
- .append(customTile.getUser())
- .append(","));
+ .append(":")
+ .append(customTile.getComponent().flattenToShortString())
+ .append(":")
+ .append(customTile.getUser())
+ .append(","));
}
sb.append("]");
return sb.toString();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt
index 64fa33c..eff3e76 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt
@@ -33,6 +33,7 @@
import com.android.systemui.qs.dagger.QSFlagsModule.PM_LITE_ENABLED
import com.android.systemui.qs.footer.data.model.UserSwitcherStatusModel
import com.android.systemui.qs.footer.domain.interactor.FooterActionsInteractor
+import com.android.systemui.qs.footer.domain.model.SecurityButtonConfig
import com.android.systemui.res.R
import com.android.systemui.util.icuMessageFormat
import javax.inject.Inject
@@ -47,17 +48,35 @@
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
+private const val TAG = "FooterActionsViewModel"
+
/** A ViewModel for the footer actions. */
class FooterActionsViewModel(
- @Application appContext: Context,
- private val footerActionsInteractor: FooterActionsInteractor,
- private val falsingManager: FalsingManager,
- private val globalActionsDialogLite: GlobalActionsDialogLite,
- showPowerButton: Boolean,
-) {
- /** The context themed with the Quick Settings colors. */
- private val context = ContextThemeWrapper(appContext, R.style.Theme_SystemUI_QuickSettings)
+ /** The model for the security button. */
+ val security: Flow<FooterActionsSecurityButtonViewModel?>,
+ /** The model for the foreground services button. */
+ val foregroundServices: Flow<FooterActionsForegroundServicesButtonViewModel?>,
+
+ /** The model for the user switcher button. */
+ val userSwitcher: Flow<FooterActionsButtonViewModel?>,
+
+ /** The model for the settings button. */
+ val settings: FooterActionsButtonViewModel,
+
+ /** The model for the power button. */
+ val power: FooterActionsButtonViewModel?,
+
+ /**
+ * Observe the device monitoring dialog requests and show the dialog accordingly. This function
+ * will suspend indefinitely and will need to be cancelled to stop observing.
+ *
+ * Important: [quickSettingsContext] must be the [Context] associated to the
+ * [Quick Settings fragment][com.android.systemui.qs.QSFragmentLegacy], and the call to this
+ * function must be cancelled when that fragment is destroyed.
+ */
+ val observeDeviceMonitoringDialogRequests: suspend (quickSettingsContext: Context) -> Unit,
+) {
/**
* Whether the UI rendering this ViewModel should be visible. Note that even when this is false,
* the UI should still participate to the layout it is included in (i.e. in the View world it
@@ -74,107 +93,6 @@
private val _backgroundAlpha = MutableStateFlow(1f)
val backgroundAlpha: StateFlow<Float> = _backgroundAlpha.asStateFlow()
- /** The model for the security button. */
- val security: Flow<FooterActionsSecurityButtonViewModel?> =
- footerActionsInteractor.securityButtonConfig
- .map { config ->
- val (icon, text, isClickable) = config ?: return@map null
- FooterActionsSecurityButtonViewModel(
- icon,
- text,
- if (isClickable) this::onSecurityButtonClicked else null,
- )
- }
- .distinctUntilChanged()
-
- /** The model for the foreground services button. */
- val foregroundServices: Flow<FooterActionsForegroundServicesButtonViewModel?> =
- combine(
- footerActionsInteractor.foregroundServicesCount,
- footerActionsInteractor.hasNewForegroundServices,
- security,
- ) { foregroundServicesCount, hasNewChanges, securityModel ->
- if (foregroundServicesCount <= 0) {
- return@combine null
- }
-
- val text =
- icuMessageFormat(
- context.resources,
- R.string.fgs_manager_footer_label,
- foregroundServicesCount,
- )
- FooterActionsForegroundServicesButtonViewModel(
- foregroundServicesCount,
- text = text,
- displayText = securityModel == null,
- hasNewChanges = hasNewChanges,
- this::onForegroundServiceButtonClicked,
- )
- }
- .distinctUntilChanged()
-
- /** The model for the user switcher button. */
- val userSwitcher: Flow<FooterActionsButtonViewModel?> =
- footerActionsInteractor.userSwitcherStatus
- .map { userSwitcherStatus ->
- when (userSwitcherStatus) {
- UserSwitcherStatusModel.Disabled -> null
- is UserSwitcherStatusModel.Enabled -> {
- if (userSwitcherStatus.currentUserImage == null) {
- Log.e(
- TAG,
- "Skipped the addition of user switcher button because " +
- "currentUserImage is missing",
- )
- return@map null
- }
-
- userSwitcherButton(userSwitcherStatus)
- }
- }
- }
- .distinctUntilChanged()
-
- /** The model for the settings button. */
- val settings: FooterActionsButtonViewModel =
- FooterActionsButtonViewModel(
- id = R.id.settings_button_container,
- Icon.Resource(
- R.drawable.ic_settings,
- ContentDescription.Resource(R.string.accessibility_quick_settings_settings)
- ),
- iconTint =
- Utils.getColorAttrDefaultColor(
- context,
- R.attr.onShadeInactiveVariant,
- ),
- backgroundColor = R.attr.shadeInactive,
- this::onSettingsButtonClicked,
- )
-
- /** The model for the power button. */
- val power: FooterActionsButtonViewModel? =
- if (showPowerButton) {
- FooterActionsButtonViewModel(
- id = R.id.pm_lite,
- Icon.Resource(
- android.R.drawable.ic_lock_power_off,
- ContentDescription.Resource(R.string.accessibility_quick_settings_power_menu)
- ),
- iconTint =
- Utils.getColorAttrDefaultColor(
- context,
- R.attr.onShadeActive,
- ),
- backgroundColor = R.attr.shadeActive,
- this::onPowerButtonClicked,
- )
- } else {
- null
- }
-
- /** Called when the visibility of the UI rendering this model should be changed. */
fun onVisibilityChangeRequested(visible: Boolean) {
_isVisible.value = visible
}
@@ -195,89 +113,6 @@
}
}
- /**
- * Observe the device monitoring dialog requests and show the dialog accordingly. This function
- * will suspend indefinitely and will need to be cancelled to stop observing.
- *
- * Important: [quickSettingsContext] must be the [Context] associated to the
- * [Quick Settings fragment][com.android.systemui.qs.QSFragmentLegacy], and the call to this
- * function must be cancelled when that fragment is destroyed.
- */
- suspend fun observeDeviceMonitoringDialogRequests(quickSettingsContext: Context) {
- footerActionsInteractor.deviceMonitoringDialogRequests.collect {
- footerActionsInteractor.showDeviceMonitoringDialog(
- quickSettingsContext,
- expandable = null,
- )
- }
- }
-
- private fun onSecurityButtonClicked(quickSettingsContext: Context, expandable: Expandable) {
- if (falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
- return
- }
-
- footerActionsInteractor.showDeviceMonitoringDialog(quickSettingsContext, expandable)
- }
-
- private fun onForegroundServiceButtonClicked(expandable: Expandable) {
- if (falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
- return
- }
-
- footerActionsInteractor.showForegroundServicesDialog(expandable)
- }
-
- private fun onUserSwitcherClicked(expandable: Expandable) {
- if (falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
- return
- }
-
- footerActionsInteractor.showUserSwitcher(expandable)
- }
-
- private fun onSettingsButtonClicked(expandable: Expandable) {
- if (falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
- return
- }
-
- footerActionsInteractor.showSettings(expandable)
- }
-
- private fun onPowerButtonClicked(expandable: Expandable) {
- if (falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
- return
- }
-
- footerActionsInteractor.showPowerMenuDialog(globalActionsDialogLite, expandable)
- }
-
- private fun userSwitcherButton(
- status: UserSwitcherStatusModel.Enabled
- ): FooterActionsButtonViewModel {
- val icon = status.currentUserImage!!
-
- return FooterActionsButtonViewModel(
- id = R.id.multi_user_switch,
- icon =
- Icon.Loaded(
- icon,
- ContentDescription.Loaded(
- userSwitcherContentDescription(status.currentUserName)
- ),
- ),
- iconTint = null,
- backgroundColor = R.attr.shadeInactive,
- onClick = this::onUserSwitcherClicked,
- )
- }
-
- private fun userSwitcherContentDescription(currentUser: String?): String? {
- return currentUser?.let { user ->
- context.getString(R.string.accessibility_quick_settings_user, user)
- }
- }
-
@SysUISingleton
class Factory
@Inject
@@ -315,8 +150,237 @@
)
}
}
+}
- companion object {
- private const val TAG = "FooterActionsViewModel"
+fun FooterActionsViewModel(
+ @Application appContext: Context,
+ footerActionsInteractor: FooterActionsInteractor,
+ falsingManager: FalsingManager,
+ globalActionsDialogLite: GlobalActionsDialogLite,
+ showPowerButton: Boolean,
+): FooterActionsViewModel {
+ suspend fun observeDeviceMonitoringDialogRequests(quickSettingsContext: Context) {
+ footerActionsInteractor.deviceMonitoringDialogRequests.collect {
+ footerActionsInteractor.showDeviceMonitoringDialog(
+ quickSettingsContext,
+ expandable = null,
+ )
+ }
}
+
+ fun onSecurityButtonClicked(quickSettingsContext: Context, expandable: Expandable) {
+ if (falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
+ return
+ }
+
+ footerActionsInteractor.showDeviceMonitoringDialog(quickSettingsContext, expandable)
+ }
+
+ fun onForegroundServiceButtonClicked(expandable: Expandable) {
+ if (falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
+ return
+ }
+
+ footerActionsInteractor.showForegroundServicesDialog(expandable)
+ }
+
+ fun onUserSwitcherClicked(expandable: Expandable) {
+ if (falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
+ return
+ }
+
+ footerActionsInteractor.showUserSwitcher(expandable)
+ }
+
+ fun onSettingsButtonClicked(expandable: Expandable) {
+ if (falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
+ return
+ }
+
+ footerActionsInteractor.showSettings(expandable)
+ }
+
+ fun onPowerButtonClicked(expandable: Expandable) {
+ if (falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
+ return
+ }
+
+ footerActionsInteractor.showPowerMenuDialog(globalActionsDialogLite, expandable)
+ }
+
+ val qsThemedContext = ContextThemeWrapper(appContext, R.style.Theme_SystemUI_QuickSettings)
+
+ val security =
+ footerActionsInteractor.securityButtonConfig
+ .map { config ->
+ config?.let { securityButtonViewModel(it, ::onSecurityButtonClicked) }
+ }
+ .distinctUntilChanged()
+
+ val foregroundServices =
+ combine(
+ footerActionsInteractor.foregroundServicesCount,
+ footerActionsInteractor.hasNewForegroundServices,
+ security,
+ ) { foregroundServicesCount, hasNewChanges, securityModel ->
+ if (foregroundServicesCount <= 0) {
+ return@combine null
+ }
+
+ foregroundServicesButtonViewModel(
+ qsThemedContext,
+ foregroundServicesCount,
+ securityModel,
+ hasNewChanges,
+ ::onForegroundServiceButtonClicked,
+ )
+ }
+ .distinctUntilChanged()
+
+ val userSwitcher =
+ footerActionsInteractor.userSwitcherStatus
+ .map { userSwitcherStatus ->
+ when (userSwitcherStatus) {
+ UserSwitcherStatusModel.Disabled -> null
+ is UserSwitcherStatusModel.Enabled -> {
+ if (userSwitcherStatus.currentUserImage == null) {
+ Log.e(
+ TAG,
+ "Skipped the addition of user switcher button because " +
+ "currentUserImage is missing",
+ )
+ return@map null
+ }
+
+ userSwitcherButtonViewModel(
+ qsThemedContext,
+ userSwitcherStatus,
+ ::onUserSwitcherClicked
+ )
+ }
+ }
+ }
+ .distinctUntilChanged()
+
+ val settings = settingsButtonViewModel(qsThemedContext, ::onSettingsButtonClicked)
+ val power =
+ if (showPowerButton) {
+ powerButtonViewModel(qsThemedContext, ::onPowerButtonClicked)
+ } else {
+ null
+ }
+
+ return FooterActionsViewModel(
+ security = security,
+ foregroundServices = foregroundServices,
+ userSwitcher = userSwitcher,
+ settings = settings,
+ power = power,
+ observeDeviceMonitoringDialogRequests = ::observeDeviceMonitoringDialogRequests,
+ )
+}
+
+fun securityButtonViewModel(
+ config: SecurityButtonConfig,
+ onSecurityButtonClicked: (Context, Expandable) -> Unit,
+): FooterActionsSecurityButtonViewModel {
+ val (icon, text, isClickable) = config
+ return FooterActionsSecurityButtonViewModel(
+ icon,
+ text,
+ if (isClickable) onSecurityButtonClicked else null,
+ )
+}
+
+fun foregroundServicesButtonViewModel(
+ qsThemedContext: Context,
+ foregroundServicesCount: Int,
+ securityModel: FooterActionsSecurityButtonViewModel?,
+ hasNewChanges: Boolean,
+ onForegroundServiceButtonClicked: (Expandable) -> Unit,
+): FooterActionsForegroundServicesButtonViewModel {
+ val text =
+ icuMessageFormat(
+ qsThemedContext.resources,
+ R.string.fgs_manager_footer_label,
+ foregroundServicesCount,
+ )
+
+ return FooterActionsForegroundServicesButtonViewModel(
+ foregroundServicesCount,
+ text = text,
+ displayText = securityModel == null,
+ hasNewChanges = hasNewChanges,
+ onForegroundServiceButtonClicked,
+ )
+}
+
+fun userSwitcherButtonViewModel(
+ qsThemedContext: Context,
+ status: UserSwitcherStatusModel.Enabled,
+ onUserSwitcherClicked: (Expandable) -> Unit,
+): FooterActionsButtonViewModel {
+ val icon = status.currentUserImage!!
+ return FooterActionsButtonViewModel(
+ id = R.id.multi_user_switch,
+ icon =
+ Icon.Loaded(
+ icon,
+ ContentDescription.Loaded(
+ userSwitcherContentDescription(qsThemedContext, status.currentUserName)
+ ),
+ ),
+ iconTint = null,
+ backgroundColor = R.attr.shadeInactive,
+ onClick = onUserSwitcherClicked,
+ )
+}
+
+private fun userSwitcherContentDescription(
+ qsThemedContext: Context,
+ currentUser: String?
+): String? {
+ return currentUser?.let { user ->
+ qsThemedContext.getString(R.string.accessibility_quick_settings_user, user)
+ }
+}
+
+fun settingsButtonViewModel(
+ qsThemedContext: Context,
+ onSettingsButtonClicked: (Expandable) -> Unit,
+): FooterActionsButtonViewModel {
+ return FooterActionsButtonViewModel(
+ id = R.id.settings_button_container,
+ Icon.Resource(
+ R.drawable.ic_settings,
+ ContentDescription.Resource(R.string.accessibility_quick_settings_settings)
+ ),
+ iconTint =
+ Utils.getColorAttrDefaultColor(
+ qsThemedContext,
+ R.attr.onShadeInactiveVariant,
+ ),
+ backgroundColor = R.attr.shadeInactive,
+ onSettingsButtonClicked,
+ )
+}
+
+fun powerButtonViewModel(
+ qsThemedContext: Context,
+ onPowerButtonClicked: (Expandable) -> Unit,
+): FooterActionsButtonViewModel {
+ return FooterActionsButtonViewModel(
+ id = R.id.pm_lite,
+ Icon.Resource(
+ android.R.drawable.ic_lock_power_off,
+ ContentDescription.Resource(R.string.accessibility_quick_settings_power_menu)
+ ),
+ iconTint =
+ Utils.getColorAttrDefaultColor(
+ qsThemedContext,
+ R.attr.onShadeActive,
+ ),
+ backgroundColor = R.attr.shadeActive,
+ onPowerButtonClicked,
+ )
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
index 5a9c0a1..f8e0159 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
@@ -23,7 +23,10 @@
import android.content.res.ColorStateList
import android.content.res.Configuration
import android.content.res.Resources.ID_NULL
+import android.graphics.Color
+import android.graphics.PorterDuff
import android.graphics.drawable.Drawable
+import android.graphics.drawable.LayerDrawable
import android.graphics.drawable.RippleDrawable
import android.os.Trace
import android.service.quicksettings.Tile
@@ -44,7 +47,6 @@
import androidx.annotation.VisibleForTesting
import com.android.settingslib.Utils
import com.android.systemui.FontSizeUtils
-import com.android.systemui.res.R
import com.android.systemui.animation.LaunchableView
import com.android.systemui.animation.LaunchableViewDelegate
import com.android.systemui.plugins.qs.QSIconView
@@ -53,6 +55,7 @@
import com.android.systemui.plugins.qs.QSTileView
import com.android.systemui.qs.logging.QSLogger
import com.android.systemui.qs.tileimpl.QSIconViewImpl.QS_ANIM_LENGTH
+import com.android.systemui.res.R
import java.util.Objects
private const val TAG = "QSTileViewImpl"
@@ -67,6 +70,7 @@
private const val LABEL_NAME = "label"
private const val SECONDARY_LABEL_NAME = "secondaryLabel"
private const val CHEVRON_NAME = "chevron"
+ private const val OVERLAY_NAME = "overlay"
const val UNAVAILABLE_ALPHA = 0.3f
@VisibleForTesting
internal const val TILE_STATE_RES_PREFIX = "tile_states_"
@@ -97,6 +101,13 @@
private val colorInactive = Utils.getColorAttrDefaultColor(context, R.attr.shadeInactive)
private val colorUnavailable = Utils.getColorAttrDefaultColor(context, R.attr.shadeDisabled)
+ private val overlayColorActive = Utils.applyAlpha(
+ /* alpha= */ 0.11f,
+ Utils.getColorAttrDefaultColor(context, R.attr.onShadeActive))
+ private val overlayColorInactive = Utils.applyAlpha(
+ /* alpha= */ 0.08f,
+ Utils.getColorAttrDefaultColor(context, R.attr.onShadeInactive))
+
private val colorLabelActive = Utils.getColorAttrDefaultColor(context, R.attr.onShadeActive)
private val colorLabelInactive = Utils.getColorAttrDefaultColor(context, R.attr.onShadeInactive)
private val colorLabelUnavailable =
@@ -123,8 +134,13 @@
protected var showRippleEffect = true
private lateinit var ripple: RippleDrawable
- private lateinit var colorBackgroundDrawable: Drawable
- private var paintColor: Int = 0
+ private lateinit var backgroundDrawable: LayerDrawable
+ private lateinit var backgroundBaseDrawable: Drawable
+ private lateinit var backgroundOverlayDrawable: Drawable
+
+ private var backgroundColor: Int = 0
+ private var backgroundOverlayColor: Int = 0
+
private val singleAnimator: ValueAnimator = ValueAnimator().apply {
setDuration(QS_ANIM_LENGTH)
addUpdateListener { animation ->
@@ -134,7 +150,8 @@
animation.getAnimatedValue(BACKGROUND_NAME) as Int,
animation.getAnimatedValue(LABEL_NAME) as Int,
animation.getAnimatedValue(SECONDARY_LABEL_NAME) as Int,
- animation.getAnimatedValue(CHEVRON_NAME) as Int
+ animation.getAnimatedValue(CHEVRON_NAME) as Int,
+ animation.getAnimatedValue(OVERLAY_NAME) as Int,
)
}
}
@@ -263,7 +280,12 @@
fun createTileBackground(): Drawable {
ripple = mContext.getDrawable(R.drawable.qs_tile_background) as RippleDrawable
- colorBackgroundDrawable = ripple.findDrawableByLayerId(R.id.background)
+ backgroundDrawable = ripple.findDrawableByLayerId(R.id.background) as LayerDrawable
+ backgroundBaseDrawable =
+ backgroundDrawable.findDrawableByLayerId(R.id.qs_tile_background_base)
+ backgroundOverlayDrawable =
+ backgroundDrawable.findDrawableByLayerId(R.id.qs_tile_background_overlay)
+ backgroundOverlayDrawable.mutate().setTintMode(PorterDuff.Mode.SRC)
return ripple
}
@@ -343,10 +365,10 @@
ripple.also {
// In case that the colorBackgroundDrawable was used as the background, make sure
// it has the correct callback instead of null
- colorBackgroundDrawable.callback = it
+ backgroundDrawable.callback = it
}
} else {
- colorBackgroundDrawable
+ backgroundDrawable
}
}
@@ -512,7 +534,7 @@
singleAnimator.setValues(
colorValuesHolder(
BACKGROUND_NAME,
- paintColor,
+ backgroundColor,
getBackgroundColorForState(state.state, state.disabledByPolicy)
),
colorValuesHolder(
@@ -529,6 +551,11 @@
CHEVRON_NAME,
chevronView.imageTintList?.defaultColor ?: 0,
getChevronColorForState(state.state, state.disabledByPolicy)
+ ),
+ colorValuesHolder(
+ OVERLAY_NAME,
+ backgroundOverlayColor,
+ getOverlayColorForState(state.state)
)
)
singleAnimator.start()
@@ -537,7 +564,8 @@
getBackgroundColorForState(state.state, state.disabledByPolicy),
getLabelColorForState(state.state, state.disabledByPolicy),
getSecondaryLabelColorForState(state.state, state.disabledByPolicy),
- getChevronColorForState(state.state, state.disabledByPolicy)
+ getChevronColorForState(state.state, state.disabledByPolicy),
+ getOverlayColorForState(state.state)
)
}
}
@@ -555,17 +583,19 @@
backgroundColor: Int,
labelColor: Int,
secondaryLabelColor: Int,
- chevronColor: Int
+ chevronColor: Int,
+ overlayColor: Int,
) {
setColor(backgroundColor)
setLabelColor(labelColor)
setSecondaryLabelColor(secondaryLabelColor)
setChevronColor(chevronColor)
+ setOverlayColor(overlayColor)
}
private fun setColor(color: Int) {
- colorBackgroundDrawable.mutate().setTint(color)
- paintColor = color
+ backgroundBaseDrawable.mutate().setTint(color)
+ backgroundColor = color
}
private fun setLabelColor(color: Int) {
@@ -580,6 +610,11 @@
chevronView.imageTintList = ColorStateList.valueOf(color)
}
+ private fun setOverlayColor(overlayColor: Int) {
+ backgroundOverlayDrawable.setTint(overlayColor)
+ backgroundOverlayColor = overlayColor
+ }
+
private fun loadSideViewDrawableIfNecessary(state: QSTile.State) {
if (state.sideViewCustomDrawable != null) {
customDrawableView.setImageDrawable(state.sideViewCustomDrawable)
@@ -654,9 +689,17 @@
private fun getChevronColorForState(state: Int, disabledByPolicy: Boolean = false): Int =
getSecondaryLabelColorForState(state, disabledByPolicy)
+ private fun getOverlayColorForState(state: Int): Int {
+ return when (state) {
+ Tile.STATE_ACTIVE -> overlayColorActive
+ Tile.STATE_INACTIVE -> overlayColorInactive
+ else -> Color.TRANSPARENT
+ }
+ }
+
@VisibleForTesting
internal fun getCurrentColors(): List<Int> = listOf(
- paintColor,
+ backgroundColor,
label.currentTextColor,
secondaryLabel.currentTextColor,
chevronView.imageTintList?.defaultColor ?: 0
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
index fb71cef..17251c3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
@@ -36,7 +36,6 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.systemui.res.R;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
@@ -49,6 +48,7 @@
import com.android.systemui.qs.SettingObserver;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
+import com.android.systemui.res.R;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.util.settings.GlobalSettings;
@@ -56,8 +56,6 @@
import javax.inject.Inject;
-
-
/** Quick settings tile: Airplane mode **/
public class AirplaneModeTile extends QSTileImpl<BooleanState> {
@@ -90,8 +88,7 @@
mBroadcastDispatcher = broadcastDispatcher;
mLazyConnectivityManager = lazyConnectivityManager;
- mSetting = new SettingObserver(globalSettings, mHandler, Global.AIRPLANE_MODE_ON,
- userTracker.getUserId()) {
+ mSetting = new SettingObserver(globalSettings, mHandler, Global.AIRPLANE_MODE_ON) {
@Override
protected void handleValueChanged(int value, boolean observedChange) {
// mHandler is the background handler so calling this is OK
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java
index 18d472b..426aa55 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java
@@ -29,7 +29,6 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.systemui.res.R;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
@@ -38,9 +37,10 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.QsEventLogger;
-import com.android.systemui.qs.SettingObserver;
+import com.android.systemui.qs.UserSettingObserver;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.util.settings.SecureSettings;
@@ -53,7 +53,7 @@
private final BatteryController mBatteryController;
@VisibleForTesting
- protected final SettingObserver mSetting;
+ protected final UserSettingObserver mSetting;
private int mLevel;
private boolean mPowerSave;
@@ -79,7 +79,7 @@
mBatteryController = batteryController;
mBatteryController.observe(getLifecycle(), this);
int currentUser = host.getUserContext().getUserId();
- mSetting = new SettingObserver(
+ mSetting = new UserSettingObserver(
secureSettings,
mHandler,
Secure.LOW_POWER_WARNING_ACKNOWLEDGED,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
index d862f56..18d2f30 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
@@ -39,7 +39,6 @@
import com.android.settingslib.Utils;
import com.android.settingslib.bluetooth.BluetoothUtils;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
-import com.android.systemui.res.R;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.flags.FeatureFlags;
@@ -53,6 +52,7 @@
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.qs.tiles.dialog.bluetooth.BluetoothTileDialogViewModel;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.policy.BluetoothController;
import java.util.List;
@@ -198,6 +198,7 @@
}
state.expandedAccessibilityClassName = Switch.class.getName();
+ state.forceExpandIcon = mFeatureFlags.isEnabled(Flags.BLUETOOTH_QS_TILE_DIALOG);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/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/shared/model/ObservableTransitionState.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/model/ObservableTransitionState.kt
index f704894..3927873 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/shared/model/ObservableTransitionState.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/shared/model/ObservableTransitionState.kt
@@ -42,13 +42,6 @@
* scene, this value will remain true after the pointer is no longer touching the screen and
* will be true in any transition created to animate back to the original position.
*/
- val isInitiatedByUserInput: Boolean,
-
- /**
- * Whether user input is currently driving the transition. For example, if a user is
- * dragging a pointer, this emits true. Once they lift their finger, this emits false while
- * the transition completes/settles.
- */
- val isUserInputOngoing: Flow<Boolean>,
+ val isUserInputDriven: Boolean,
) : ObservableTransitionState()
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java
index 7a0c087..f469c6b 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java
@@ -38,6 +38,8 @@
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
+import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger;
+import com.android.systemui.mediaprojection.SessionCreationSource;
import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDevicePolicyResolver;
import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDisabledDialog;
import com.android.systemui.plugins.ActivityStarter;
@@ -45,13 +47,13 @@
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.policy.CallbackController;
+import dagger.Lazy;
+
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Executor;
import javax.inject.Inject;
-import dagger.Lazy;
-
/**
* Helper class to initiate a screen recording
*/
@@ -71,6 +73,7 @@
private final FeatureFlags mFlags;
private final UserContextProvider mUserContextProvider;
private final UserTracker mUserTracker;
+ private final MediaProjectionMetricsLogger mMediaProjectionMetricsLogger;
protected static final String INTENT_UPDATE_STATE =
"com.android.systemui.screenrecord.UPDATE_STATE";
@@ -115,7 +118,8 @@
FeatureFlags flags,
UserContextProvider userContextProvider,
Lazy<ScreenCaptureDevicePolicyResolver> devicePolicyResolver,
- UserTracker userTracker) {
+ UserTracker userTracker,
+ MediaProjectionMetricsLogger mediaProjectionMetricsLogger) {
mMainExecutor = mainExecutor;
mContext = context;
mFlags = flags;
@@ -123,6 +127,7 @@
mBroadcastDispatcher = broadcastDispatcher;
mUserContextProvider = userContextProvider;
mUserTracker = userTracker;
+ mMediaProjectionMetricsLogger = mediaProjectionMetricsLogger;
BroadcastOptions options = BroadcastOptions.makeBasic();
options.setInteractive(true);
@@ -149,6 +154,9 @@
return new ScreenCaptureDisabledDialog(mContext);
}
+ mMediaProjectionMetricsLogger.notifyProjectionInitiated(
+ SessionCreationSource.SYSTEM_UI_SCREEN_RECORDER);
+
return flags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING)
? new ScreenRecordPermissionDialog(context, getHostUserHandle(), this,
activityStarter, mUserContextProvider, onStartRecordingClicked)
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/RequestProcessor.kt b/packages/SystemUI/src/com/android/systemui/screenshot/RequestProcessor.kt
index 5154067..c34fd42 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/RequestProcessor.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/RequestProcessor.kt
@@ -20,11 +20,10 @@
import android.view.WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.flags.FeatureFlags
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.launch
import java.util.function.Consumer
import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
/** Processes a screenshot request sent from [ScreenshotHelper]. */
interface ScreenshotRequestProcessor {
@@ -36,16 +35,15 @@
suspend fun process(screenshot: ScreenshotData): ScreenshotData
}
-/**
- * Implementation of [ScreenshotRequestProcessor]
- */
+/** Implementation of [ScreenshotRequestProcessor] */
@SysUISingleton
-class RequestProcessor @Inject constructor(
- private val capture: ImageCapture,
- private val policy: ScreenshotPolicy,
- private val flags: FeatureFlags,
- /** For the Java Async version, to invoke the callback. */
- @Application private val mainScope: CoroutineScope
+class RequestProcessor
+@Inject
+constructor(
+ private val capture: ImageCapture,
+ private val policy: ScreenshotPolicy,
+ /** For the Java Async version, to invoke the callback. */
+ @Application private val mainScope: CoroutineScope
) : ScreenshotRequestProcessor {
override suspend fun process(screenshot: ScreenshotData): ScreenshotData {
@@ -67,8 +65,9 @@
result.userHandle = info.user
if (policy.isManagedProfile(info.user.identifier)) {
- val image = capture.captureTask(info.taskId)
- ?: error("Task snapshot returned a null Bitmap!")
+ val image =
+ capture.captureTask(info.taskId)
+ ?: throw RequestProcessorException("Task snapshot returned a null Bitmap!")
// Provide the task snapshot as the screenshot
result.type = TAKE_SCREENSHOT_PROVIDED_IMAGE
@@ -97,3 +96,6 @@
}
private const val TAG = "RequestProcessor"
+
+/** Exception thrown by [RequestProcessor] if something goes wrong. */
+class RequestProcessorException(message: String) : IllegalStateException(message)
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
index 4b3bd0b..21a08a9 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
@@ -318,7 +325,7 @@
Context context,
FeatureFlags flags,
ScreenshotSmartActions screenshotSmartActions,
- ScreenshotNotificationsController screenshotNotificationsController,
+ ScreenshotNotificationsController.Factory screenshotNotificationsControllerFactory,
ScrollCaptureClient scrollCaptureClient,
UiEventLogger uiEventLogger,
ImageExporter imageExporter,
@@ -335,10 +342,11 @@
AssistContentRequester assistContentRequester,
MessageContainerController messageContainerController,
Provider<ScreenshotSoundController> screenshotSoundController,
- @Assisted int displayId
+ @Assisted int displayId,
+ @Assisted boolean showUIOnExternalDisplay
) {
mScreenshotSmartActions = screenshotSmartActions;
- mNotificationsController = screenshotNotificationsController;
+ mNotificationsController = screenshotNotificationsControllerFactory.create(displayId);
mScrollCaptureClient = scrollCaptureClient;
mUiEventLogger = uiEventLogger;
mImageExporter = imageExporter;
@@ -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/ScreenshotNotificationsController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationsController.java
deleted file mode 100644
index 4344fd1..0000000
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationsController.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.screenshot;
-
-import static android.content.Context.NOTIFICATION_SERVICE;
-
-import android.app.Notification;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
-import android.app.admin.DevicePolicyManager;
-import android.content.Context;
-import android.content.Intent;
-import android.content.res.Resources;
-import android.os.UserHandle;
-import android.util.DisplayMetrics;
-import android.view.WindowManager;
-
-import com.android.internal.messages.nano.SystemMessageProto;
-import com.android.systemui.res.R;
-import com.android.systemui.SystemUIApplication;
-import com.android.systemui.util.NotificationChannels;
-
-import javax.inject.Inject;
-
-/**
- * Convenience class to handle showing and hiding notifications while taking a screenshot.
- */
-public class ScreenshotNotificationsController {
- private static final String TAG = "ScreenshotNotificationManager";
-
- private final Context mContext;
- private final Resources mResources;
- private final NotificationManager mNotificationManager;
-
- @Inject
- ScreenshotNotificationsController(Context context, WindowManager windowManager) {
- mContext = context;
- mResources = context.getResources();
- mNotificationManager =
- (NotificationManager) context.getSystemService(NOTIFICATION_SERVICE);
-
- DisplayMetrics displayMetrics = new DisplayMetrics();
- windowManager.getDefaultDisplay().getRealMetrics(displayMetrics);
- }
-
- /**
- * Sends a notification that the screenshot capture has failed.
- */
- public void notifyScreenshotError(int msgResId) {
- Resources res = mContext.getResources();
- String errorMsg = res.getString(msgResId);
-
- // Repurpose the existing notification to notify the user of the error
- Notification.Builder b = new Notification.Builder(mContext, NotificationChannels.ALERTS)
- .setTicker(res.getString(R.string.screenshot_failed_title))
- .setContentTitle(res.getString(R.string.screenshot_failed_title))
- .setContentText(errorMsg)
- .setSmallIcon(R.drawable.stat_notify_image_error)
- .setWhen(System.currentTimeMillis())
- .setVisibility(Notification.VISIBILITY_PUBLIC) // ok to show outside lockscreen
- .setCategory(Notification.CATEGORY_ERROR)
- .setAutoCancel(true)
- .setColor(mContext.getColor(
- com.android.internal.R.color.system_notification_accent_color));
- final DevicePolicyManager dpm =
- (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
- final Intent intent =
- dpm.createAdminSupportIntent(DevicePolicyManager.POLICY_DISABLE_SCREEN_CAPTURE);
- if (intent != null) {
- final PendingIntent pendingIntent = PendingIntent.getActivityAsUser(
- mContext, 0, intent, PendingIntent.FLAG_IMMUTABLE, null, UserHandle.CURRENT);
- b.setContentIntent(pendingIntent);
- }
-
- SystemUIApplication.overrideNotificationAppName(mContext, b, true);
-
- Notification n = new Notification.BigTextStyle(b)
- .bigText(errorMsg)
- .build();
- mNotificationManager.notify(SystemMessageProto.SystemMessage.NOTE_GLOBAL_SCREENSHOT, n);
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationsController.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationsController.kt
new file mode 100644
index 0000000..d874eb6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationsController.kt
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.screenshot
+
+import android.app.Notification
+import android.app.NotificationManager
+import android.app.PendingIntent
+import android.app.admin.DevicePolicyManager
+import android.content.Context
+import android.os.UserHandle
+import android.view.Display
+import com.android.internal.R
+import com.android.internal.messages.nano.SystemMessageProto
+import com.android.systemui.SystemUIApplication
+import com.android.systemui.util.NotificationChannels
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+
+/** Convenience class to handle showing and hiding notifications while taking a screenshot. */
+class ScreenshotNotificationsController
+@AssistedInject
+internal constructor(
+ @Assisted private val displayId: Int,
+ private val context: Context,
+ private val notificationManager: NotificationManager,
+ private val devicePolicyManager: DevicePolicyManager,
+) {
+ private val res = context.resources
+
+ /**
+ * Sends a notification that the screenshot capture has failed.
+ *
+ * Errors for the non-default display are shown in a unique separate notification.
+ */
+ fun notifyScreenshotError(msgResId: Int) {
+ val displayErrorString =
+ if (displayId != Display.DEFAULT_DISPLAY) {
+ " ($externalDisplayString)"
+ } else {
+ ""
+ }
+ val errorMsg = res.getString(msgResId) + displayErrorString
+
+ // Repurpose the existing notification or create a new one
+ val builder =
+ Notification.Builder(context, NotificationChannels.ALERTS)
+ .setTicker(res.getString(com.android.systemui.res.R.string.screenshot_failed_title))
+ .setContentTitle(
+ res.getString(com.android.systemui.res.R.string.screenshot_failed_title)
+ )
+ .setContentText(errorMsg)
+ .setSmallIcon(com.android.systemui.res.R.drawable.stat_notify_image_error)
+ .setWhen(System.currentTimeMillis())
+ .setVisibility(Notification.VISIBILITY_PUBLIC) // ok to show outside lockscreen
+ .setCategory(Notification.CATEGORY_ERROR)
+ .setAutoCancel(true)
+ .setColor(context.getColor(R.color.system_notification_accent_color))
+ val intent =
+ devicePolicyManager.createAdminSupportIntent(
+ DevicePolicyManager.POLICY_DISABLE_SCREEN_CAPTURE
+ )
+ if (intent != null) {
+ val pendingIntent =
+ PendingIntent.getActivityAsUser(
+ context,
+ 0,
+ intent,
+ PendingIntent.FLAG_IMMUTABLE,
+ null,
+ UserHandle.CURRENT
+ )
+ builder.setContentIntent(pendingIntent)
+ }
+ SystemUIApplication.overrideNotificationAppName(context, builder, true)
+ val notification = Notification.BigTextStyle(builder).bigText(errorMsg).build()
+ // A different id for external displays to keep the 2 error notifications separated.
+ val id =
+ if (displayId == Display.DEFAULT_DISPLAY) {
+ SystemMessageProto.SystemMessage.NOTE_GLOBAL_SCREENSHOT
+ } else {
+ SystemMessageProto.SystemMessage.NOTE_GLOBAL_SCREENSHOT_EXTERNAL_DISPLAY
+ }
+ notificationManager.notify(id, notification)
+ }
+
+ private val externalDisplayString: String
+ get() =
+ res.getString(
+ com.android.systemui.res.R.string.screenshot_failed_external_display_indication
+ )
+
+ /** Factory for [ScreenshotNotificationsController]. */
+ @AssistedFactory
+ interface Factory {
+ fun create(displayId: Int = Display.DEFAULT_DISPLAY): ScreenshotNotificationsController
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotServiceErrorReceiver.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotServiceErrorReceiver.java
index 070fb1e..049799e 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotServiceErrorReceiver.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotServiceErrorReceiver.java
@@ -16,25 +16,29 @@
package com.android.systemui.screenshot;
+import android.app.NotificationManager;
+import android.app.admin.DevicePolicyManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
-import android.view.WindowManager;
+import android.view.Display;
import com.android.systemui.res.R;
/**
- * Performs a number of miscellaneous, non-system-critical actions
- * after the system has finished booting.
+ * Receives errors related to screenshot.
*/
public class ScreenshotServiceErrorReceiver extends BroadcastReceiver {
@Override
public void onReceive(final Context context, Intent intent) {
// Show a message that we've failed to save the image to disk
- WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
- ScreenshotNotificationsController controller =
- new ScreenshotNotificationsController(context, wm);
+ NotificationManager notificationManager = context.getSystemService(
+ NotificationManager.class);
+ DevicePolicyManager devicePolicyManager = context.getSystemService(
+ DevicePolicyManager.class);
+ ScreenshotNotificationsController controller = new ScreenshotNotificationsController(
+ Display.DEFAULT_DISPLAY, context, notificationManager, devicePolicyManager);
controller.notifyScreenshotError(R.string.screenshot_failed_to_save_unknown_text);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotExecutor.kt b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotExecutor.kt
index 03c3f7a..0158284 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotExecutor.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotExecutor.kt
@@ -10,6 +10,8 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.display.data.repository.DisplayRepository
+import com.android.systemui.res.R
+import com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_CAPTURE_FAILED
import com.android.systemui.screenshot.TakeScreenshotService.RequestCallback
import java.util.function.Consumer
import javax.inject.Inject
@@ -34,7 +36,8 @@
displayRepository: DisplayRepository,
@Application private val mainScope: CoroutineScope,
private val screenshotRequestProcessor: ScreenshotRequestProcessor,
- private val uiEventLogger: UiEventLogger
+ private val uiEventLogger: UiEventLogger,
+ private val screenshotNotificationControllerFactory: ScreenshotNotificationsController.Factory,
) {
private lateinit var displays: StateFlow<Set<Display>>
@@ -44,6 +47,7 @@
}
private val screenshotControllers = mutableMapOf<Int, ScreenshotController>()
+ private val notificationControllers = mutableMapOf<Int, ScreenshotNotificationsController>()
/**
* Executes the [ScreenshotRequest].
@@ -58,40 +62,68 @@
) {
val displayIds = getDisplaysToScreenshot(screenshotRequest.type)
val resultCallbackWrapper = MultiResultCallbackWrapper(requestCallback)
- screenshotRequest.oneForEachDisplay(displayIds).forEach { screenshotData: ScreenshotData ->
+ displayIds.forEach { displayId: Int ->
dispatchToController(
- screenshotData = screenshotData,
+ rawScreenshotData = ScreenshotData.fromRequest(screenshotRequest, displayId),
onSaved =
- if (screenshotData.displayId == Display.DEFAULT_DISPLAY) onSaved else { _ -> },
- callback = resultCallbackWrapper.createCallbackForId(screenshotData.displayId)
+ if (displayId == Display.DEFAULT_DISPLAY) {
+ onSaved
+ } else { _ -> },
+ callback = resultCallbackWrapper.createCallbackForId(displayId)
)
}
}
- /** Creates a [ScreenshotData] for each display. */
- private suspend fun ScreenshotRequest.oneForEachDisplay(
- displayIds: List<Int>
- ): List<ScreenshotData> {
- return displayIds
- .map { displayId -> ScreenshotData.fromRequest(this, displayId) }
- .map { screenshotData: ScreenshotData ->
- screenshotRequestProcessor.process(screenshotData)
- }
- }
-
- private fun dispatchToController(
- screenshotData: ScreenshotData,
+ /** All logging should be triggered only by this method. */
+ private suspend fun dispatchToController(
+ rawScreenshotData: ScreenshotData,
onSaved: (Uri) -> Unit,
callback: RequestCallback
) {
+ // Let's wait before logging "screenshot requested", as we should log the processed
+ // ScreenshotData.
+ val screenshotData =
+ try {
+ screenshotRequestProcessor.process(rawScreenshotData)
+ } catch (e: RequestProcessorException) {
+ Log.e(TAG, "Failed to process screenshot request!", e)
+ logScreenshotRequested(rawScreenshotData)
+ onFailedScreenshotRequest(rawScreenshotData, callback)
+ return
+ }
+
+ logScreenshotRequested(screenshotData)
+ Log.d(TAG, "Screenshot request: $screenshotData")
+ try {
+ getScreenshotController(screenshotData.displayId)
+ .handleScreenshot(screenshotData, onSaved, callback)
+ } catch (e: IllegalStateException) {
+ Log.e(TAG, "Error while ScreenshotController was handling ScreenshotData!", e)
+ onFailedScreenshotRequest(screenshotData, callback)
+ return // After a failure log, nothing else should run.
+ }
+ }
+
+ /**
+ * This should be logged also in case of failed requests, before the [SCREENSHOT_CAPTURE_FAILED]
+ * event.
+ */
+ private fun logScreenshotRequested(screenshotData: ScreenshotData) {
uiEventLogger.log(
ScreenshotEvent.getScreenshotSource(screenshotData.source),
0,
screenshotData.packageNameString
)
- Log.d(TAG, "Screenshot request: $screenshotData")
- getScreenshotController(screenshotData.displayId)
- .handleScreenshot(screenshotData, onSaved, callback)
+ }
+
+ private fun onFailedScreenshotRequest(
+ screenshotData: ScreenshotData,
+ callback: RequestCallback
+ ) {
+ uiEventLogger.log(SCREENSHOT_CAPTURE_FAILED, 0, screenshotData.packageNameString)
+ getNotificationController(screenshotData.displayId)
+ .notifyScreenshotError(R.string.screenshot_failed_to_capture_text)
+ callback.reportError()
}
private fun getDisplaysToScreenshot(requestType: Int): List<Int> {
@@ -135,7 +167,15 @@
}
private fun getScreenshotController(id: Int): ScreenshotController {
- return screenshotControllers.computeIfAbsent(id) { screenshotControllerFactory.create(id) }
+ return screenshotControllers.computeIfAbsent(id) {
+ screenshotControllerFactory.create(id, /* showUIOnExternalDisplay= */ false)
+ }
+ }
+
+ private fun getNotificationController(id: Int): ScreenshotNotificationsController {
+ return notificationControllers.computeIfAbsent(id) {
+ screenshotNotificationControllerFactory.create(id)
+ }
}
/** 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..0991c9a 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
@@ -113,7 +113,8 @@
@Inject
public TakeScreenshotService(ScreenshotController.Factory screenshotControllerFactory,
UserManager userManager, DevicePolicyManager devicePolicyManager,
- UiEventLogger uiEventLogger, ScreenshotNotificationsController notificationsController,
+ UiEventLogger uiEventLogger,
+ ScreenshotNotificationsController.Factory notificationsControllerFactory,
Context context, @Background Executor bgExecutor, FeatureFlags featureFlags,
RequestProcessor processor, Provider<TakeScreenshotExecutor> takeScreenshotExecutor) {
if (DEBUG_SERVICE) {
@@ -123,7 +124,7 @@
mUserManager = userManager;
mDevicePolicyManager = devicePolicyManager;
mUiEventLogger = uiEventLogger;
- mNotificationsController = notificationsController;
+ mNotificationsController = notificationsControllerFactory.create(Display.DEFAULT_DISPLAY);
mContext = context;
mBgExecutor = bgExecutor;
mFeatureFlags = featureFlags;
@@ -132,7 +133,8 @@
if (mFeatureFlags.isEnabled(MULTI_DISPLAY_SCREENSHOT)) {
mScreenshot = null;
} else {
- mScreenshot = screenshotControllerFactory.create(Display.DEFAULT_DISPLAY);
+ mScreenshot = screenshotControllerFactory.create(
+ Display.DEFAULT_DISPLAY, /* showUIOnExternalDisplay= */ false);
}
}
@@ -245,15 +247,17 @@
Log.d(TAG, "Processing screenshot data");
- ScreenshotData screenshotData = ScreenshotData.fromRequest(
- request, Display.DEFAULT_DISPLAY);
+ if (mFeatureFlags.isEnabled(MULTI_DISPLAY_SCREENSHOT)) {
+ mTakeScreenshotExecutor.get().executeScreenshotsAsync(request, onSaved, callback);
+ return;
+ }
+ // TODO(b/295143676): Delete the following after the flag is released.
try {
- if (mFeatureFlags.isEnabled(MULTI_DISPLAY_SCREENSHOT)) {
- mTakeScreenshotExecutor.get().executeScreenshotsAsync(request, onSaved, callback);
- } else {
- mProcessor.processAsync(screenshotData, (data) ->
- dispatchToController(data, onSaved, callback));
- }
+ ScreenshotData screenshotData = ScreenshotData.fromRequest(
+ request, Display.DEFAULT_DISPLAY);
+ mProcessor.processAsync(screenshotData, (data) ->
+ dispatchToController(data, onSaved, callback));
+
} catch (IllegalStateException e) {
Log.e(TAG, "Failed to process screenshot request!", e);
logFailedRequest(request);
@@ -263,6 +267,7 @@
}
}
+ // TODO(b/295143676): Delete this.
private void dispatchToController(ScreenshotData screenshot,
Consumer<Uri> uriConsumer, RequestCallback callback) {
mUiEventLogger.log(ScreenshotEvent.getScreenshotSource(screenshot.getSource()), 0,
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/brightness/BrightnessDialog.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java
index 6783afa..1ecb127 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java
@@ -21,6 +21,7 @@
import static android.view.WindowManagerPolicyConstants.EXTRA_FROM_BRIGHTNESS_KEY;
import android.app.Activity;
+import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Bundle;
import android.view.Gravity;
@@ -35,8 +36,8 @@
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.Main;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
import com.android.systemui.util.concurrency.DelayableExecutor;
@@ -74,21 +75,26 @@
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ setWindowAttributes();
+ setContentView(R.layout.brightness_mirror_container);
+ setBrightnessDialogViewAttributes();
+ }
+ private void setWindowAttributes() {
final Window window = getWindow();
- window.setGravity(Gravity.TOP | Gravity.CENTER_HORIZONTAL);
+ window.setGravity(Gravity.TOP | Gravity.LEFT);
window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
window.requestFeature(Window.FEATURE_NO_TITLE);
// Calling this creates the decor View, so setLayout takes proper effect
// (see Dialog#onWindowAttributesChanged)
window.getDecorView();
- window.setLayout(
- WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.WRAP_CONTENT);
+ window.setLayout(WRAP_CONTENT, WRAP_CONTENT);
getTheme().applyStyle(R.style.Theme_SystemUI_QuickSettings, false);
+ }
- setContentView(R.layout.brightness_mirror_container);
+ void setBrightnessDialogViewAttributes() {
FrameLayout frame = findViewById(R.id.brightness_mirror_container);
// The brightness mirror container is INVISIBLE by default.
frame.setVisibility(View.VISIBLE);
@@ -97,6 +103,14 @@
getResources().getDimensionPixelSize(R.dimen.notification_side_paddings);
lp.leftMargin = horizontalMargin;
lp.rightMargin = horizontalMargin;
+
+ int verticalMargin =
+ getResources().getDimensionPixelSize(
+ R.dimen.notification_guts_option_vertical_padding);
+
+ lp.topMargin = verticalMargin;
+ lp.bottomMargin = verticalMargin;
+
frame.setLayoutParams(lp);
Rect bounds = new Rect();
frame.addOnLayoutChangeListener(
@@ -113,6 +127,20 @@
frame.addView(controller.getRootView(), MATCH_PARENT, WRAP_CONTENT);
mBrightnessController = mBrightnessControllerFactory.create(controller);
+
+ Configuration configuration = getResources().getConfiguration();
+ int orientation = configuration.orientation;
+
+ if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
+ lp.width = getWindowManager().getDefaultDisplay().getWidth() / 2
+ - lp.leftMargin * 2;
+ } else if (orientation == Configuration.ORIENTATION_PORTRAIT) {
+ lp.width = getWindowManager().getDefaultDisplay().getWidth()
+ - lp.leftMargin * 2;
+ }
+
+ frame.setLayoutParams(lp);
+
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 2ef83dd..8dc97c0 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -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;
@@ -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) {
@@ -1444,9 +1446,7 @@
mBarState);
}
- if (mFeatureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) {
- setKeyguardVisibility(mBarState, false);
- } else {
+ if (!mFeatureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) {
setKeyguardBottomAreaVisibility(mBarState, false);
}
@@ -2356,14 +2356,6 @@
}
}
- private void setKeyguardVisibility(int statusBarState, boolean goingToFullShade) {
- mKeyguardInteractor.setKeyguardRootVisibility(
- statusBarState,
- goingToFullShade,
- mIsOcclusionTransitionRunning
- );
- }
-
@Deprecated
private void setKeyguardBottomAreaVisibility(int statusBarState, boolean goingToFullShade) {
mKeyguardBottomArea.animate().cancel();
@@ -3379,6 +3371,7 @@
mBlockingExpansionForCurrentTouch = isTracking();
}
+ @NeverCompile
@Override
public void dump(PrintWriter pw, String[] args) {
pw.println(TAG + ":");
@@ -4440,11 +4433,13 @@
&& statusBarState == KEYGUARD) {
// This means we're doing the screen off animation - position the keyguard status
// view where it'll be on AOD, so we can animate it in.
- mKeyguardStatusViewController.updatePosition(
- mClockPositionResult.clockX,
- mClockPositionResult.clockYFullyDozing,
- mClockPositionResult.clockScale,
- false /* animate */);
+ if (!mFeatureFlags.isEnabled(Flags.MIGRATE_NSSL)) {
+ mKeyguardStatusViewController.updatePosition(
+ mClockPositionResult.clockX,
+ mClockPositionResult.clockYFullyDozing,
+ mClockPositionResult.clockScale,
+ false /* animate */);
+ }
}
mKeyguardStatusViewController.setKeyguardStatusViewVisibility(
@@ -4453,9 +4448,7 @@
goingToFullShade,
mBarState);
- if (mFeatureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) {
- setKeyguardVisibility(statusBarState, goingToFullShade);
- } else {
+ if (!mFeatureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) {
setKeyguardBottomAreaVisibility(statusBarState, goingToFullShade);
}
@@ -4559,7 +4552,12 @@
public void showAodUi() {
setDozing(true /* dozing */, false /* animate */);
mStatusBarStateController.setUpcomingState(KEYGUARD);
- mStatusBarStateListener.onStateChanged(KEYGUARD);
+
+ if (mFeatureFlags.isEnabled(Flags.MIGRATE_NSSL)) {
+ mStatusBarStateController.setState(KEYGUARD);
+ } else {
+ mStatusBarStateListener.onStateChanged(KEYGUARD);
+ }
mStatusBarStateListener.onDozeAmountChanged(1f, 1f);
setExpandedFraction(1f);
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
index 5414b3f..d05dfe2 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
@@ -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;
@@ -106,7 +106,7 @@
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;
@@ -185,7 +185,7 @@
SystemClock clock,
BouncerMessageInteractor bouncerMessageInteractor,
BouncerLogger bouncerLogger,
- KeyEventInteractor keyEventInteractor,
+ SysUIKeyEventHandler sysUIKeyEventHandler,
PrimaryBouncerInteractor primaryBouncerInteractor,
AlternateBouncerInteractor alternateBouncerInteractor) {
mLockscreenShadeTransitionController = transitionController;
@@ -214,7 +214,7 @@
mNotificationInsetsController = notificationInsetsController;
mIsTrackpadCommonEnabled = featureFlags.isEnabled(TRACKPAD_GESTURE_COMMON);
mFeatureFlags = featureFlags;
- mKeyEventInteractor = keyEventInteractor;
+ mSysUIKeyEventHandler = sysUIKeyEventHandler;
mPrimaryBouncerInteractor = primaryBouncerInteractor;
mAlternateBouncerInteractor = alternateBouncerInteractor;
@@ -529,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/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/domain/interactor/ShadeInteractor.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt
index 6117f9f..e487a6f 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt
@@ -261,7 +261,7 @@
when (state) {
is ObservableTransitionState.Idle -> false
is ObservableTransitionState.Transition ->
- state.isInitiatedByUserInput &&
+ state.isUserInputDriven &&
(state.toScene == sceneKey || state.fromScene == sceneKey)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/MediaArtworkProcessor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/MediaArtworkProcessor.kt
deleted file mode 100644
index 17b4e3b..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/MediaArtworkProcessor.kt
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.systemui.statusbar
-
-import android.content.Context
-import android.graphics.Bitmap
-import android.graphics.Canvas
-import android.graphics.Point
-import android.graphics.Rect
-import android.renderscript.Allocation
-import android.renderscript.Element
-import android.renderscript.RenderScript
-import android.renderscript.ScriptIntrinsicBlur
-import android.util.Log
-import android.util.MathUtils
-import com.android.internal.graphics.ColorUtils
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.statusbar.notification.MediaNotificationProcessor
-import javax.inject.Inject
-
-private const val TAG = "MediaArtworkProcessor"
-private const val COLOR_ALPHA = (255 * 0.7f).toInt()
-private const val BLUR_RADIUS = 25f
-private const val DOWNSAMPLE = 6
-
-@SysUISingleton
-class MediaArtworkProcessor @Inject constructor() {
-
- private val mTmpSize = Point()
- private var mArtworkCache: Bitmap? = null
-
- fun processArtwork(context: Context, artwork: Bitmap): Bitmap? {
- if (mArtworkCache != null) {
- return mArtworkCache
- }
- val renderScript = RenderScript.create(context)
- val blur = ScriptIntrinsicBlur.create(renderScript, Element.U8_4(renderScript))
- var input: Allocation? = null
- var output: Allocation? = null
- var inBitmap: Bitmap? = null
- try {
- @Suppress("DEPRECATION")
- context.display?.getSize(mTmpSize)
- val rect = Rect(0, 0, artwork.width, artwork.height)
- MathUtils.fitRect(rect, Math.max(mTmpSize.x / DOWNSAMPLE, mTmpSize.y / DOWNSAMPLE))
- inBitmap = Bitmap.createScaledBitmap(artwork, rect.width(), rect.height(),
- true /* filter */)
- // Render script blurs only support ARGB_8888, we need a conversion if we got a
- // different bitmap config.
- if (inBitmap.config != Bitmap.Config.ARGB_8888) {
- val oldIn = inBitmap
- inBitmap = oldIn.copy(Bitmap.Config.ARGB_8888, false /* isMutable */)
- oldIn.recycle()
- }
- val outBitmap = Bitmap.createBitmap(inBitmap?.width ?: 0, inBitmap?.height ?: 0,
- Bitmap.Config.ARGB_8888)
-
- input = Allocation.createFromBitmap(renderScript, inBitmap,
- Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_GRAPHICS_TEXTURE)
- output = Allocation.createFromBitmap(renderScript, outBitmap)
-
- blur.setRadius(BLUR_RADIUS)
- blur.setInput(input)
- blur.forEach(output)
- output.copyTo(outBitmap)
-
- val swatch = MediaNotificationProcessor.findBackgroundSwatch(artwork)
-
- val canvas = Canvas(outBitmap)
- canvas.drawColor(ColorUtils.setAlphaComponent(swatch.rgb, COLOR_ALPHA))
- return outBitmap
- } catch (ex: IllegalArgumentException) {
- Log.e(TAG, "error while processing artwork", ex)
- return null
- } finally {
- input?.destroy()
- output?.destroy()
- blur.destroy()
- inBitmap?.recycle()
- }
- }
-
- fun clearCache() {
- mArtworkCache?.recycle()
- mArtworkCache = null
- }
-}
\ No newline at end of file
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/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
index 5bd40b8..389486f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
@@ -15,43 +15,29 @@
*/
package com.android.systemui.statusbar;
-import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
-import static com.android.systemui.statusbar.phone.CentralSurfaces.DEBUG_MEDIA_FAKE_ARTWORK;
-import static com.android.systemui.statusbar.phone.CentralSurfaces.ENABLE_LOCKSCREEN_WALLPAPER;
-import static com.android.systemui.statusbar.phone.CentralSurfaces.SHOW_LOCKSCREEN_MEDIA_ARTWORK;
-
-import android.annotation.MainThread;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.Notification;
import android.app.WallpaperManager;
import android.content.Context;
-import android.graphics.Bitmap;
import android.graphics.Point;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.ColorDrawable;
-import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.hardware.display.DisplayManager;
import android.media.MediaMetadata;
import android.media.session.MediaController;
import android.media.session.MediaSession;
import android.media.session.PlaybackState;
-import android.os.AsyncTask;
import android.os.Trace;
import android.service.notification.NotificationStats;
import android.service.notification.StatusBarNotification;
-import android.util.ArraySet;
import android.util.Log;
import android.view.Display;
import android.view.View;
import android.widget.ImageView;
-import com.android.app.animation.Interpolators;
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.Dumpable;
import com.android.systemui.colorextraction.SysuiColorExtractor;
-import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.media.controls.models.player.MediaData;
import com.android.systemui.media.controls.models.recommendation.SmartspaceMediaData;
@@ -65,18 +51,11 @@
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
import com.android.systemui.statusbar.phone.BiometricUnlockController;
-import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.LockscreenWallpaper;
import com.android.systemui.statusbar.phone.ScrimController;
-import com.android.systemui.statusbar.phone.ScrimState;
import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.util.Utils;
-import com.android.systemui.util.concurrency.DelayableExecutor;
-
-import dagger.Lazy;
import java.io.PrintWriter;
-import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -85,7 +64,6 @@
import java.util.List;
import java.util.Objects;
import java.util.Optional;
-import java.util.Set;
import java.util.stream.Collectors;
/**
@@ -99,7 +77,6 @@
private final StatusBarStateController mStatusBarStateController;
private final SysuiColorExtractor mColorExtractor;
private final KeyguardStateController mKeyguardStateController;
- private final KeyguardBypassController mKeyguardBypassController;
private static final HashSet<Integer> PAUSED_MEDIA_STATES = new HashSet<>();
private static final HashSet<Integer> CONNECTING_MEDIA_STATES = new HashSet<>();
static {
@@ -117,9 +94,6 @@
private final NotifCollection mNotifCollection;
@Nullable
- private Lazy<NotificationShadeWindowController> mNotificationShadeWindowController;
-
- @Nullable
private BiometricUnlockController mBiometricUnlockController;
@Nullable
private ScrimController mScrimController;
@@ -128,12 +102,8 @@
@VisibleForTesting
boolean mIsLockscreenLiveWallpaperEnabled;
- private final DelayableExecutor mMainExecutor;
-
private final Context mContext;
private final ArrayList<MediaListener> mMediaListeners;
- private final MediaArtworkProcessor mMediaArtworkProcessor;
- private final Set<AsyncTask<?, ?, ?>> mProcessArtworkTasks = new ArraySet<>();
protected NotificationPresenter mPresenter;
private MediaController mMediaController;
@@ -150,8 +120,6 @@
private List<String> mSmallerInternalDisplayUids;
private Display mCurrentDisplay;
- private LockscreenWallpaper.WallpaperDrawable mWallapperDrawable;
-
private final MediaController.Callback mMediaListener = new MediaController.Callback() {
@Override
public void onPlaybackStateChanged(PlaybackState state) {
@@ -173,7 +141,6 @@
if (DEBUG_MEDIA) {
Log.v(TAG, "DEBUG_MEDIA: onMetadataChanged: " + metadata);
}
- mMediaArtworkProcessor.clearCache();
mMediaMetadata = metadata;
dispatchUpdateMediaMetaData(true /* changed */, true /* allowAnimation */);
}
@@ -184,13 +151,9 @@
*/
public NotificationMediaManager(
Context context,
- Lazy<NotificationShadeWindowController> notificationShadeWindowController,
NotificationVisibilityProvider visibilityProvider,
- MediaArtworkProcessor mediaArtworkProcessor,
- KeyguardBypassController keyguardBypassController,
NotifPipeline notifPipeline,
NotifCollection notifCollection,
- @Main DelayableExecutor mainExecutor,
MediaDataManager mediaDataManager,
StatusBarStateController statusBarStateController,
SysuiColorExtractor colorExtractor,
@@ -199,12 +162,8 @@
WallpaperManager wallpaperManager,
DisplayManager displayManager) {
mContext = context;
- mMediaArtworkProcessor = mediaArtworkProcessor;
- mKeyguardBypassController = keyguardBypassController;
mMediaListeners = new ArrayList<>();
- mNotificationShadeWindowController = notificationShadeWindowController;
mVisibilityProvider = visibilityProvider;
- mMainExecutor = mainExecutor;
mMediaDataManager = mediaDataManager;
mNotifPipeline = notifPipeline;
mNotifCollection = notifCollection;
@@ -476,7 +435,6 @@
}
private void clearCurrentMediaNotificationSession() {
- mMediaArtworkProcessor.clearCache();
mMediaMetadata = null;
if (mMediaController != null) {
if (DEBUG_MEDIA) {
@@ -494,9 +452,6 @@
public void onDisplayUpdated(Display display) {
Trace.beginSection("NotificationMediaManager#onDisplayUpdated");
mCurrentDisplay = display;
- if (mWallapperDrawable != null) {
- mWallapperDrawable.onDisplayUpdated(isOnSmallerInternalDisplays());
- }
Trace.endSection();
}
@@ -531,18 +486,13 @@
}
/**
- * Refresh or remove lockscreen artwork from media metadata or the lockscreen wallpaper.
+ * Update media state of lockscreen media views and controllers .
*/
- public void updateMediaMetaData(boolean metaDataChanged, boolean allowEnterAnimation) {
+ public void updateMediaMetaData(boolean metaDataChanged) {
if (mIsLockscreenLiveWallpaperEnabled) return;
Trace.beginSection("CentralSurfaces#updateMediaMetaData");
- if (!SHOW_LOCKSCREEN_MEDIA_ARTWORK) {
- Trace.endSection();
- return;
- }
-
if (getBackDropView() == null) {
Trace.endSection();
return; // called too early
@@ -566,170 +516,14 @@
+ " state=" + mStatusBarStateController.getState());
}
- Bitmap artworkBitmap = null;
- if (mediaMetadata != null && !mKeyguardBypassController.getBypassEnabled()) {
- artworkBitmap = mediaMetadata.getBitmap(MediaMetadata.METADATA_KEY_ART);
- if (artworkBitmap == null) {
- artworkBitmap = mediaMetadata.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART);
- }
- }
-
- // Process artwork on a background thread and send the resulting bitmap to
- // finishUpdateMediaMetaData.
- if (metaDataChanged) {
- for (AsyncTask<?, ?, ?> task : mProcessArtworkTasks) {
- task.cancel(true);
- }
- mProcessArtworkTasks.clear();
- }
- if (artworkBitmap != null && !Utils.useQsMediaPlayer(mContext)) {
- mProcessArtworkTasks.add(new ProcessArtworkTask(this, metaDataChanged,
- allowEnterAnimation).execute(artworkBitmap));
- } else {
- finishUpdateMediaMetaData(metaDataChanged, allowEnterAnimation, null);
+ mColorExtractor.setHasMediaArtwork(false);
+ if (mScrimController != null) {
+ mScrimController.setHasBackdrop(false);
}
Trace.endSection();
}
- private void finishUpdateMediaMetaData(boolean metaDataChanged, boolean allowEnterAnimation,
- @Nullable Bitmap bmp) {
- Drawable artworkDrawable = null;
- if (bmp != null) {
- artworkDrawable = new BitmapDrawable(mBackdropBack.getResources(), bmp);
- }
- boolean hasMediaArtwork = artworkDrawable != null;
- boolean allowWhenShade = false;
- if (ENABLE_LOCKSCREEN_WALLPAPER && artworkDrawable == null) {
- Bitmap lockWallpaper =
- mLockscreenWallpaper != null ? mLockscreenWallpaper.getBitmap() : null;
- if (lockWallpaper != null) {
- artworkDrawable = new LockscreenWallpaper.WallpaperDrawable(
- mBackdropBack.getResources(), lockWallpaper, isOnSmallerInternalDisplays());
- // We're in the SHADE mode on the SIM screen - yet we still need to show
- // the lockscreen wallpaper in that mode.
- allowWhenShade = mStatusBarStateController.getState() == KEYGUARD;
- }
- }
-
- NotificationShadeWindowController windowController =
- mNotificationShadeWindowController.get();
- boolean hideBecauseOccluded = mKeyguardStateController.isOccluded();
-
- final boolean hasArtwork = artworkDrawable != null;
- mColorExtractor.setHasMediaArtwork(hasMediaArtwork);
- if (mScrimController != null) {
- mScrimController.setHasBackdrop(hasArtwork);
- }
-
- if ((hasArtwork || DEBUG_MEDIA_FAKE_ARTWORK)
- && (mStatusBarStateController.getState() != StatusBarState.SHADE || allowWhenShade)
- && mBiometricUnlockController != null && mBiometricUnlockController.getMode()
- != BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING
- && !hideBecauseOccluded) {
- // time to show some art!
- if (mBackdrop.getVisibility() != View.VISIBLE) {
- mBackdrop.setVisibility(View.VISIBLE);
- if (allowEnterAnimation) {
- mBackdrop.setAlpha(0);
- mBackdrop.animate().alpha(1f);
- } else {
- mBackdrop.animate().cancel();
- mBackdrop.setAlpha(1f);
- }
- if (windowController != null) {
- windowController.setBackdropShowing(true);
- }
- metaDataChanged = true;
- if (DEBUG_MEDIA) {
- Log.v(TAG, "DEBUG_MEDIA: Fading in album artwork");
- }
- }
- if (metaDataChanged) {
- if (mBackdropBack.getDrawable() != null) {
- Drawable drawable =
- mBackdropBack.getDrawable().getConstantState()
- .newDrawable(mBackdropFront.getResources()).mutate();
- mBackdropFront.setImageDrawable(drawable);
- mBackdropFront.setAlpha(1f);
- mBackdropFront.setVisibility(View.VISIBLE);
- } else {
- mBackdropFront.setVisibility(View.INVISIBLE);
- }
-
- if (DEBUG_MEDIA_FAKE_ARTWORK) {
- final int c = 0xFF000000 | (int)(Math.random() * 0xFFFFFF);
- Log.v(TAG, String.format("DEBUG_MEDIA: setting new color: 0x%08x", c));
- mBackdropBack.setBackgroundColor(0xFFFFFFFF);
- mBackdropBack.setImageDrawable(new ColorDrawable(c));
- } else {
- if (artworkDrawable instanceof LockscreenWallpaper.WallpaperDrawable) {
- mWallapperDrawable =
- (LockscreenWallpaper.WallpaperDrawable) artworkDrawable;
- }
- mBackdropBack.setImageDrawable(artworkDrawable);
- }
-
- if (mBackdropFront.getVisibility() == View.VISIBLE) {
- if (DEBUG_MEDIA) {
- Log.v(TAG, "DEBUG_MEDIA: Crossfading album artwork from "
- + mBackdropFront.getDrawable()
- + " to "
- + mBackdropBack.getDrawable());
- }
- mBackdropFront.animate()
- .setDuration(250)
- .alpha(0f).withEndAction(mHideBackdropFront);
- }
- }
- } else {
- // need to hide the album art, either because we are unlocked, on AOD
- // or because the metadata isn't there to support it
- if (mBackdrop.getVisibility() != View.GONE) {
- if (DEBUG_MEDIA) {
- Log.v(TAG, "DEBUG_MEDIA: Fading out album artwork");
- }
- boolean cannotAnimateDoze = mStatusBarStateController.isDozing()
- && !ScrimState.AOD.getAnimateChange();
- if (((mBiometricUnlockController != null && mBiometricUnlockController.getMode()
- == BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING
- || cannotAnimateDoze))
- || hideBecauseOccluded) {
- // We are unlocking directly - no animation!
- mBackdrop.setVisibility(View.GONE);
- mBackdropBack.setImageDrawable(null);
- if (windowController != null) {
- windowController.setBackdropShowing(false);
- }
- } else {
- if (windowController != null) {
- windowController.setBackdropShowing(false);
- }
- mBackdrop.animate()
- .alpha(0)
- .setInterpolator(Interpolators.ACCELERATE_DECELERATE)
- .setDuration(300)
- .setStartDelay(0)
- .withEndAction(() -> {
- mBackdrop.setVisibility(View.GONE);
- mBackdropFront.animate().cancel();
- mBackdropBack.setImageDrawable(null);
- mMainExecutor.execute(mHideBackdropFront);
- });
- if (mKeyguardStateController.isKeyguardFadingAway()) {
- mBackdrop.animate()
- .setDuration(
- mKeyguardStateController.getShortenedFadingAwayDuration())
- .setStartDelay(
- mKeyguardStateController.getKeyguardFadingAwayDelay())
- .setInterpolator(Interpolators.LINEAR)
- .start();
- }
- }
- }
- }
- }
-
public void setup(BackDropView backdrop, ImageView backdropFront, ImageView backdropBack,
ScrimController scrimController, LockscreenWallpaper lockscreenWallpaper) {
mBackdrop = backdrop;
@@ -758,15 +552,6 @@
}
};
- private Bitmap processArtwork(Bitmap artwork) {
- return mMediaArtworkProcessor.processArtwork(mContext, artwork);
- }
-
- @MainThread
- private void removeTask(AsyncTask<?, ?, ?> task) {
- mProcessArtworkTasks.remove(task);
- }
-
// TODO(b/273443374): remove
public boolean isLockscreenWallpaperOnNotificationShade() {
return mBackdrop != null && mLockscreenWallpaper != null
@@ -780,52 +565,6 @@
return mBackdrop;
}
- /**
- * {@link AsyncTask} to prepare album art for use as backdrop on lock screen.
- */
- private static final class ProcessArtworkTask extends AsyncTask<Bitmap, Void, Bitmap> {
-
- private final WeakReference<NotificationMediaManager> mManagerRef;
- private final boolean mMetaDataChanged;
- private final boolean mAllowEnterAnimation;
-
- ProcessArtworkTask(NotificationMediaManager manager, boolean changed,
- boolean allowAnimation) {
- mManagerRef = new WeakReference<>(manager);
- mMetaDataChanged = changed;
- mAllowEnterAnimation = allowAnimation;
- }
-
- @Override
- protected Bitmap doInBackground(Bitmap... bitmaps) {
- NotificationMediaManager manager = mManagerRef.get();
- if (manager == null || bitmaps.length == 0 || isCancelled()) {
- return null;
- }
- return manager.processArtwork(bitmaps[0]);
- }
-
- @Override
- protected void onPostExecute(@Nullable Bitmap result) {
- NotificationMediaManager manager = mManagerRef.get();
- if (manager != null && !isCancelled()) {
- manager.removeTask(this);
- manager.finishUpdateMediaMetaData(mMetaDataChanged, mAllowEnterAnimation, result);
- }
- }
-
- @Override
- protected void onCancelled(Bitmap result) {
- if (result != null) {
- result.recycle();
- }
- NotificationMediaManager manager = mManagerRef.get();
- if (manager != null) {
- manager.removeTask(this);
- }
- }
- }
-
public interface MediaListener {
/**
* Called whenever there's new metadata or playback state.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
index d4b6dfb..61ebcc0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
@@ -15,6 +15,7 @@
*/
package com.android.systemui.statusbar;
+
import android.app.ActivityManager;
import android.app.ActivityOptions;
import android.app.KeyguardManager;
@@ -48,10 +49,10 @@
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.statusbar.NotificationVisibility;
import com.android.systemui.Dumpable;
-import com.android.systemui.res.R;
import com.android.systemui.dump.DumpManager;
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.statusbar.dagger.CentralSurfacesDependenciesModule;
import com.android.systemui.statusbar.notification.NotifPipelineFlags;
import com.android.systemui.statusbar.notification.RemoteInputControllerLogger;
@@ -535,7 +536,8 @@
public void cleanUpRemoteInputForUserRemoval(NotificationEntry entry) {
if (isRemoteInputActive(entry)) {
entry.mRemoteEditImeVisible = false;
- mRemoteInputController.removeRemoteInput(entry, null);
+ mRemoteInputController.removeRemoteInput(entry, null,
+ /* reason= */"RemoteInputManager#cleanUpRemoteInputForUserRemoval");
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index 09ad55e..23b697e 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;
@@ -133,7 +133,7 @@
public void bind(AmbientState ambientState,
NotificationStackScrollLayoutController hostLayoutController) {
- mShelfRefactor.assertDisabled();
+ mShelfRefactor.assertInLegacyMode();
mAmbientState = ambientState;
mHostLayoutController = hostLayoutController;
hostLayoutController.setOnNotificationRemovedListener((child, isTransferInProgress) -> {
@@ -143,7 +143,7 @@
public void bind(AmbientState ambientState, NotificationStackScrollLayout hostLayout,
NotificationRoundnessManager roundnessManager) {
- if (!mShelfRefactor.expectEnabled()) return;
+ if (mShelfRefactor.isUnexpectedlyInLegacyMode()) return;
mAmbientState = ambientState;
mHostLayout = hostLayout;
mRoundnessManager = roundnessManager;
@@ -964,7 +964,7 @@
@Override
public void onStateChanged(int newState) {
- mShelfRefactor.assertDisabled();
+ mShelfRefactor.assertInLegacyMode();
mStatusBarState = newState;
updateInteractiveness();
}
@@ -1022,17 +1022,17 @@
}
public void setController(NotificationShelfController notificationShelfController) {
- mShelfRefactor.assertDisabled();
+ mShelfRefactor.assertInLegacyMode();
mController = notificationShelfController;
}
public void setCanModifyColorOfNotifications(boolean canModifyColorOfNotifications) {
- if (!mShelfRefactor.expectEnabled()) return;
+ if (mShelfRefactor.isUnexpectedlyInLegacyMode()) return;
mCanModifyColorOfNotifications = canModifyColorOfNotifications;
}
public void setCanInteract(boolean canInteract) {
- if (!mShelfRefactor.expectEnabled()) return;
+ if (mShelfRefactor.isUnexpectedlyInLegacyMode()) return;
mCanInteract = canInteract;
updateInteractiveness();
}
@@ -1050,7 +1050,7 @@
}
public void requestRoundnessResetFor(ExpandableView child) {
- if (!mShelfRefactor.expectEnabled()) return;
+ if (mShelfRefactor.isUnexpectedlyInLegacyMode()) return;
child.requestRoundnessReset(SHELF_SCROLL);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java b/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java
index d5e4902..17da015 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java
@@ -31,6 +31,8 @@
import com.android.systemui.statusbar.policy.RemoteInputView;
import com.android.systemui.util.DumpUtilsKt;
+import com.google.errorprone.annotations.CompileTimeConstant;
+
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Objects;
@@ -69,7 +71,8 @@
* @param entry the entry for which a remote input is now active.
* @param token a token identifying the view that is managing the remote input
*/
- public void addRemoteInput(NotificationEntry entry, Object token) {
+ public void addRemoteInput(NotificationEntry entry, Object token,
+ @CompileTimeConstant String reason) {
Objects.requireNonNull(entry);
Objects.requireNonNull(token);
boolean isActive = isRemoteInputActive(entry);
@@ -77,7 +80,9 @@
entry /* contains */, null /* remove */, token /* removeToken */);
mLogger.logAddRemoteInput(entry.getKey()/* entryKey */,
isActive /* isRemoteInputAlreadyActive */,
- found /* isRemoteInputFound */);
+ found /* isRemoteInputFound */,
+ reason /* reason */,
+ entry.getNotificationStyle()/* notificationStyle */);
if (!found) {
mOpen.add(new Pair<>(new WeakReference<>(entry), token));
}
@@ -96,7 +101,8 @@
* the entry is only removed if the token matches the last added token for this
* entry. If null, the entry is removed regardless.
*/
- public void removeRemoteInput(NotificationEntry entry, Object token) {
+ public void removeRemoteInput(NotificationEntry entry, Object token,
+ @CompileTimeConstant String reason) {
Objects.requireNonNull(entry);
if (entry.mRemoteEditImeVisible && entry.mRemoteEditImeAnimatingAway) {
mLogger.logRemoveRemoteInput(
@@ -104,9 +110,12 @@
true /* remoteEditImeVisible */,
true /* remoteEditImeAnimatingAway */,
isRemoteInputActive(entry) /* isRemoteInputActiveForEntry */,
- isRemoteInputActive() /* isRemoteInputActive */);
+ isRemoteInputActive() /* isRemoteInputActive */,
+ reason /* reason */,
+ entry.getNotificationStyle()/* notificationStyle */);
return;
}
+
// If the view is being removed, this may be called even though we're not active
boolean remoteInputActiveForEntry = isRemoteInputActive(entry);
mLogger.logRemoveRemoteInput(
@@ -114,7 +123,9 @@
entry.mRemoteEditImeVisible /* remoteEditImeVisible */,
entry.mRemoteEditImeAnimatingAway /* remoteEditImeAnimatingAway */,
remoteInputActiveForEntry /* isRemoteInputActiveForEntry */,
- isRemoteInputActive()/* isRemoteInputActive */);
+ isRemoteInputActive()/* isRemoteInputActive */,
+ reason/* reason */,
+ entry.getNotificationStyle()/* notificationStyle */);
if (!remoteInputActiveForEntry) return;
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/dagger/CentralSurfacesDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
index 7f5829d..125c8efe 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
@@ -31,7 +31,6 @@
import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpHandler;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
@@ -45,12 +44,10 @@
import com.android.systemui.shade.carrier.ShadeCarrierGroupController;
import com.android.systemui.statusbar.ActionClickLogger;
import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.MediaArtworkProcessor;
import com.android.systemui.statusbar.NotificationClickNotifier;
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.StatusBarStateControllerImpl;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
@@ -61,7 +58,6 @@
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
import com.android.systemui.statusbar.phone.CentralSurfacesImpl;
-import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.ManagedProfileController;
import com.android.systemui.statusbar.phone.ManagedProfileControllerImpl;
import com.android.systemui.statusbar.phone.StatusBarIconController;
@@ -71,7 +67,6 @@
import com.android.systemui.statusbar.phone.StatusBarRemoteInputCallback;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.RemoteInputUriController;
-import com.android.systemui.util.concurrency.DelayableExecutor;
import dagger.Binds;
import dagger.Lazy;
@@ -122,13 +117,9 @@
@Provides
static NotificationMediaManager provideNotificationMediaManager(
Context context,
- Lazy<NotificationShadeWindowController> notificationShadeWindowController,
NotificationVisibilityProvider visibilityProvider,
- MediaArtworkProcessor mediaArtworkProcessor,
- KeyguardBypassController keyguardBypassController,
NotifPipeline notifPipeline,
NotifCollection notifCollection,
- @Main DelayableExecutor mainExecutor,
MediaDataManager mediaDataManager,
StatusBarStateController statusBarStateController,
SysuiColorExtractor colorExtractor,
@@ -138,13 +129,9 @@
DisplayManager displayManager) {
return new NotificationMediaManager(
context,
- notificationShadeWindowController,
visibilityProvider,
- mediaArtworkProcessor,
- keyguardBypassController,
notifPipeline,
notifCollection,
- mainExecutor,
mediaDataManager,
statusBarStateController,
colorExtractor,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/MediaNotificationProcessor.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/MediaNotificationProcessor.java
deleted file mode 100644
index 732c115..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/MediaNotificationProcessor.java
+++ /dev/null
@@ -1,131 +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.systemui.statusbar.notification;
-
-import android.graphics.Bitmap;
-import android.graphics.Color;
-
-import androidx.palette.graphics.Palette;
-
-import java.util.List;
-
-/**
- * A gutted class that now contains only a color extraction utility used by the
- * MediaArtworkProcessor, which has otherwise supplanted this.
- *
- * TODO(b/182926117): move this into MediaArtworkProcessor.kt
- */
-public class MediaNotificationProcessor {
-
- /**
- * The population fraction to select a white or black color as the background over a color.
- */
- private static final float POPULATION_FRACTION_FOR_WHITE_OR_BLACK = 2.5f;
- private static final float BLACK_MAX_LIGHTNESS = 0.08f;
- private static final float WHITE_MIN_LIGHTNESS = 0.90f;
- private static final int RESIZE_BITMAP_AREA = 150 * 150;
-
- private MediaNotificationProcessor() {
- }
-
- /**
- * Finds an appropriate background swatch from media artwork.
- *
- * @param artwork Media artwork
- * @return Swatch that should be used as the background of the media notification.
- */
- public static Palette.Swatch findBackgroundSwatch(Bitmap artwork) {
- return findBackgroundSwatch(generateArtworkPaletteBuilder(artwork).generate());
- }
-
- /**
- * Finds an appropriate background swatch from the palette of media artwork.
- *
- * @param palette Artwork palette, should be obtained from {@link generateArtworkPaletteBuilder}
- * @return Swatch that should be used as the background of the media notification.
- */
- public static Palette.Swatch findBackgroundSwatch(Palette palette) {
- // by default we use the dominant palette
- Palette.Swatch dominantSwatch = palette.getDominantSwatch();
- if (dominantSwatch == null) {
- return new Palette.Swatch(Color.WHITE, 100);
- }
-
- if (!isWhiteOrBlack(dominantSwatch.getHsl())) {
- return dominantSwatch;
- }
- // Oh well, we selected black or white. Lets look at the second color!
- List<Palette.Swatch> swatches = palette.getSwatches();
- float highestNonWhitePopulation = -1;
- Palette.Swatch second = null;
- for (Palette.Swatch swatch : swatches) {
- if (swatch != dominantSwatch
- && swatch.getPopulation() > highestNonWhitePopulation
- && !isWhiteOrBlack(swatch.getHsl())) {
- second = swatch;
- highestNonWhitePopulation = swatch.getPopulation();
- }
- }
- if (second == null) {
- return dominantSwatch;
- }
- if (dominantSwatch.getPopulation() / highestNonWhitePopulation
- > POPULATION_FRACTION_FOR_WHITE_OR_BLACK) {
- // The dominant swatch is very dominant, lets take it!
- // We're not filtering on white or black
- return dominantSwatch;
- } else {
- return second;
- }
- }
-
- /**
- * Generate a palette builder for media artwork.
- *
- * For producing a smooth background transition, the palette is extracted from only the left
- * side of the artwork.
- *
- * @param artwork Media artwork
- * @return Builder that generates the {@link Palette} for the media artwork.
- */
- public static Palette.Builder generateArtworkPaletteBuilder(Bitmap artwork) {
- // for the background we only take the left side of the image to ensure
- // a smooth transition
- return Palette.from(artwork)
- .setRegion(0, 0, artwork.getWidth() / 2, artwork.getHeight())
- .clearFilters() // we want all colors, red / white / black ones too!
- .resizeBitmapArea(RESIZE_BITMAP_AREA);
- }
-
- private static boolean isWhiteOrBlack(float[] hsl) {
- return isBlack(hsl) || isWhite(hsl);
- }
-
- /**
- * @return true if the color represents a color which is close to black.
- */
- private static boolean isBlack(float[] hslColor) {
- return hslColor[2] <= BLACK_MAX_LIGHTNESS;
- }
-
- /**
- * @return true if the color represents a color which is close to white.
- */
- private static boolean isWhite(float[] hslColor) {
- return hslColor[2] >= WHITE_MIN_LIGHTNESS;
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/RemoteInputControllerLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/RemoteInputControllerLogger.kt
index 39b999c..ff89c62 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/RemoteInputControllerLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/RemoteInputControllerLogger.kt
@@ -32,17 +32,24 @@
fun logAddRemoteInput(
entryKey: String,
isRemoteInputAlreadyActive: Boolean,
- isRemoteInputFound: Boolean
+ isRemoteInputFound: Boolean,
+ reason: String,
+ notificationStyle: String
) =
logBuffer.log(
TAG,
DEBUG,
{
str1 = entryKey
+ str2 = reason
+ str3 = notificationStyle
bool1 = isRemoteInputAlreadyActive
bool2 = isRemoteInputFound
},
- { "addRemoteInput entry: $str1, isAlreadyActive: $bool1, isFound:$bool2" }
+ {
+ "addRemoteInput reason:$str2 entry: $str1, style:$str3" +
+ ", isAlreadyActive: $bool1, isFound:$bool2"
+ }
)
/** logs removeRemoteInput invocation of [RemoteInputController] */
@@ -52,20 +59,25 @@
remoteEditImeVisible: Boolean,
remoteEditImeAnimatingAway: Boolean,
isRemoteInputActiveForEntry: Boolean,
- isRemoteInputActive: Boolean
+ isRemoteInputActive: Boolean,
+ reason: String,
+ notificationStyle: String
) =
logBuffer.log(
TAG,
DEBUG,
{
str1 = entryKey
+ str2 = reason
+ str3 = notificationStyle
bool1 = remoteEditImeVisible
bool2 = remoteEditImeAnimatingAway
bool3 = isRemoteInputActiveForEntry
bool4 = isRemoteInputActive
},
{
- "removeRemoteInput entry: $str1, remoteEditImeVisible: $bool1" +
+ "removeRemoteInput reason: $str2 entry: $str1" +
+ ", style: $str3, remoteEditImeVisible: $bool1" +
", remoteEditImeAnimatingAway: $bool2, isRemoteInputActiveForEntry: $bool3" +
", isRemoteInputActive: $bool4"
}
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/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index affd2d1..4573d59 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -979,6 +979,19 @@
return mExpandAnimationRunning;
}
+ /**
+ * @return NotificationStyle
+ */
+ public String getNotificationStyle() {
+ if (isSummaryWithChildren()) {
+ return "summary";
+ }
+
+ final Class<? extends Notification.Style> style =
+ getSbn().getNotification().getNotificationStyle();
+ return style == null ? "nostyle" : style.getSimpleName();
+ }
+
/** Information about a suggestion that is being edited. */
public static class EditedSuggestionInfo {
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/icon/ui/viewbinder/NotificationIconAreaControllerViewBinderWrapperImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconAreaControllerViewBinderWrapperImpl.kt
index 805a4db..eb5c1fa 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
@@ -34,7 +34,7 @@
import com.android.systemui.demomode.DemoModeController
import com.android.systemui.flags.FeatureFlagsClassic
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.res.R
import com.android.systemui.statusbar.NotificationListener
@@ -99,7 +99,7 @@
private val contrastColorUtil: ContrastColorUtil = ContrastColorUtil.getInstance(context)
private val updateStatusBarIcons = Runnable { updateStatusBarIcons() }
- private val shelfRefactor = ViewRefactorFlag(featureFlags, Flags.NOTIFICATION_SHELF_REFACTOR)
+ private val shelfRefactor = RefactorFlag(featureFlags, Flags.NOTIFICATION_SHELF_REFACTOR)
private val tintAreas = ArrayList<Rect>()
private var iconSize = 0
@@ -167,17 +167,16 @@
NotificationShelfViewBinderWrapperControllerImpl.unsupported
override fun setShelfIcons(icons: NotificationIconContainer) {
- if (shelfRefactor.expectEnabled()) {
- NotificationIconContainerViewBinder.bind(
- icons,
- shelfIconsViewModel,
- configurationController,
- dozeParameters,
- featureFlags,
- screenOffAnimationController,
- )
- shelfIcons = icons
- }
+ if (shelfRefactor.isUnexpectedlyInLegacyMode()) return
+ NotificationIconContainerViewBinder.bind(
+ icons,
+ shelfIconsViewModel,
+ configurationController,
+ dozeParameters,
+ featureFlags,
+ screenOffAnimationController,
+ )
+ shelfIcons = icons
}
override fun onDensityOrFontScaleChanged(context: Context) {
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/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 847d948..661768d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
@@ -351,12 +351,21 @@
@Override
public long performRemoveAnimation(long duration, long delay, float translationDirection,
- boolean isHeadsUpAnimation, Runnable onFinishedRunnable,
+ boolean isHeadsUpAnimation, Runnable onStartedRunnable, Runnable onFinishedRunnable,
AnimatorListenerAdapter animationListener) {
enableAppearDrawing(true);
mIsHeadsUpAnimation = isHeadsUpAnimation;
- startAppearAnimation(false /* isAppearing */, translationDirection,
- delay, duration, onFinishedRunnable, animationListener);
+ if (mDrawingAppearAnimation) {
+ startAppearAnimation(false /* isAppearing */, translationDirection,
+ delay, duration, onStartedRunnable, onFinishedRunnable, animationListener);
+ } else {
+ if (onStartedRunnable != null) {
+ onStartedRunnable.run();
+ }
+ if (onFinishedRunnable != null) {
+ onFinishedRunnable.run();
+ }
+ }
return 0;
}
@@ -365,12 +374,14 @@
Runnable onFinishRunnable) {
enableAppearDrawing(true);
mIsHeadsUpAnimation = isHeadsUpAppear;
- startAppearAnimation(true /* isAppearing */, isHeadsUpAppear ? 0.0f : -1.0f, delay,
- duration, null, null);
+ if (mDrawingAppearAnimation) {
+ startAppearAnimation(true /* isAppearing */, isHeadsUpAppear ? 0.0f : -1.0f, delay,
+ duration, null, null, null);
+ }
}
private void startAppearAnimation(boolean isAppearing, float translationDirection, long delay,
- long duration, final Runnable onFinishedRunnable,
+ long duration, final Runnable onStartedRunnable, final Runnable onFinishedRunnable,
AnimatorListenerAdapter animationListener) {
mAnimationTranslationY = translationDirection * getActualHeight();
cancelAppearAnimation();
@@ -434,6 +445,9 @@
@Override
public void onAnimationStart(Animator animation) {
+ if (onStartedRunnable != null) {
+ onStartedRunnable.run();
+ }
mRunWithoutInterruptions = true;
Configuration.Builder builder = Configuration.Builder
.withView(getCujType(isAppearing), ActivatableNotificationView.this);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 061132f..11c65e5 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.
@@ -1904,16 +1901,7 @@
return traceTag;
}
- if (isSummaryWithChildren()) {
- return traceTag + "(summary)";
- }
- Class<? extends Notification.Style> style =
- getEntry().getSbn().getNotification().getNotificationStyle();
- if (style == null) {
- return traceTag + "(nostyle)";
- } else {
- return traceTag + "(" + style.getSimpleName() + ")";
- }
+ return traceTag + "(" + getEntry().getNotificationStyle() + ")";
}
@Override
@@ -2930,6 +2918,7 @@
long delay,
float translationDirection,
boolean isHeadsUpAnimation,
+ Runnable onStartedRunnable,
Runnable onFinishedRunnable,
AnimatorListenerAdapter animationListener) {
if (mMenuRow != null && mMenuRow.isMenuVisible()) {
@@ -2937,10 +2926,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 +2943,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/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
index a27a305..60e75ff 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
@@ -1363,12 +1363,12 @@
result.mController.setPendingIntent(existingPendingIntent);
}
if (result.mController.updatePendingIntentFromActions(actions)) {
- if (!result.mView.isActive()) {
- result.mView.focus();
+ if (!result.mController.isActive()) {
+ result.mController.focus();
}
} else {
- if (result.mView.isActive()) {
- result.mView.close();
+ if (result.mController.isActive()) {
+ result.mController.close();
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java
index e200b90..aabf295 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java
@@ -237,9 +237,13 @@
@Override
public long performRemoveAnimation(long duration, long delay,
float translationDirection, boolean isHeadsUpAnimation,
+ Runnable onStartedRunnable,
Runnable onFinishedRunnable,
AnimatorListenerAdapter animationListener) {
// TODO: Use duration
+ if (onStartedRunnable != null) {
+ onStartedRunnable.run();
+ }
setContentVisible(false, true /* animate */, (cancelled) -> onFinishedRunnable.run());
return 0;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaContainerView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaContainerView.kt
index 5d46f52..bae5baa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaContainerView.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaContainerView.kt
@@ -25,9 +25,7 @@
import com.android.systemui.res.R
import com.android.systemui.statusbar.notification.row.ExpandableView
-/**
- * Root view to insert Lock screen media controls into the notification stack.
- */
+/** Root view to insert Lock screen media controls into the notification stack. */
class MediaContainerView(context: Context, attrs: AttributeSet?) : ExpandableView(context, attrs) {
override var clipHeight = 0
@@ -46,8 +44,8 @@
}
private fun updateResources() {
- cornerRadius = context.resources
- .getDimensionPixelSize(R.dimen.notification_corner_radius).toFloat()
+ cornerRadius =
+ context.resources.getDimensionPixelSize(R.dimen.notification_corner_radius).toFloat()
}
public override fun updateClipping() {
@@ -70,18 +68,23 @@
}
override fun performRemoveAnimation(
- duration: Long,
- delay: Long,
- translationDirection: Float,
- isHeadsUpAnimation: Boolean,
- onFinishedRunnable: Runnable?,
- animationListener: AnimatorListenerAdapter?
+ duration: Long,
+ delay: Long,
+ translationDirection: Float,
+ isHeadsUpAnimation: Boolean,
+ onStartedRunnable: Runnable?,
+ onFinishedRunnable: Runnable?,
+ animationListener: AnimatorListenerAdapter?
): Long {
return 0
}
- override fun performAddAnimation(delay: Long, duration: Long, isHeadsUpAppear: Boolean,
- onEnd: Runnable?) {
+ override fun performAddAnimation(
+ delay: Long,
+ duration: Long,
+ isHeadsUpAppear: Boolean,
+ onEnd: Runnable?
+ ) {
// No animation, it doesn't need it, this would be local
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 10b1bcd..dba93d9 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;
@@ -88,7 +89,7 @@
import com.android.systemui.ExpandHelper;
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;
@@ -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);
@@ -2733,7 +2735,7 @@
* @param listener callback for notification removed
*/
public void setOnNotificationRemovedListener(OnNotificationRemovedListener listener) {
- mShelfRefactor.assertDisabled();
+ mShelfRefactor.assertInLegacyMode();
mOnNotificationRemovedListener = listener;
}
@@ -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
@@ -4967,12 +4982,12 @@
@Nullable
public ExpandableView getShelf() {
- if (!mShelfRefactor.expectEnabled()) return null;
+ if (mShelfRefactor.isUnexpectedlyInLegacyMode()) return null;
return mShelf;
}
public void setShelf(NotificationShelf shelf) {
- if (!mShelfRefactor.expectEnabled()) return;
+ if (mShelfRefactor.isUnexpectedlyInLegacyMode()) return;
int index = -1;
if (mShelf != null) {
index = indexOfChild(mShelf);
@@ -4986,7 +5001,7 @@
}
public void setShelfController(NotificationShelfController notificationShelfController) {
- mShelfRefactor.assertDisabled();
+ mShelfRefactor.assertInLegacyMode();
int index = -1;
if (mShelf != null) {
index = indexOfChild(mShelf);
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 9695cb1..6a70815 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;
@@ -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;
@@ -719,7 +719,7 @@
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;
@@ -1431,7 +1431,7 @@
}
public void setShelfController(NotificationShelfController notificationShelfController) {
- mShelfRefactor.assertDisabled();
+ mShelfRefactor.assertInLegacyMode();
mView.setShelfController(notificationShelfController);
}
@@ -1644,12 +1644,12 @@
}
public void setShelf(NotificationShelf shelf) {
- if (!mShelfRefactor.expectEnabled()) return;
+ if (mShelfRefactor.isUnexpectedlyInLegacyMode()) return;
mView.setShelf(shelf);
}
public int getShelfHeight() {
- if (!mShelfRefactor.expectEnabled()) {
+ if (mShelfRefactor.isUnexpectedlyInLegacyMode()) {
return 0;
}
ExpandableView shelf = mView.getShelf();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
index 69453c6..e94258f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
@@ -16,6 +16,9 @@
package com.android.systemui.statusbar.notification.stack;
+import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR;
+import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK;
+
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
@@ -346,21 +349,19 @@
ArrayList<NotificationStackScrollLayout.AnimationEvent> animationEvents) {
boolean needsCustomAnimation = false;
for (NotificationStackScrollLayout.AnimationEvent event : animationEvents) {
- final ExpandableView changingView = (ExpandableView) event.mChangingView;
+ final ExpandableView changingView = event.mChangingView;
boolean loggable = false;
boolean isHeadsUp = false;
- boolean isGroupChild = false;
String key = null;
if (changingView instanceof ExpandableNotificationRow && mLogger != null) {
loggable = true;
isHeadsUp = ((ExpandableNotificationRow) changingView).isHeadsUp();
- isGroupChild = changingView.isChildInGroup();
key = ((ExpandableNotificationRow) changingView).getEntry().getKey();
}
if (event.animationType ==
NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_ADD) {
- // This item is added, initialize it's properties.
+ // This item is added, initialize its properties.
ExpandableViewState viewState = changingView.getViewState();
if (viewState == null || viewState.gone) {
// The position for this child was never generated, let's continue.
@@ -374,7 +375,11 @@
} else if (event.animationType ==
NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_REMOVE) {
- if (changingView.getVisibility() != View.VISIBLE) {
+ int changingViewVisibility = changingView.getVisibility();
+ if (loggable) {
+ mLogger.processAnimationEventsRemoval(key, changingViewVisibility, isHeadsUp);
+ }
+ if (changingViewVisibility != View.VISIBLE) {
changingView.removeFromTransientContainer();
continue;
}
@@ -410,30 +415,40 @@
translationDirection = Math.max(Math.min(translationDirection, 1.0f),-1.0f);
}
- Runnable postAnimation = changingView::removeFromTransientContainer;
+ Runnable postAnimation;
+ Runnable startAnimation;
if (loggable) {
String finalKey = key;
- if (isHeadsUp) {
- mLogger.logHUNViewDisappearingWithRemoveEvent(key);
- postAnimation = () -> {
- mLogger.disappearAnimationEnded(finalKey);
- changingView.removeFromTransientContainer();
- };
- } else if (isGroupChild) {
- mLogger.groupChildRemovalEventProcessed(key);
- postAnimation = () -> {
- mLogger.groupChildRemovalAnimationEnded(finalKey);
- changingView.removeFromTransientContainer();
- };
- }
+ final boolean finalIsHeadsHp = isHeadsUp;
+ startAnimation = () -> {
+ mLogger.animationStart(finalKey, "ANIMATION_TYPE_REMOVE", finalIsHeadsHp);
+ changingView.setInRemovalAnimation(true);
+ };
+ postAnimation = () -> {
+ mLogger.animationEnd(finalKey, "ANIMATION_TYPE_REMOVE", finalIsHeadsHp);
+ changingView.setInRemovalAnimation(false);
+ changingView.removeFromTransientContainer();
+ };
+ } else {
+ startAnimation = ()-> {
+ changingView.setInRemovalAnimation(true);
+ };
+ postAnimation = () -> {
+ changingView.setInRemovalAnimation(false);
+ changingView.removeFromTransientContainer();
+ };
}
changingView.performRemoveAnimation(ANIMATION_DURATION_APPEAR_DISAPPEAR,
0 /* delay */, translationDirection, false /* isHeadsUpAppear */,
- postAnimation, getGlobalAnimationFinishedListener());
+ startAnimation, postAnimation, getGlobalAnimationFinishedListener());
needsCustomAnimation = true;
} else if (event.animationType ==
NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_REMOVE_SWIPED_OUT) {
- if (mHostLayout.isFullySwipedOut(changingView)) {
+ boolean isFullySwipedOut = mHostLayout.isFullySwipedOut(changingView);
+ if (loggable) {
+ mLogger.processAnimationEventsRemoveSwipeOut(key, isFullySwipedOut, isHeadsUp);
+ }
+ if (isFullySwipedOut) {
changingView.removeFromTransientContainer();
}
} else if (event.animationType == NotificationStackScrollLayout
@@ -442,7 +457,7 @@
row.prepareExpansionChanged();
} else if (event.animationType == NotificationStackScrollLayout
.AnimationEvent.ANIMATION_TYPE_HEADS_UP_APPEAR) {
- // This item is added, initialize it's properties.
+ // This item is added, initialize its properties.
ExpandableViewState viewState = changingView.getViewState();
mTmpState.copyFrom(viewState);
if (event.headsUpFromBottom) {
@@ -464,22 +479,23 @@
}
mTmpState.applyToView(changingView);
- } else if (event.animationType == NotificationStackScrollLayout
- .AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR ||
- event.animationType == NotificationStackScrollLayout
- .AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK) {
+ } else if (event.animationType == ANIMATION_TYPE_HEADS_UP_DISAPPEAR
+ || event.animationType == ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK) {
mHeadsUpDisappearChildren.add(changingView);
Runnable endRunnable = null;
if (changingView.getParent() == null) {
- // This notification was actually removed, so we need to add it transiently
+ // This notification was actually removed, so we need to add it
+ // transiently
mHostLayout.addTransientView(changingView, 0);
changingView.setTransientContainer(mHostLayout);
mTmpState.initFrom(changingView);
endRunnable = changingView::removeFromTransientContainer;
}
+
boolean needsAnimation = true;
if (changingView instanceof ExpandableNotificationRow) {
- ExpandableNotificationRow row = (ExpandableNotificationRow) changingView;
+ ExpandableNotificationRow row =
+ (ExpandableNotificationRow) changingView;
if (row.isDismissed()) {
needsAnimation = false;
}
@@ -488,21 +504,43 @@
// We need to add the global animation listener, since once no animations are
// running anymore, the panel will instantly hide itself. We need to wait until
// the animation is fully finished for this though.
- Runnable postAnimation = endRunnable;
+ final Runnable tmpEndRunnable = endRunnable;
+ Runnable postAnimation;
+ Runnable startAnimation;
if (loggable) {
- mLogger.logHUNViewDisappearing(key);
-
- Runnable finalEndRunnable = endRunnable;
String finalKey1 = key;
+ final boolean finalIsHeadsUp = isHeadsUp;
+ final String type =
+ event.animationType == ANIMATION_TYPE_HEADS_UP_DISAPPEAR
+ ? "ANIMATION_TYPE_HEADS_UP_DISAPPEAR"
+ : "ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK";
+ startAnimation = () -> {
+ mLogger.animationStart(finalKey1, type, finalIsHeadsUp);
+ changingView.setInRemovalAnimation(true);
+ };
postAnimation = () -> {
- mLogger.disappearAnimationEnded(finalKey1);
- if (finalEndRunnable != null) finalEndRunnable.run();
+ mLogger.animationEnd(finalKey1, type, finalIsHeadsUp);
+ changingView.setInRemovalAnimation(false);
+ if (tmpEndRunnable != null) {
+ tmpEndRunnable.run();
+ }
+ };
+ } else {
+ postAnimation = () -> {
+ changingView.setInRemovalAnimation(false);
+ if (tmpEndRunnable != null) {
+ tmpEndRunnable.run();
+ }
+ };
+ startAnimation = () -> {
+ changingView.setInRemovalAnimation(true);
};
}
long removeAnimationDelay = changingView.performRemoveAnimation(
ANIMATION_DURATION_HEADS_UP_DISAPPEAR,
0, 0.0f, true /* isHeadsUpAppear */,
- postAnimation, getGlobalAnimationFinishedListener());
+ startAnimation, postAnimation,
+ getGlobalAnimationFinishedListener());
mAnimationProperties.delay += removeAnimationDelay;
} else if (endRunnable != null) {
endRunnable.run();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateLogger.kt
index 0b2c486..d635f89 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateLogger.kt
@@ -5,74 +5,104 @@
import com.android.systemui.log.dagger.NotificationHeadsUpLog
import com.android.systemui.log.dagger.NotificationRenderLog
import com.android.systemui.statusbar.notification.logKey
+import com.android.systemui.util.visibilityString
import javax.inject.Inject
-class StackStateLogger @Inject constructor(
+class StackStateLogger
+@Inject
+constructor(
@NotificationHeadsUpLog private val buffer: LogBuffer,
@NotificationRenderLog private val notificationRenderBuffer: LogBuffer
) {
- fun logHUNViewDisappearing(key: String) {
- buffer.log(TAG, LogLevel.INFO, {
- str1 = logKey(key)
- }, {
- "Heads up view disappearing $str1 "
- })
- }
fun logHUNViewAppearing(key: String) {
- buffer.log(TAG, LogLevel.INFO, {
- str1 = logKey(key)
- }, {
- "Heads up notification view appearing $str1 "
- })
- }
-
- fun logHUNViewDisappearingWithRemoveEvent(key: String) {
- buffer.log(TAG, LogLevel.ERROR, {
- str1 = logKey(key)
- }, {
- "Heads up view disappearing $str1 for ANIMATION_TYPE_REMOVE"
- })
+ buffer.log(
+ TAG,
+ LogLevel.INFO,
+ { str1 = logKey(key) },
+ { "Heads up notification view appearing $str1 " }
+ )
}
fun logHUNViewAppearingWithAddEvent(key: String) {
- buffer.log(TAG, LogLevel.ERROR, {
- str1 = logKey(key)
- }, {
- "Heads up view disappearing $str1 for ANIMATION_TYPE_ADD"
- })
- }
-
- fun disappearAnimationEnded(key: String) {
- buffer.log(TAG, LogLevel.INFO, {
- str1 = logKey(key)
- }, {
- "Heads up notification disappear animation ended $str1 "
- })
+ buffer.log(
+ TAG,
+ LogLevel.ERROR,
+ { str1 = logKey(key) },
+ { "Heads up view disappearing $str1 for ANIMATION_TYPE_ADD" }
+ )
}
fun appearAnimationEnded(key: String) {
- buffer.log(TAG, LogLevel.INFO, {
- str1 = logKey(key)
- }, {
- "Heads up notification appear animation ended $str1 "
- })
+ buffer.log(
+ TAG,
+ LogLevel.INFO,
+ { str1 = logKey(key) },
+ { "Heads up notification appear animation ended $str1 " }
+ )
}
- fun groupChildRemovalEventProcessed(key: String) {
- notificationRenderBuffer.log(TAG, LogLevel.DEBUG, {
- str1 = logKey(key)
- }, {
- "Group Child Notification removal event processed $str1 for ANIMATION_TYPE_REMOVE"
- })
+ fun processAnimationEventsRemoval(key: String, visibility: Int, isHeadsUp: Boolean) {
+ notificationRenderBuffer.log(
+ TAG,
+ LogLevel.INFO,
+ {
+ str1 = logKey(key)
+ int1 = visibility
+ bool1 = isHeadsUp
+ },
+ {
+ "ProcessAnimationEvents ANIMATION_TYPE_REMOVE for: $str1, " +
+ "changingViewVisibility: ${visibilityString(int1)}, isHeadsUp: $bool1"
+ }
+ )
}
- fun groupChildRemovalAnimationEnded(key: String) {
- notificationRenderBuffer.log(TAG, LogLevel.INFO, {
- str1 = logKey(key)
- }, {
- "Group child notification removal animation ended $str1 "
- })
+
+ fun processAnimationEventsRemoveSwipeOut(
+ key: String,
+ isFullySwipedOut: Boolean,
+ isHeadsUp: Boolean
+ ) {
+ notificationRenderBuffer.log(
+ TAG,
+ LogLevel.INFO,
+ {
+ str1 = logKey(key)
+ bool1 = isFullySwipedOut
+ bool2 = isHeadsUp
+ },
+ {
+ "ProcessAnimationEvents ANIMATION_TYPE_REMOVE_SWIPED_OUT for: $str1, " +
+ "isFullySwipedOut: $bool1, isHeadsUp: $bool2"
+ }
+ )
+ }
+
+ fun animationStart(key: String?, animationType: String, isHeadsUp: Boolean) {
+ notificationRenderBuffer.log(
+ TAG,
+ LogLevel.INFO,
+ {
+ str1 = logKey(key)
+ str2 = animationType
+ bool1 = isHeadsUp
+ },
+ { "Animation Start, type: $str2, notif key: $str1, isHeadsUp: $bool1" }
+ )
+ }
+
+ fun animationEnd(key: String, animationType: String, isHeadsUp: Boolean) {
+ notificationRenderBuffer.log(
+ TAG,
+ LogLevel.INFO,
+ {
+ str1 = logKey(key)
+ str2 = animationType
+ bool1 = isHeadsUp
+ },
+ { "Animation End, type: $str2, notif key: $str1, isHeadsUp: $bool1" }
+ )
}
}
-private const val TAG = "StackScroll"
\ No newline at end of file
+private const val TAG = "StackScroll"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
index 697d297..3877bb6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
@@ -28,15 +28,15 @@
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.res.R;
import com.android.systemui.dagger.NightDisplayListenerModule;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.qs.AutoAddTracker;
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.ReduceBrightColorsController;
-import com.android.systemui.qs.SettingObserver;
+import com.android.systemui.qs.UserSettingObserver;
import com.android.systemui.qs.external.CustomTile;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.policy.CastController;
import com.android.systemui.statusbar.policy.CastController.CastDevice;
import com.android.systemui.statusbar.policy.DataSaverController;
@@ -461,8 +461,8 @@
};
@VisibleForTesting
- protected SettingObserver getSecureSettingForKey(String key) {
- for (SettingObserver s : mAutoAddSettingList) {
+ protected UserSettingObserver getSecureSettingForKey(String key) {
+ for (UserSettingObserver s : mAutoAddSettingList) {
if (Objects.equals(key, s.getKey())) {
return s;
}
@@ -476,7 +476,7 @@
* When the setting changes to a value different from 0, if the tile has not been auto added
* before, it will be added and the listener will be stopped.
*/
- private class AutoAddSetting extends SettingObserver {
+ private class AutoAddSetting extends UserSettingObserver {
private final String mSpec;
AutoAddSetting(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
index 2809cad..fb45a67 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
@@ -498,8 +498,7 @@
case MODE_WAKE_AND_UNLOCK:
if (mMode == MODE_WAKE_AND_UNLOCK_PULSING) {
Trace.beginSection("MODE_WAKE_AND_UNLOCK_PULSING");
- mMediaManager.updateMediaMetaData(false /* metaDataChanged */,
- true /* allowEnterAnimation */);
+ mMediaManager.updateMediaMetaData(false /* metaDataChanged */);
} else if (mMode == MODE_WAKE_AND_UNLOCK){
Trace.beginSection("MODE_WAKE_AND_UNLOCK");
} else {
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 6e6318e..9fb6c1b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -239,6 +239,8 @@
import dagger.Lazy;
+import dalvik.annotation.optimization.NeverCompile;
+
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.List;
@@ -310,8 +312,8 @@
};
void onStatusBarWindowStateChanged(@WindowVisibleState int state) {
- updateBubblesVisibility();
mStatusBarWindowState = state;
+ updateBubblesVisibility();
}
@Override
@@ -1086,6 +1088,7 @@
* @deprecated use {@link
* WindowRootViewVisibilityInteractor.isLockscreenOrShadeVisible} instead.
*/ @VisibleForTesting
+ @Deprecated
void initShadeVisibilityListener() {
mShadeController.setVisibilityListener(new ShadeController.ShadeVisibilityListener() {
@Override
@@ -1723,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(
@@ -1763,6 +1767,7 @@
}
}
+ @NeverCompile
@Override
public void dump(PrintWriter pwOriginal, String[] args) {
IndentingPrintWriter pw = DumpUtilsKt.asIndenting(pwOriginal);
@@ -2599,7 +2604,10 @@
mShouldDelayWakeUpAnimation);
updateIsKeyguard();
+ // TODO(b/301913237): can't delay transition if config_displayBlanksAfterDoze=true,
+ // otherwise, the clock will flicker during LOCKSCREEN_TRANSITION_FROM_AOD
mShouldDelayLockscreenTransitionFromAod = mDozeParameters.getAlwaysOn()
+ && !mDozeParameters.getDisplayNeedsBlanking()
&& mFeatureFlags.isEnabled(
Flags.ZJ_285570694_LOCKSCREEN_TRANSITION_FROM_AOD);
if (!mShouldDelayLockscreenTransitionFromAod) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/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/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..f9856b0 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;
@@ -205,14 +205,13 @@
}
public void setupShelf(NotificationShelfController notificationShelfController) {
- mShelfRefactor.assertDisabled();
+ mShelfRefactor.assertInLegacyMode();
mShelfIcons = notificationShelfController.getShelfIcons();
}
public void setShelfIcons(NotificationIconContainer icons) {
- if (mShelfRefactor.expectEnabled()) {
- mShelfIcons = icons;
- }
+ if (mShelfRefactor.isUnexpectedlyInLegacyMode()) return;
+ mShelfIcons = icons;
}
public void onDensityOrFontScaleChanged(@NotNull Context context) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java
index 92c786f..00fd9fb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java
@@ -263,8 +263,7 @@
if (result.success) {
mCached = true;
mCache = result.bitmap;
- mMediaManager.updateMediaMetaData(
- true /* metaDataChanged */, true /* allowEnterAnimation */);
+ mMediaManager.updateMediaMetaData(true /* metaDataChanged */);
}
mLoader = null;
}
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 62b2445..400ac7b 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;
@@ -963,7 +961,7 @@
SysUiStatsLog.KEYGUARD_STATE_CHANGED__STATE__SHOWN);
}
if (isShowing) {
- mMediaManager.updateMediaMetaData(false, animate && !isOccluded);
+ mMediaManager.updateMediaMetaData(false);
}
mNotificationShadeWindowController.setKeyguardOccluded(isOccluded);
@@ -1594,7 +1592,7 @@
final boolean actionDownThenUp = mAlternateBouncerInteractor.getReceivedDownTouch()
&& event.getActionMasked() == MotionEvent.ACTION_UP;
final boolean udfpsOverlayWillForwardEventsOutsideNotificationShade =
- mUdfpsNewTouchDetectionEnabled && mKeyguardUpdateManager.isUdfpsEnrolled();
+ mKeyguardUpdateManager.isUdfpsEnrolled();
final boolean actionOutsideShouldDismissAlternateBouncer =
event.getActionMasked() == MotionEvent.ACTION_OUTSIDE
&& !udfpsOverlayWillForwardEventsOutsideNotificationShade;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
index 2d14f6b..57a8e6f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
@@ -221,7 +221,7 @@
@Override
public void updateMediaMetaData(boolean metaDataChanged, boolean allowEnterAnimation) {
- mMediaManager.updateMediaMetaData(metaDataChanged, allowEnterAnimation);
+ mMediaManager.updateMediaMetaData(metaDataChanged);
}
@Override
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/UnlockedScreenOffAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
index 2c15e27..de37170 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
@@ -34,6 +34,8 @@
import com.android.systemui.util.TraceUtils
import com.android.systemui.util.settings.GlobalSettings
import javax.inject.Inject
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
/**
* When to show the keyguard (AOD) view. This should be once the light reveal scrim is barely
@@ -65,7 +67,8 @@
private val notifShadeWindowControllerLazy: dagger.Lazy<NotificationShadeWindowController>,
private val interactionJankMonitor: InteractionJankMonitor,
private val powerManager: PowerManager,
- private val handler: Handler = Handler()
+ private val handler: Handler = Handler(),
+ private val featureFlags: FeatureFlags,
) : WakefulnessLifecycle.Observer, ScreenOffAnimation {
private lateinit var centralSurfaces: CentralSurfaces
private lateinit var shadeViewController: ShadeViewController
@@ -285,7 +288,11 @@
// up, with unpredictable consequences.
if (!powerManager.isInteractive(Display.DEFAULT_DISPLAY) &&
shouldAnimateInKeyguard) {
- aodUiAnimationPlaying = true
+ if (!featureFlags.isEnabled(Flags.MIGRATE_KEYGUARD_STATUS_VIEW)) {
+ // Tracking this state should no longer be relevant, as the isInteractive
+ // check covers it
+ aodUiAnimationPlaying = true
+ }
// Show AOD. That'll cause the KeyguardVisibilityHelper to call
// #animateInKeyguard.
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/BluetoothControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
index 945cc6b..53b343c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
@@ -27,6 +27,7 @@
import android.os.UserManager;
import androidx.annotation.NonNull;
+import androidx.annotation.WorkerThread;
import com.android.internal.annotations.GuardedBy;
import com.android.settingslib.bluetooth.BluetoothCallback;
@@ -36,6 +37,7 @@
import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
import com.android.systemui.bluetooth.BluetoothLogger;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.settings.UserTracker;
@@ -81,6 +83,8 @@
private int mState;
private final BluetoothAdapter mAdapter;
+
+ private final Executor mBackgroundExecutor;
/**
*/
@Inject
@@ -90,6 +94,7 @@
DumpManager dumpManager,
BluetoothLogger logger,
BluetoothRepository bluetoothRepository,
+ @Background Executor executor,
@Main Looper mainLooper,
@Nullable LocalBluetoothManager localBluetoothManager,
@Nullable BluetoothAdapter bluetoothAdapter) {
@@ -98,6 +103,7 @@
mBluetoothRepository = bluetoothRepository;
mLocalBluetoothManager = localBluetoothManager;
mHandler = new H(mainLooper);
+ mBackgroundExecutor = executor;
if (mLocalBluetoothManager != null) {
mLocalBluetoothManager.getEventManager().registerCallback(this);
mLocalBluetoothManager.getProfileManager().addServiceListener(this);
@@ -218,6 +224,7 @@
return mIsActive;
}
+ @WorkerThread
@Override
public void setBluetoothEnabled(boolean enabled) {
if (mLocalBluetoothManager != null) {
@@ -230,6 +237,7 @@
return mLocalBluetoothManager != null;
}
+ @WorkerThread
@Override
public String getConnectedDeviceName() {
synchronized (mConnectedDevices) {
@@ -251,6 +259,7 @@
getDevices(), this::onConnectionStatusFetched);
}
+ // Careful! This may be invoked in the main thread.
private void onConnectionStatusFetched(ConnectionStatusModel status) {
List<CachedBluetoothDevice> newList = status.getConnectedDevices();
int state = status.getMaxConnectionState();
@@ -282,30 +291,33 @@
}
private void updateAudioProfile() {
- boolean audioProfileConnected = false;
- boolean otherProfileConnected = false;
+ // We want this in the background as calls inside `LocalBluetoothProfile` end up being
+ // binder calls
+ mBackgroundExecutor.execute(() -> {
+ boolean audioProfileConnected = false;
+ boolean otherProfileConnected = false;
- for (CachedBluetoothDevice device : getDevices()) {
- for (LocalBluetoothProfile profile : device.getProfiles()) {
- int profileId = profile.getProfileId();
- boolean isConnected = device.isConnectedProfile(profile);
- if (profileId == BluetoothProfile.HEADSET
- || profileId == BluetoothProfile.A2DP
- || profileId == BluetoothProfile.HEARING_AID
- || profileId == BluetoothProfile.LE_AUDIO) {
- audioProfileConnected |= isConnected;
- } else {
- otherProfileConnected |= isConnected;
+ for (CachedBluetoothDevice device : getDevices()) {
+ for (LocalBluetoothProfile profile : device.getProfiles()) {
+ int profileId = profile.getProfileId();
+ boolean isConnected = device.isConnectedProfile(profile);
+ if (profileId == BluetoothProfile.HEADSET
+ || profileId == BluetoothProfile.A2DP
+ || profileId == BluetoothProfile.HEARING_AID
+ || profileId == BluetoothProfile.LE_AUDIO) {
+ audioProfileConnected |= isConnected;
+ } else {
+ otherProfileConnected |= isConnected;
+ }
}
}
- }
- boolean audioProfileOnly = (audioProfileConnected && !otherProfileConnected);
- if (audioProfileOnly != mAudioProfileOnly) {
- mAudioProfileOnly = audioProfileOnly;
- mHandler.sendEmptyMessage(H.MSG_STATE_CHANGED);
- }
-
+ boolean audioProfileOnly = (audioProfileConnected && !otherProfileConnected);
+ if (audioProfileOnly != mAudioProfileOnly) {
+ mAudioProfileOnly = audioProfileOnly;
+ mHandler.sendEmptyMessage(H.MSG_STATE_CHANGED);
+ }
+ });
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.kt
index 8c61ada..8b20283 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.kt
@@ -62,7 +62,7 @@
}
private val deviceProvisionedUri = globalSettings.getUriFor(Settings.Global.DEVICE_PROVISIONED)
- private val frpActiveUri = secureSettings.getUriFor(Settings.Secure.SECURE_FRP_MODE)
+ private val frpActiveUri = globalSettings.getUriFor(Settings.Global.SECURE_FRP_MODE)
private val userSetupUri = secureSettings.getUriFor(Settings.Secure.USER_SETUP_COMPLETE)
private val deviceProvisioned = AtomicBoolean(false)
@@ -148,7 +148,7 @@
.set(globalSettings.getInt(Settings.Global.DEVICE_PROVISIONED, 0) != 0)
}
if (updateFrp) {
- frpActive.set(globalSettings.getInt(Settings.Secure.SECURE_FRP_MODE, 0) != 0)
+ frpActive.set(globalSettings.getInt(Settings.Global.SECURE_FRP_MODE, 0) != 0)
}
synchronized(lock) {
if (updateUser == ALL_USERS) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java
index 62e2381..b614b6d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java
@@ -38,6 +38,7 @@
import com.android.systemui.res.R;
import com.android.systemui.animation.Expandable;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.user.UserSwitchDialogController;
@@ -148,6 +149,7 @@
DozeParameters dozeParameters,
ScreenOffAnimationController screenOffAnimationController,
UserSwitchDialogController userSwitchDialogController,
+ FeatureFlags featureFlags,
UiEventLogger uiEventLogger) {
super(view);
if (DEBUG) Log.d(TAG, "New KeyguardQsUserSwitchController");
@@ -160,7 +162,8 @@
mStatusBarStateController = statusBarStateController;
mKeyguardVisibilityHelper = new KeyguardVisibilityHelper(mView,
keyguardStateController, dozeParameters,
- screenOffAnimationController, /* animateYPos= */ false, /* logBuffer= */ null);
+ screenOffAnimationController, /* animateYPos= */ false,
+ featureFlags, /* logBuffer= */ null);
mUserSwitchDialogController = userSwitchDialogController;
mUiEventLogger = uiEventLogger;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java
index bb074ac..dfe2686 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java
@@ -38,10 +38,11 @@
import com.android.keyguard.KeyguardVisibilityHelper;
import com.android.keyguard.dagger.KeyguardUserSwitcherScope;
import com.android.settingslib.drawable.CircleFramedDrawable;
-import com.android.systemui.res.R;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.notification.AnimatableProperty;
import com.android.systemui.statusbar.notification.PropertyAnimator;
@@ -160,6 +161,7 @@
KeyguardStateController keyguardStateController,
SysuiStatusBarStateController statusBarStateController,
KeyguardUpdateMonitor keyguardUpdateMonitor,
+ FeatureFlags featureFlags,
DozeParameters dozeParameters,
ScreenOffAnimationController screenOffAnimationController) {
super(keyguardUserSwitcherView);
@@ -174,7 +176,8 @@
mUserSwitcherController, this);
mKeyguardVisibilityHelper = new KeyguardVisibilityHelper(mView,
keyguardStateController, dozeParameters,
- screenOffAnimationController, /* animateYPos= */ false, /* logBuffer= */ null);
+ screenOffAnimationController, /* animateYPos= */ false,
+ featureFlags, /* logBuffer= */ null);
mBackground = new KeyguardUserSwitcherScrim(context);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
index 53fed3d..ceed81a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
@@ -305,7 +305,8 @@
&& editTextRootWindowInsets.isVisible(WindowInsets.Type.ime());
if (!mEntry.mRemoteEditImeVisible && !mEditText.mShowImeOnInputConnection) {
// Pass null to ensure all inputs are cleared for this entry b/227115380
- mController.removeRemoteInput(mEntry, null);
+ mController.removeRemoteInput(mEntry, null,
+ /* reason= */"RemoteInputView$WindowInsetAnimation#onEnd");
}
}
}
@@ -426,7 +427,7 @@
@VisibleForTesting
void onDefocus(boolean animate, boolean logClose, @Nullable Runnable doAfterDefocus) {
- mController.removeRemoteInput(mEntry, mToken);
+ mController.removeRemoteInput(mEntry, mToken, /* reason= */"RemoteInputView#onDefocus");
mEntry.remoteInputText = mEditText.getText();
// During removal, we get reattached and lose focus. Not hiding in that
@@ -536,7 +537,8 @@
if (mEntry.getRow().isChangingPosition() || isTemporarilyDetached()) {
return;
}
- mController.removeRemoteInput(mEntry, mToken);
+ mController.removeRemoteInput(mEntry, mToken,
+ /* reason= */"RemoteInputView#onDetachedFromWindow");
mController.removeSpinning(mEntry.getKey(), mToken);
}
@@ -655,7 +657,7 @@
mEditText.setText(mEntry.remoteInputText);
mEditText.setSelection(mEditText.length());
mEditText.requestFocus();
- mController.addRemoteInput(mEntry, mToken);
+ mController.addRemoteInput(mEntry, mToken, "RemoteInputView#focus");
setAttachment(mEntry.remoteInputAttachment);
updateSendButton();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputViewController.kt
index a50fd6f..6c0d433 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputViewController.kt
@@ -255,7 +255,8 @@
entry.lastRemoteInputSent = SystemClock.elapsedRealtime()
entry.mRemoteEditImeAnimatingAway = true
remoteInputController.addSpinning(entry.key, view.mToken)
- remoteInputController.removeRemoteInput(entry, view.mToken)
+ remoteInputController.removeRemoteInput(entry, view.mToken,
+ /* reason= */ "RemoteInputViewController#sendRemoteInput")
remoteInputController.remoteInputSent(entry)
entry.setHasSentReply()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/bluetooth/BluetoothRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/bluetooth/BluetoothRepository.kt
index 80f3d76..96717c7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/bluetooth/BluetoothRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/bluetooth/BluetoothRepository.kt
@@ -38,7 +38,8 @@
/**
* Fetches the connection statuses for the given [currentDevices] and invokes [callback] once
* those statuses have been fetched. The fetching occurs on a background thread because IPCs may
- * be required to fetch the statuses (see b/271058380).
+ * be required to fetch the statuses (see b/271058380). However, the callback will be invoked in
+ * the main thread.
*/
fun fetchConnectionStatusInBackground(
currentDevices: Collection<CachedBluetoothDevice>,
diff --git a/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt b/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt
index f404549..5fc435a 100644
--- a/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt
@@ -23,7 +23,6 @@
import android.os.UserManager
import android.provider.Settings
import androidx.annotation.VisibleForTesting
-import com.android.systemui.res.R
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.dagger.SysUISingleton
@@ -31,6 +30,7 @@
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.res.R
import com.android.systemui.settings.UserTracker
import com.android.systemui.user.data.model.SelectedUserModel
import com.android.systemui.user.data.model.SelectionStatus
@@ -132,7 +132,6 @@
Settings.Global.ADD_USERS_WHEN_LOCKED,
Settings.Global.USER_SWITCHER_ENABLED,
),
- userId = UserHandle.USER_SYSTEM,
)
.onStart { emit(Unit) } // Forces an initial update.
.map { getSettings() }
@@ -247,7 +246,7 @@
private suspend fun getSettings(): UserSwitcherSettingsModel {
return withContext(backgroundDispatcher) {
val isSimpleUserSwitcher =
- globalSettings.getIntForUser(
+ globalSettings.getInt(
SETTING_SIMPLE_USER_SWITCHER,
if (
appContext.resources.getBoolean(
@@ -258,18 +257,16 @@
} else {
0
},
- UserHandle.USER_SYSTEM,
) != 0
val isAddUsersFromLockscreen =
- globalSettings.getIntForUser(
+ globalSettings.getInt(
Settings.Global.ADD_USERS_WHEN_LOCKED,
0,
- UserHandle.USER_SYSTEM,
) != 0
val isUserSwitcherEnabled =
- globalSettings.getIntForUser(
+ globalSettings.getInt(
Settings.Global.USER_SWITCHER_ENABLED,
if (
appContext.resources.getBoolean(
@@ -280,7 +277,6 @@
} else {
0
},
- UserHandle.USER_SYSTEM,
) != 0
UserSwitcherSettingsModel(
diff --git a/packages/SystemUI/src/com/android/systemui/util/Utils.java b/packages/SystemUI/src/com/android/systemui/util/Utils.java
index 760fe6a..f5edb7b 100644
--- a/packages/SystemUI/src/com/android/systemui/util/Utils.java
+++ b/packages/SystemUI/src/com/android/systemui/util/Utils.java
@@ -42,6 +42,7 @@
* list, then list.get(i) could throw an IndexOutOfBoundsException. This method should not be
* used; try using `synchronized` or making a copy of the list instead.
*/
+ @Deprecated
public static <T> void safeForeach(List<T> list, Consumer<T> c) {
for (int i = list.size() - 1; i >= 0; i--) {
T item = list.get(i);
diff --git a/packages/SystemUI/src/com/android/systemui/util/annotations/WeaklyReferencedCallback.java b/packages/SystemUI/src/com/android/systemui/util/annotations/WeaklyReferencedCallback.java
new file mode 100644
index 0000000..855bba6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/annotations/WeaklyReferencedCallback.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util.annotations;
+
+import static java.lang.annotation.ElementType.TYPE;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+
+/**
+ * Descriptive annotation for clearly tagging callback types that are weakly
+ * referenced during registration.
+ *
+ * This is useful in providing hints to Proguard about certain fields that
+ * should be kept to preserve strong references.
+ */
+@Retention(RetentionPolicy.CLASS)
+@Target({TYPE})
+public @interface WeaklyReferencedCallback {}
diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/Utils.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/Utils.kt
index 73e2f97..ffbc10a 100644
--- a/packages/SystemUI/src/com/android/systemui/util/kotlin/Utils.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/Utils.kt
@@ -19,6 +19,7 @@
class Utils {
companion object {
fun <A, B, C> toTriple(a: A, bc: Pair<B, C>) = Triple(a, bc.first, bc.second)
+ fun <A, B, C> toTriple(ab: Pair<A, B>, c: C) = Triple(ab.first, ab.second, c)
fun <A, B, C, D> toQuad(a: A, b: B, c: C, d: D) = Quad(a, b, c, d)
fun <A, B, C, D> toQuad(a: A, bcd: Triple<B, C, D>) =
diff --git a/packages/SystemUI/src/com/android/systemui/util/service/ObservableServiceConnection.java b/packages/SystemUI/src/com/android/systemui/util/service/ObservableServiceConnection.java
index 968dcc9..df5162a 100644
--- a/packages/SystemUI/src/com/android/systemui/util/service/ObservableServiceConnection.java
+++ b/packages/SystemUI/src/com/android/systemui/util/service/ObservableServiceConnection.java
@@ -26,6 +26,7 @@
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.settings.UserTracker;
+import com.android.systemui.util.annotations.WeaklyReferencedCallback;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -64,6 +65,7 @@
* An interface for listening to the connection status.
* @param <T> The wrapper type.
*/
+ @WeaklyReferencedCallback
public interface Callback<T> {
/**
* Invoked when the service has been successfully connected to.
diff --git a/packages/SystemUI/src/com/android/systemui/util/service/Observer.java b/packages/SystemUI/src/com/android/systemui/util/service/Observer.java
index 7687432..425336d 100644
--- a/packages/SystemUI/src/com/android/systemui/util/service/Observer.java
+++ b/packages/SystemUI/src/com/android/systemui/util/service/Observer.java
@@ -16,6 +16,8 @@
package com.android.systemui.util.service;
+import com.android.systemui.util.annotations.WeaklyReferencedCallback;
+
/**
* The {@link Observer} interface specifies an entity which listeners
* can be informed of changes to the source, which will require updating. Note that this deals
@@ -25,6 +27,7 @@
/**
* Callback for receiving updates from the {@link Observer}.
*/
+ @WeaklyReferencedCallback
interface Callback {
/**
* Invoked when the source has changed.
diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/GlobalSettingsImpl.java b/packages/SystemUI/src/com/android/systemui/util/settings/GlobalSettingsImpl.java
index 85fada2..42389f0 100644
--- a/packages/SystemUI/src/com/android/systemui/util/settings/GlobalSettingsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/util/settings/GlobalSettingsImpl.java
@@ -16,22 +16,23 @@
package com.android.systemui.util.settings;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.content.ContentResolver;
import android.net.Uri;
import android.provider.Settings;
-import com.android.systemui.settings.UserTracker;
-
import javax.inject.Inject;
+// use UserHandle.USER_SYSTEM everywhere
+@SuppressLint("StaticSettingsProvider")
class GlobalSettingsImpl implements GlobalSettings {
private final ContentResolver mContentResolver;
- private final UserTracker mUserTracker;
@Inject
- GlobalSettingsImpl(ContentResolver contentResolver, UserTracker userTracker) {
+ GlobalSettingsImpl(ContentResolver contentResolver) {
mContentResolver = contentResolver;
- mUserTracker = userTracker;
}
@Override
@@ -40,43 +41,23 @@
}
@Override
- public UserTracker getUserTracker() {
- return mUserTracker;
- }
-
- @Override
public Uri getUriFor(String name) {
return Settings.Global.getUriFor(name);
}
@Override
- public String getStringForUser(String name, int userHandle) {
- return Settings.Global.getStringForUser(mContentResolver, name,
- getRealUserHandle(userHandle));
+ public String getString(String name) {
+ return Settings.Global.getString(mContentResolver, name);
}
@Override
- public boolean putString(String name, String value, boolean overrideableByRestore) {
- throw new UnsupportedOperationException(
- "This method only exists publicly for Settings.System and Settings.Secure");
+ public boolean putString(String name, String value) {
+ return Settings.Global.putString(mContentResolver, name, value);
}
@Override
- public boolean putStringForUser(String name, String value, int userHandle) {
- return Settings.Global.putStringForUser(mContentResolver, name, value,
- getRealUserHandle(userHandle));
- }
-
- @Override
- public boolean putStringForUser(String name, String value, String tag, boolean makeDefault,
- int userHandle, boolean overrideableByRestore) {
- return Settings.Global.putStringForUser(
- mContentResolver, name, value, tag, makeDefault, getRealUserHandle(userHandle),
- overrideableByRestore);
- }
-
- @Override
- public boolean putString(String name, String value, String tag, boolean makeDefault) {
+ public boolean putString(@NonNull String name, @Nullable String value, @Nullable String tag,
+ boolean makeDefault) {
return Settings.Global.putString(mContentResolver, name, value, tag, makeDefault);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/SecureSettings.java b/packages/SystemUI/src/com/android/systemui/util/settings/SecureSettings.java
index 798033e..6031a4e 100644
--- a/packages/SystemUI/src/com/android/systemui/util/settings/SecureSettings.java
+++ b/packages/SystemUI/src/com/android/systemui/util/settings/SecureSettings.java
@@ -22,5 +22,5 @@
* See {@link SettingsProxy} for details.
*/
-public interface SecureSettings extends SettingsProxy {
+public interface SecureSettings extends UserSettingsProxy {
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/SecureSettingsImpl.java b/packages/SystemUI/src/com/android/systemui/util/settings/SecureSettingsImpl.java
index f995436..6532ce8 100644
--- a/packages/SystemUI/src/com/android/systemui/util/settings/SecureSettingsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/util/settings/SecureSettingsImpl.java
@@ -20,6 +20,8 @@
import android.net.Uri;
import android.provider.Settings;
+import androidx.annotation.NonNull;
+
import com.android.systemui.settings.UserTracker;
import javax.inject.Inject;
@@ -75,7 +77,7 @@
}
@Override
- public boolean putString(String name, String value, String tag, boolean makeDefault) {
+ public boolean putString(@NonNull String name, String value, String tag, boolean makeDefault) {
return Settings.Secure.putString(mContentResolver, name, value, tag, makeDefault);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.java b/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.java
index b6846a3..6a9edc1 100644
--- a/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.java
@@ -18,25 +18,22 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.UserIdInt;
import android.content.ContentResolver;
import android.database.ContentObserver;
import android.net.Uri;
-import android.os.UserHandle;
import android.provider.Settings;
-import com.android.systemui.settings.UserTracker;
-
/**
- * Used to interact with Settings.Secure, Settings.Global, and Settings.System.
- *
+ * Used to interact with mainly with Settings.Global, but can also be used for Settings.System
+ * and Settings.Secure. To use the per-user System and Secure settings, {@link UserSettingsProxy}
+ * must be used instead.
+ * <p>
* This interface can be implemented to give instance method (instead of static method) versions
- * of Settings.Secure, Settings.Global, and Settings.System. It can be injected into class
- * constructors and then faked or mocked as needed in tests.
- *
- * You can ask for {@link SecureSettings}, {@link GlobalSettings}, or {@link SystemSettings} to be
- * injected as needed.
- *
+ * of Settings.Global. It can be injected into class constructors and then faked or mocked as needed
+ * in tests.
+ * <p>
+ * You can ask for {@link GlobalSettings} to be injected as needed.
+ * <p>
* This class also provides {@link #registerContentObserver(String, ContentObserver)} methods,
* normally found on {@link ContentResolver} instances, unifying setting related actions in one
* place.
@@ -49,29 +46,6 @@
ContentResolver getContentResolver();
/**
- * Returns that {@link UserTracker} this instance was constructed with.
- */
- UserTracker getUserTracker();
-
- /**
- * Returns the user id for the associated {@link ContentResolver}.
- */
- default int getUserId() {
- return getContentResolver().getUserId();
- }
-
- /**
- * Returns the actual current user handle when querying with the current user. Otherwise,
- * returns the passed in user id.
- */
- default int getRealUserHandle(int userHandle) {
- if (userHandle != UserHandle.USER_CURRENT) {
- return userHandle;
- }
- return getUserTracker().getUserId();
- }
-
- /**
* Construct the content URI for a particular name/value pair,
* useful for monitoring changes with a ContentObserver.
* @param name to look up in the table
@@ -82,7 +56,7 @@
/**
* Convenience wrapper around
* {@link ContentResolver#registerContentObserver(Uri, boolean, ContentObserver)}.'
- *
+ * <p>
* Implicitly calls {@link #getUriFor(String)} on the passed in name.
*/
default void registerContentObserver(String name, ContentObserver settingsObserver) {
@@ -94,13 +68,13 @@
* {@link ContentResolver#registerContentObserver(Uri, boolean, ContentObserver)}.'
*/
default void registerContentObserver(Uri uri, ContentObserver settingsObserver) {
- registerContentObserverForUser(uri, settingsObserver, getUserId());
+ registerContentObserver(uri, false, settingsObserver);
}
/**
* Convenience wrapper around
* {@link ContentResolver#registerContentObserver(Uri, boolean, ContentObserver)}.'
- *
+ * <p>
* Implicitly calls {@link #getUriFor(String)} on the passed in name.
*/
default void registerContentObserver(String name, boolean notifyForDescendants,
@@ -114,53 +88,8 @@
*/
default void registerContentObserver(Uri uri, boolean notifyForDescendants,
ContentObserver settingsObserver) {
- registerContentObserverForUser(uri, notifyForDescendants, settingsObserver, getUserId());
- }
-
- /**
- * Convenience wrapper around
- * {@link ContentResolver#registerContentObserver(Uri, boolean, ContentObserver, int)}
- *
- * Implicitly calls {@link #getUriFor(String)} on the passed in name.
- */
- default void registerContentObserverForUser(
- String name, ContentObserver settingsObserver, int userHandle) {
- registerContentObserverForUser(
- getUriFor(name), settingsObserver, userHandle);
- }
-
- /**
- * Convenience wrapper around
- * {@link ContentResolver#registerContentObserver(Uri, boolean, ContentObserver, int)}
- */
- default void registerContentObserverForUser(
- Uri uri, ContentObserver settingsObserver, int userHandle) {
- registerContentObserverForUser(
- uri, false, settingsObserver, userHandle);
- }
-
- /**
- * Convenience wrapper around
- * {@link ContentResolver#registerContentObserver(Uri, boolean, ContentObserver, int)}
- *
- * Implicitly calls {@link #getUriFor(String)} on the passed in name.
- */
- default void registerContentObserverForUser(
- String name, boolean notifyForDescendants, ContentObserver settingsObserver,
- int userHandle) {
- registerContentObserverForUser(
- getUriFor(name), notifyForDescendants, settingsObserver, userHandle);
- }
-
- /**
- * Convenience wrapper around
- * {@link ContentResolver#registerContentObserver(Uri, boolean, ContentObserver, int)}
- */
- default void registerContentObserverForUser(
- Uri uri, boolean notifyForDescendants, ContentObserver settingsObserver,
- int userHandle) {
getContentResolver().registerContentObserver(
- uri, notifyForDescendants, settingsObserver, getRealUserHandle(userHandle));
+ uri, notifyForDescendants, settingsObserver);
}
/** See {@link ContentResolver#unregisterContentObserver(ContentObserver)}. */
@@ -173,22 +102,7 @@
* @param name to look up in the table
* @return the corresponding value, or null if not present
*/
- default String getString(String name) {
- return getStringForUser(name, getUserId());
- }
-
- /**See {@link #getString(String)}. */
- String getStringForUser(String name, int userHandle);
-
- /**
- * Store a name/value pair into the database. Values written by this method will be
- * overridden if a restore happens in the future.
- *
- * @param name to store
- * @param value to associate with the name
- * @return true if the value was set, false on database errors
- */
- boolean putString(String name, String value, boolean overrideableByRestore);
+ String getString(String name);
/**
* Store a name/value pair into the database.
@@ -196,16 +110,7 @@
* @param value to associate with the name
* @return true if the value was set, false on database errors
*/
- default boolean putString(String name, String value) {
- return putStringForUser(name, value, getUserId());
- }
-
- /** See {@link #putString(String, String)}. */
- boolean putStringForUser(String name, String value, int userHandle);
-
- /** See {@link #putString(String, String)}. */
- boolean putStringForUser(@NonNull String name, @Nullable String value, @Nullable String tag,
- boolean makeDefault, @UserIdInt int userHandle, boolean overrideableByRestore);
+ boolean putString(String name, String value);
/**
* Store a name/value pair into the database.
@@ -262,12 +167,7 @@
* or not a valid integer.
*/
default int getInt(String name, int def) {
- return getIntForUser(name, def, getUserId());
- }
-
- /** See {@link #getInt(String, int)}. */
- default int getIntForUser(String name, int def, int userHandle) {
- String v = getStringForUser(name, userHandle);
+ String v = getString(name);
try {
return v != null ? Integer.parseInt(v) : def;
} catch (NumberFormatException e) {
@@ -292,14 +192,9 @@
*
* @return The setting's current value.
*/
- default int getInt(String name) throws Settings.SettingNotFoundException {
- return getIntForUser(name, getUserId());
- }
-
- /** See {@link #getInt(String)}. */
- default int getIntForUser(String name, int userHandle)
+ default int getInt(String name)
throws Settings.SettingNotFoundException {
- String v = getStringForUser(name, userHandle);
+ String v = getString(name);
try {
return Integer.parseInt(v);
} catch (NumberFormatException e) {
@@ -320,12 +215,7 @@
* @return true if the value was set, false on database errors
*/
default boolean putInt(String name, int value) {
- return putIntForUser(name, value, getUserId());
- }
-
- /** See {@link #putInt(String, int)}. */
- default boolean putIntForUser(String name, int value, int userHandle) {
- return putStringForUser(name, Integer.toString(value), userHandle);
+ return putString(name, Integer.toString(value));
}
/**
@@ -342,12 +232,7 @@
* or not a valid boolean.
*/
default boolean getBool(String name, boolean def) {
- return getBoolForUser(name, def, getUserId());
- }
-
- /** See {@link #getBool(String, boolean)}. */
- default boolean getBoolForUser(String name, boolean def, int userHandle) {
- return getIntForUser(name, def ? 1 : 0, userHandle) != 0;
+ return getInt(name, def ? 1 : 0) != 0;
}
/**
@@ -367,14 +252,9 @@
*
* @return The setting's current value.
*/
- default boolean getBool(String name) throws Settings.SettingNotFoundException {
- return getBoolForUser(name, getUserId());
- }
-
- /** See {@link #getBool(String)}. */
- default boolean getBoolForUser(String name, int userHandle)
+ default boolean getBool(String name)
throws Settings.SettingNotFoundException {
- return getIntForUser(name, userHandle) != 0;
+ return getInt(name) != 0;
}
/**
@@ -390,12 +270,7 @@
* @return true if the value was set, false on database errors
*/
default boolean putBool(String name, boolean value) {
- return putBoolForUser(name, value, getUserId());
- }
-
- /** See {@link #putBool(String, boolean)}. */
- default boolean putBoolForUser(String name, boolean value, int userHandle) {
- return putIntForUser(name, value ? 1 : 0, userHandle);
+ return putInt(name, value ? 1 : 0);
}
/**
@@ -412,12 +287,12 @@
* or not a valid {@code long}.
*/
default long getLong(String name, long def) {
- return getLongForUser(name, def, getUserId());
+ String valString = getString(name);
+ return parseLongOrUseDefault(valString, def);
}
- /** See {@link #getLong(String, long)}. */
- default long getLongForUser(String name, long def, int userHandle) {
- String valString = getStringForUser(name, userHandle);
+ /** Convert a string to a long, or uses a default if the string is malformed or null */
+ static long parseLongOrUseDefault(String valString, long def) {
long value;
try {
value = valString != null ? Long.parseLong(valString) : def;
@@ -443,14 +318,15 @@
* @throws Settings.SettingNotFoundException Thrown if a setting by the given
* name can't be found or the setting value is not an integer.
*/
- default long getLong(String name) throws Settings.SettingNotFoundException {
- return getLongForUser(name, getUserId());
+ default long getLong(String name)
+ throws Settings.SettingNotFoundException {
+ String valString = getString(name);
+ return parseLongOrThrow(name, valString);
}
- /** See {@link #getLong(String)}. */
- default long getLongForUser(String name, int userHandle)
+ /** Convert a string to a long, or throws an exception if the string is malformed or null */
+ static long parseLongOrThrow(String name, String valString)
throws Settings.SettingNotFoundException {
- String valString = getStringForUser(name, userHandle);
try {
return Long.parseLong(valString);
} catch (NumberFormatException e) {
@@ -471,12 +347,7 @@
* @return true if the value was set, false on database errors
*/
default boolean putLong(String name, long value) {
- return putLongForUser(name, value, getUserId());
- }
-
- /** See {@link #putLong(String, long)}. */
- default boolean putLongForUser(String name, long value, int userHandle) {
- return putStringForUser(name, Long.toString(value), userHandle);
+ return putString(name, Long.toString(value));
}
/**
@@ -493,12 +364,12 @@
* or not a valid float.
*/
default float getFloat(String name, float def) {
- return getFloatForUser(name, def, getUserId());
+ String v = getString(name);
+ return parseFloat(v, def);
}
- /** See {@link #getFloat(String)}. */
- default float getFloatForUser(String name, float def, int userHandle) {
- String v = getStringForUser(name, userHandle);
+ /** Convert a string to a float, or uses a default if the string is malformed or null */
+ static float parseFloat(String v, float def) {
try {
return v != null ? Float.parseFloat(v) : def;
} catch (NumberFormatException e) {
@@ -523,14 +394,15 @@
*
* @return The setting's current value.
*/
- default float getFloat(String name) throws Settings.SettingNotFoundException {
- return getFloatForUser(name, getUserId());
+ default float getFloat(String name)
+ throws Settings.SettingNotFoundException {
+ String v = getString(name);
+ return parseFloatOrThrow(name, v);
}
- /** See {@link #getFloat(String, float)}. */
- default float getFloatForUser(String name, int userHandle)
+ /** Convert a string to a float, or throws an exception if the string is malformed or null */
+ static float parseFloatOrThrow(String name, String v)
throws Settings.SettingNotFoundException {
- String v = getStringForUser(name, userHandle);
if (v == null) {
throw new Settings.SettingNotFoundException(name);
}
@@ -554,11 +426,6 @@
* @return true if the value was set, false on database errors
*/
default boolean putFloat(String name, float value) {
- return putFloatForUser(name, value, getUserId());
- }
-
- /** See {@link #putFloat(String, float)} */
- default boolean putFloatForUser(String name, float value, int userHandle) {
- return putStringForUser(name, Float.toString(value), userHandle);
+ return putString(name, Float.toString(value));
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxyExt.kt b/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxyExt.kt
index 561495e..7484368 100644
--- a/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxyExt.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxyExt.kt
@@ -27,7 +27,7 @@
object SettingsProxyExt {
/** Returns a flow of [Unit] that is invoked each time that content is updated. */
- fun SettingsProxy.observerFlow(
+ fun UserSettingsProxy.observerFlow(
@UserIdInt userId: Int,
vararg names: String,
): Flow<Unit> {
@@ -44,4 +44,22 @@
awaitClose { unregisterContentObserver(observer) }
}
}
+
+ /** Returns a flow of [Unit] that is invoked each time that content is updated. */
+ fun SettingsProxy.observerFlow(
+ vararg names: String,
+ ): Flow<Unit> {
+ return conflatedCallbackFlow {
+ val observer =
+ object : ContentObserver(null) {
+ override fun onChange(selfChange: Boolean) {
+ trySend(Unit)
+ }
+ }
+
+ names.forEach { name -> registerContentObserver(name, observer) }
+
+ awaitClose { unregisterContentObserver(observer) }
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/SystemSettings.java b/packages/SystemUI/src/com/android/systemui/util/settings/SystemSettings.java
index d57d749..c67c603 100644
--- a/packages/SystemUI/src/com/android/systemui/util/settings/SystemSettings.java
+++ b/packages/SystemUI/src/com/android/systemui/util/settings/SystemSettings.java
@@ -21,5 +21,5 @@
*
* See {@link SettingsProxy} for details.
*/
-public interface SystemSettings extends SettingsProxy {
+public interface SystemSettings extends UserSettingsProxy {
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/SystemSettingsImpl.java b/packages/SystemUI/src/com/android/systemui/util/settings/SystemSettingsImpl.java
index fba7ddf..658b299 100644
--- a/packages/SystemUI/src/com/android/systemui/util/settings/SystemSettingsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/util/settings/SystemSettingsImpl.java
@@ -20,6 +20,8 @@
import android.net.Uri;
import android.provider.Settings;
+import androidx.annotation.NonNull;
+
import com.android.systemui.settings.UserTracker;
import javax.inject.Inject;
@@ -74,7 +76,7 @@
}
@Override
- public boolean putString(String name, String value, String tag, boolean makeDefault) {
+ public boolean putString(@NonNull String name, String value, String tag, boolean makeDefault) {
throw new UnsupportedOperationException(
"This method only exists publicly for Settings.Secure and Settings.Global");
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/UserSettingsProxy.java b/packages/SystemUI/src/com/android/systemui/util/settings/UserSettingsProxy.java
new file mode 100644
index 0000000..0d6c0f5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/settings/UserSettingsProxy.java
@@ -0,0 +1,272 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util.settings;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.content.ContentResolver;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.UserHandle;
+import android.provider.Settings;
+
+import com.android.systemui.settings.UserTracker;
+
+/**
+ * Used to interact with per-user Settings.Secure and Settings.System settings (but not
+ * Settings.Global, since those do not vary per-user)
+ * <p>
+ * This interface can be implemented to give instance method (instead of static method) versions
+ * of Settings.Secure and Settings.System. It can be injected into class constructors and then
+ * faked or mocked as needed in tests.
+ * <p>
+ * You can ask for {@link SecureSettings} or {@link SystemSettings} to be injected as needed.
+ * <p>
+ * This class also provides {@link #registerContentObserver(String, ContentObserver)} methods,
+ * normally found on {@link ContentResolver} instances, unifying setting related actions in one
+ * place.
+ */
+public interface UserSettingsProxy extends SettingsProxy {
+
+ /**
+ * Returns that {@link UserTracker} this instance was constructed with.
+ */
+ UserTracker getUserTracker();
+
+ /**
+ * Returns the user id for the associated {@link ContentResolver}.
+ */
+ default int getUserId() {
+ return getContentResolver().getUserId();
+ }
+
+ /**
+ * Returns the actual current user handle when querying with the current user. Otherwise,
+ * returns the passed in user id.
+ */
+ default int getRealUserHandle(int userHandle) {
+ if (userHandle != UserHandle.USER_CURRENT) {
+ return userHandle;
+ }
+ return getUserTracker().getUserId();
+ }
+
+ @Override
+ default void registerContentObserver(Uri uri, ContentObserver settingsObserver) {
+ registerContentObserverForUser(uri, settingsObserver, getUserId());
+ }
+
+ /**
+ * Convenience wrapper around
+ * {@link ContentResolver#registerContentObserver(Uri, boolean, ContentObserver)}.'
+ */
+ @Override
+ default void registerContentObserver(Uri uri, boolean notifyForDescendants,
+ ContentObserver settingsObserver) {
+ registerContentObserverForUser(uri, notifyForDescendants, settingsObserver, getUserId());
+ }
+
+ /**
+ * Convenience wrapper around
+ * {@link ContentResolver#registerContentObserver(Uri, boolean, ContentObserver, int)}
+ *
+ * Implicitly calls {@link #getUriFor(String)} on the passed in name.
+ */
+ default void registerContentObserverForUser(
+ String name, ContentObserver settingsObserver, int userHandle) {
+ registerContentObserverForUser(
+ getUriFor(name), settingsObserver, userHandle);
+ }
+
+ /**
+ * Convenience wrapper around
+ * {@link ContentResolver#registerContentObserver(Uri, boolean, ContentObserver, int)}
+ */
+ default void registerContentObserverForUser(
+ Uri uri, ContentObserver settingsObserver, int userHandle) {
+ registerContentObserverForUser(
+ uri, false, settingsObserver, userHandle);
+ }
+
+ /**
+ * Convenience wrapper around
+ * {@link ContentResolver#registerContentObserver(Uri, boolean, ContentObserver, int)}
+ *
+ * Implicitly calls {@link #getUriFor(String)} on the passed in name.
+ */
+ default void registerContentObserverForUser(
+ String name, boolean notifyForDescendants, ContentObserver settingsObserver,
+ int userHandle) {
+ registerContentObserverForUser(
+ getUriFor(name), notifyForDescendants, settingsObserver, userHandle);
+ }
+
+ /**
+ * Convenience wrapper around
+ * {@link ContentResolver#registerContentObserver(Uri, boolean, ContentObserver, int)}
+ */
+ default void registerContentObserverForUser(
+ Uri uri, boolean notifyForDescendants, ContentObserver settingsObserver,
+ int userHandle) {
+ getContentResolver().registerContentObserver(
+ uri, notifyForDescendants, settingsObserver, getRealUserHandle(userHandle));
+ }
+
+ /**
+ * Look up a name in the database.
+ * @param name to look up in the table
+ * @return the corresponding value, or null if not present
+ */
+ @Override
+ default String getString(String name) {
+ return getStringForUser(name, getUserId());
+ }
+
+ /**See {@link #getString(String)}. */
+ String getStringForUser(String name, int userHandle);
+
+ /**
+ * Store a name/value pair into the database. Values written by this method will be
+ * overridden if a restore happens in the future.
+ *
+ * @param name to store
+ * @param value to associate with the name
+ * @return true if the value was set, false on database errors
+ */
+ boolean putString(String name, String value, boolean overrideableByRestore);
+
+ @Override
+ default boolean putString(String name, String value) {
+ return putStringForUser(name, value, getUserId());
+ }
+
+ /** See {@link #putString(String, String)}. */
+ boolean putStringForUser(String name, String value, int userHandle);
+
+ /** See {@link #putString(String, String)}. */
+ boolean putStringForUser(@NonNull String name, @Nullable String value, @Nullable String tag,
+ boolean makeDefault, @UserIdInt int userHandle, boolean overrideableByRestore);
+
+ @Override
+ default int getInt(String name, int def) {
+ return getIntForUser(name, def, getUserId());
+ }
+
+ /** See {@link #getInt(String, int)}. */
+ default int getIntForUser(String name, int def, int userHandle) {
+ String v = getStringForUser(name, userHandle);
+ try {
+ return v != null ? Integer.parseInt(v) : def;
+ } catch (NumberFormatException e) {
+ return def;
+ }
+ }
+
+ @Override
+ default int getInt(String name) throws Settings.SettingNotFoundException {
+ return getIntForUser(name, getUserId());
+ }
+
+ /** See {@link #getInt(String)}. */
+ default int getIntForUser(String name, int userHandle)
+ throws Settings.SettingNotFoundException {
+ String v = getStringForUser(name, userHandle);
+ try {
+ return Integer.parseInt(v);
+ } catch (NumberFormatException e) {
+ throw new Settings.SettingNotFoundException(name);
+ }
+ }
+
+ @Override
+ default boolean putInt(String name, int value) {
+ return putIntForUser(name, value, getUserId());
+ }
+
+ /** See {@link #putInt(String, int)}. */
+ default boolean putIntForUser(String name, int value, int userHandle) {
+ return putStringForUser(name, Integer.toString(value), userHandle);
+ }
+
+ @Override
+ default boolean getBool(String name, boolean def) {
+ return getBoolForUser(name, def, getUserId());
+ }
+
+ /** See {@link #getBool(String, boolean)}. */
+ default boolean getBoolForUser(String name, boolean def, int userHandle) {
+ return getIntForUser(name, def ? 1 : 0, userHandle) != 0;
+ }
+
+ @Override
+ default boolean getBool(String name) throws Settings.SettingNotFoundException {
+ return getBoolForUser(name, getUserId());
+ }
+
+ /** See {@link #getBool(String)}. */
+ default boolean getBoolForUser(String name, int userHandle)
+ throws Settings.SettingNotFoundException {
+ return getIntForUser(name, userHandle) != 0;
+ }
+
+ @Override
+ default boolean putBool(String name, boolean value) {
+ return putBoolForUser(name, value, getUserId());
+ }
+
+ /** See {@link #putBool(String, boolean)}. */
+ default boolean putBoolForUser(String name, boolean value, int userHandle) {
+ return putIntForUser(name, value ? 1 : 0, userHandle);
+ }
+
+ /** See {@link #getLong(String, long)}. */
+ default long getLongForUser(String name, long def, int userHandle) {
+ String valString = getStringForUser(name, userHandle);
+ return SettingsProxy.parseLongOrUseDefault(valString, def);
+ }
+
+ /** See {@link #getLong(String)}. */
+ default long getLongForUser(String name, int userHandle)
+ throws Settings.SettingNotFoundException {
+ String valString = getStringForUser(name, userHandle);
+ return SettingsProxy.parseLongOrThrow(name, valString);
+ }
+
+ /** See {@link #putLong(String, long)}. */
+ default boolean putLongForUser(String name, long value, int userHandle) {
+ return putStringForUser(name, Long.toString(value), userHandle);
+ }
+
+ /** See {@link #getFloat(String)}. */
+ default float getFloatForUser(String name, float def, int userHandle) {
+ String v = getStringForUser(name, userHandle);
+ return SettingsProxy.parseFloat(v, def);
+ }
+
+ /** See {@link #getFloat(String, float)}. */
+ default float getFloatForUser(String name, int userHandle)
+ throws Settings.SettingNotFoundException {
+ String v = getStringForUser(name, userHandle);
+ return SettingsProxy.parseFloatOrThrow(name, v);
+ }
+
+ /** See {@link #putFloat(String, float)} */
+ default boolean putFloatForUser(String name, float value, int userHandle) {
+ return putStringForUser(name, Float.toString(value), userHandle);
+ }
+}
diff --git a/packages/SystemUI/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/tests/src/com/android/keyguard/ClockEventControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
index 6afa525..4d8768f 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
@@ -25,9 +25,11 @@
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
-import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+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.log.LogBuffer
import com.android.systemui.plugins.ClockAnimations
import com.android.systemui.plugins.ClockController
@@ -47,8 +49,8 @@
import java.util.TimeZone
import java.util.concurrent.Executor
import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.runBlocking
-import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.yield
import org.junit.Assert.assertEquals
import org.junit.Before
@@ -90,10 +92,10 @@
@Mock private lateinit var smallClockEvents: ClockFaceEvents
@Mock private lateinit var largeClockEvents: ClockFaceEvents
@Mock private lateinit var parentView: View
- @Mock private lateinit var transitionRepository: KeyguardTransitionRepository
private lateinit var repository: FakeKeyguardRepository
@Mock private lateinit var smallLogBuffer: LogBuffer
@Mock private lateinit var largeLogBuffer: LogBuffer
+ @Mock private lateinit var keyguardTransitionInteractor: KeyguardTransitionInteractor
private lateinit var underTest: ClockEventController
@Before
@@ -125,17 +127,13 @@
withDeps.featureFlags.apply {
set(Flags.REGION_SAMPLING, false)
- set(Flags.DOZING_MIGRATION_1, false)
+ set(Flags.MIGRATE_KEYGUARD_STATUS_VIEW, false)
set(Flags.FACE_AUTH_REFACTOR, false)
}
underTest =
ClockEventController(
withDeps.keyguardInteractor,
- KeyguardTransitionInteractorFactory.create(
- scope = TestScope().backgroundScope,
- featureFlags = withDeps.featureFlags,
- )
- .keyguardTransitionInteractor,
+ keyguardTransitionInteractor,
broadcastDispatcher,
batteryController,
keyguardUpdateMonitor,
@@ -316,6 +314,68 @@
}
@Test
+ fun listenForDozeAmountTransition_updatesClockDozeAmount() =
+ runBlocking(IMMEDIATE) {
+ val transitionStep = MutableStateFlow(TransitionStep())
+ whenever(keyguardTransitionInteractor.dozeAmountTransition).thenReturn(transitionStep)
+
+ val job = underTest.listenForDozeAmountTransition(this)
+ transitionStep.value =
+ TransitionStep(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.AOD,
+ value = 0.4f
+ )
+ yield()
+
+ verify(animations, times(2)).doze(0.4f)
+
+ job.cancel()
+ }
+
+ @Test
+ fun listenForTransitionToAodFromGone_updatesClockDozeAmountToOne() =
+ runBlocking(IMMEDIATE) {
+ val transitionStep = MutableStateFlow(TransitionStep())
+ whenever(keyguardTransitionInteractor.transitionStepsToState(KeyguardState.AOD))
+ .thenReturn(transitionStep)
+
+ val job = underTest.listenForAnyStateToAodTransition(this)
+ transitionStep.value =
+ TransitionStep(
+ from = KeyguardState.GONE,
+ to = KeyguardState.AOD,
+ transitionState = TransitionState.STARTED,
+ )
+ yield()
+
+ verify(animations, times(2)).doze(1f)
+
+ job.cancel()
+ }
+
+ @Test
+ fun listenForTransitionToAodFromLockscreen_neverUpdatesClockDozeAmount() =
+ runBlocking(IMMEDIATE) {
+ val transitionStep = MutableStateFlow(TransitionStep())
+ whenever(keyguardTransitionInteractor.transitionStepsToState(KeyguardState.AOD))
+ .thenReturn(transitionStep)
+
+ val job = underTest.listenForAnyStateToAodTransition(this)
+ transitionStep.value =
+ TransitionStep(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.AOD,
+ transitionState = TransitionState.STARTED,
+ )
+ yield()
+
+ verify(animations, never()).doze(1f)
+
+ job.cancel()
+ }
+
+ @Test
fun unregisterListeners_validate() =
runBlocking(IMMEDIATE) {
underTest.unregisterListeners()
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
index 62f9a9d..20d4eb9 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
@@ -810,7 +810,6 @@
SceneKey.Bouncer,
flowOf(.5f),
false,
- isUserInputOngoing = flowOf(false),
)
runCurrent()
sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer, null), "reason")
@@ -826,8 +825,7 @@
SceneKey.Bouncer,
SceneKey.Gone,
flowOf(.5f),
- false,
- isUserInputOngoing = flowOf(false),
+ false
)
runCurrent()
sceneInteractor.onSceneChanged(SceneModel(SceneKey.Gone, null), "reason")
@@ -844,8 +842,7 @@
SceneKey.Gone,
SceneKey.Bouncer,
flowOf(.5f),
- false,
- isUserInputOngoing = flowOf(false),
+ false
)
runCurrent()
sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer, null), "reason")
@@ -863,8 +860,7 @@
SceneKey.Bouncer,
SceneKey.Gone,
flowOf(.5f),
- false,
- isUserInputOngoing = flowOf(false),
+ false
)
runCurrent()
sceneInteractor.onSceneChanged(SceneModel(SceneKey.Gone, null), "reason")
@@ -880,7 +876,6 @@
SceneKey.Lockscreen,
flowOf(.5f),
false,
- isUserInputOngoing = flowOf(false),
)
runCurrent()
sceneInteractor.onSceneChanged(SceneModel(SceneKey.Lockscreen, null), "reason")
@@ -898,7 +893,6 @@
SceneKey.Gone,
flowOf(.5f),
false,
- isUserInputOngoing = flowOf(false),
)
runCurrent()
sceneInteractor.onSceneChanged(SceneModel(SceneKey.Gone, null), "reason")
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerBaseTest.java
index 3b8e02f..22c75d8 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerBaseTest.java
@@ -16,6 +16,8 @@
package com.android.keyguard;
+import static kotlinx.coroutines.flow.FlowKt.emptyFlow;
+
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -31,6 +33,7 @@
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository;
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory;
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
import com.android.systemui.power.data.repository.FakePowerRepository;
import com.android.systemui.power.domain.interactor.PowerInteractorFactory;
import com.android.systemui.statusbar.notification.AnimatableProperty;
@@ -60,6 +63,7 @@
@Mock protected FeatureFlags mFeatureFlags;
@Mock protected InteractionJankMonitor mInteractionJankMonitor;
@Mock protected ViewTreeObserver mViewTreeObserver;
+ @Mock protected KeyguardTransitionInteractor mKeyguardTransitionInteractor;
@Mock protected DumpManager mDumpManager;
protected FakeKeyguardRepository mFakeKeyguardRepository;
protected FakePowerRepository mFakePowerRepository;
@@ -90,6 +94,7 @@
mFeatureFlags,
mInteractionJankMonitor,
deps.getKeyguardInteractor(),
+ mKeyguardTransitionInteractor,
mDumpManager,
PowerInteractorFactory.create(
mFakePowerRepository
@@ -105,8 +110,8 @@
};
when(mKeyguardStatusView.getViewTreeObserver()).thenReturn(mViewTreeObserver);
-
when(mKeyguardClockSwitchController.getView()).thenReturn(mKeyguardClockSwitch);
+ when(mKeyguardTransitionInteractor.getGoneToAodTransition()).thenReturn(emptyFlow());
}
protected void givenViewAttached() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java
index 3da7261..5346db1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java
@@ -19,6 +19,9 @@
import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU;
import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR;
+import static com.android.systemui.Flags.FLAG_FLOATING_MENU_OVERLAPS_NAV_BARS_FLAG;
+import static com.android.systemui.flags.SetFlagsRuleExtensionsKt.setFlagDefault;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
@@ -87,6 +90,7 @@
@Before
public void setUp() throws Exception {
+ setFlagDefault(mSetFlagsRule, FLAG_FLOATING_MENU_OVERLAPS_NAV_BARS_FLAG);
MockitoAnnotations.initMocks(this);
mContextWrapper = new ContextWrapper(mContext) {
@Override
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/DismissAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/DismissAnimationControllerTest.java
index fd258e3..3b2ea0f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/DismissAnimationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/DismissAnimationControllerTest.java
@@ -16,6 +16,9 @@
package com.android.systemui.accessibility.floatingmenu;
+import static com.android.systemui.Flags.FLAG_FLOATING_MENU_OVERLAPS_NAV_BARS_FLAG;
+import static com.android.systemui.flags.SetFlagsRuleExtensionsKt.setFlagDefault;
+
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
@@ -56,6 +59,7 @@
@Before
public void setUp() throws Exception {
+ setFlagDefault(mSetFlagsRule, FLAG_FLOATING_MENU_OVERLAPS_NAV_BARS_FLAG);
final WindowManager stubWindowManager = mContext.getSystemService(WindowManager.class);
final MenuViewModel stubMenuViewModel = new MenuViewModel(mContext, mAccessibilityManager,
mock(SecureSettings.class));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationControllerTest.java
index 3a8bcd0..76a3153 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationControllerTest.java
@@ -16,6 +16,9 @@
package com.android.systemui.accessibility.floatingmenu;
+import static com.android.systemui.Flags.FLAG_FLOATING_MENU_OVERLAPS_NAV_BARS_FLAG;
+import static com.android.systemui.flags.SetFlagsRuleExtensionsKt.setFlagDefault;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.any;
@@ -75,6 +78,7 @@
@Before
public void setUp() throws Exception {
+ setFlagDefault(mSetFlagsRule, FLAG_FLOATING_MENU_OVERLAPS_NAV_BARS_FLAG);
final WindowManager stubWindowManager = mContext.getSystemService(WindowManager.class);
final MenuViewAppearance stubMenuViewAppearance = new MenuViewAppearance(mContext,
stubWindowManager);
@@ -96,6 +100,7 @@
Prefs.putBoolean(mContext, Prefs.Key.HAS_ACCESSIBILITY_FLOATING_MENU_TUCKED,
mLastIsMoveToTucked);
mEndListenerCaptor.getAllValues().clear();
+ mMenuAnimationController.mPositionAnimations.values().forEach(DynamicAnimation::cancel);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuEduTooltipViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuEduTooltipViewTest.java
index 9b81947..83bcd8d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuEduTooltipViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuEduTooltipViewTest.java
@@ -16,6 +16,9 @@
package com.android.systemui.accessibility.floatingmenu;
+import static com.android.systemui.Flags.FLAG_FLOATING_MENU_OVERLAPS_NAV_BARS_FLAG;
+import static com.android.systemui.flags.SetFlagsRuleExtensionsKt.setFlagDefault;
+
import static com.google.common.truth.Truth.assertThat;
import android.content.res.Resources;
@@ -43,6 +46,7 @@
@Before
public void setUp() throws Exception {
+ setFlagDefault(mSetFlagsRule, FLAG_FLOATING_MENU_OVERLAPS_NAV_BARS_FLAG);
final WindowManager windowManager = mContext.getSystemService(WindowManager.class);
mMenuViewAppearance = new MenuViewAppearance(mContext, windowManager);
mMenuEduTooltipView = new MenuEduTooltipView(mContext, mMenuViewAppearance);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegateTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegateTest.java
index 5764839..e01f1b7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegateTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegateTest.java
@@ -19,6 +19,9 @@
import static androidx.core.view.accessibility.AccessibilityNodeInfoCompat.ACTION_ACCESSIBILITY_FOCUS;
import static androidx.core.view.accessibility.AccessibilityNodeInfoCompat.ACTION_CLEAR_ACCESSIBILITY_FOCUS;
+import static com.android.systemui.Flags.FLAG_FLOATING_MENU_OVERLAPS_NAV_BARS_FLAG;
+import static com.android.systemui.flags.SetFlagsRuleExtensionsKt.setFlagDefault;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.doReturn;
@@ -72,6 +75,7 @@
@Before
public void setUp() {
+ setFlagDefault(mSetFlagsRule, FLAG_FLOATING_MENU_OVERLAPS_NAV_BARS_FLAG);
final WindowManager stubWindowManager = mContext.getSystemService(WindowManager.class);
final MenuViewAppearance stubMenuViewAppearance = new MenuViewAppearance(mContext,
stubWindowManager);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java
index 98be49f..a88ee10 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java
@@ -18,6 +18,9 @@
import static android.view.View.OVER_SCROLL_NEVER;
+import static com.android.systemui.Flags.FLAG_FLOATING_MENU_OVERLAPS_NAV_BARS_FLAG;
+import static com.android.systemui.flags.SetFlagsRuleExtensionsKt.setFlagDefault;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.anyFloat;
@@ -33,6 +36,7 @@
import android.view.WindowManager;
import android.view.accessibility.AccessibilityManager;
+import androidx.dynamicanimation.animation.DynamicAnimation;
import androidx.recyclerview.widget.RecyclerView;
import androidx.test.filters.SmallTest;
@@ -79,6 +83,7 @@
@Before
public void setUp() throws Exception {
+ setFlagDefault(mSetFlagsRule, FLAG_FLOATING_MENU_OVERLAPS_NAV_BARS_FLAG);
final WindowManager windowManager = mContext.getSystemService(WindowManager.class);
final MenuViewModel stubMenuViewModel = new MenuViewModel(mContext, mAccessibilityManager,
mock(SecureSettings.class));
@@ -213,5 +218,6 @@
@After
public void tearDown() {
mMotionEventHelper.recycleEvents();
+ mMenuAnimationController.mPositionAnimations.values().forEach(DynamicAnimation::cancel);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerControllerTest.java
index 31824ec..41e5c20 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerControllerTest.java
@@ -19,6 +19,9 @@
import static android.view.WindowInsets.Type.displayCutout;
import static android.view.WindowInsets.Type.systemBars;
+import static com.android.systemui.Flags.FLAG_FLOATING_MENU_OVERLAPS_NAV_BARS_FLAG;
+import static com.android.systemui.flags.SetFlagsRuleExtensionsKt.setFlagDefault;
+
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.verify;
@@ -73,6 +76,7 @@
@Before
public void setUp() throws Exception {
+ setFlagDefault(mSetFlagsRule, FLAG_FLOATING_MENU_OVERLAPS_NAV_BARS_FLAG);
final WindowManager wm = mContext.getSystemService(WindowManager.class);
doAnswer(invocation -> wm.getMaximumWindowMetrics()).when(
mWindowManager).getMaximumWindowMetrics();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java
index 5bb5e01..b0776c9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java
@@ -22,7 +22,9 @@
import static android.view.WindowInsets.Type.ime;
import static android.view.WindowInsets.Type.systemBars;
+import static com.android.systemui.Flags.FLAG_FLOATING_MENU_OVERLAPS_NAV_BARS_FLAG;
import static com.android.systemui.accessibility.floatingmenu.MenuViewLayer.LayerIndex;
+import static com.android.systemui.flags.SetFlagsRuleExtensionsKt.setFlagDefault;
import static com.google.common.truth.Truth.assertThat;
@@ -50,6 +52,7 @@
import android.view.WindowMetrics;
import android.view.accessibility.AccessibilityManager;
+import androidx.dynamicanimation.animation.DynamicAnimation;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -110,6 +113,7 @@
@Before
public void setUp() throws Exception {
+ setFlagDefault(mSetFlagsRule, FLAG_FLOATING_MENU_OVERLAPS_NAV_BARS_FLAG);
final Rect mDisplayBounds = new Rect();
mDisplayBounds.set(/* left= */ 0, /* top= */ 0, DISPLAY_WINDOW_WIDTH,
DISPLAY_WINDOW_HEIGHT);
@@ -145,6 +149,7 @@
UserHandle.USER_CURRENT);
mMenuView.updateMenuMoveToTucked(/* isMoveToTucked= */ false);
+ mMenuAnimationController.mPositionAnimations.values().forEach(DynamicAnimation::cancel);
mMenuViewLayer.onDetachedFromWindow();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java
index 5cd0fd0..ac2bfaf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java
@@ -18,6 +18,9 @@
import static android.app.UiModeManager.MODE_NIGHT_YES;
+import static com.android.systemui.Flags.FLAG_FLOATING_MENU_OVERLAPS_NAV_BARS_FLAG;
+import static com.android.systemui.flags.SetFlagsRuleExtensionsKt.setFlagDefault;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.mock;
@@ -67,6 +70,7 @@
@Before
public void setUp() throws Exception {
+ setFlagDefault(mSetFlagsRule, FLAG_FLOATING_MENU_OVERLAPS_NAV_BARS_FLAG);
mUiModeManager = mContext.getSystemService(UiModeManager.class);
mNightMode = mUiModeManager.getNightMode();
mUiModeManager.setNightMode(MODE_NIGHT_YES);
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/UdfpsControllerOverlayTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
index e9e9624..ebe13fe 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
@@ -29,7 +29,6 @@
import android.view.LayoutInflater
import android.view.MotionEvent
import android.view.Surface
-import android.view.Surface.ROTATION_0
import android.view.Surface.Rotation
import android.view.View
import android.view.WindowManager
@@ -45,7 +44,6 @@
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
import com.android.systemui.dump.DumpManager
import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.ui.viewmodel.UdfpsKeyguardViewModels
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.LockscreenShadeTransitionController
@@ -141,7 +139,6 @@
) {
controllerOverlay = UdfpsControllerOverlay(
context,
- fingerprintManager,
inflater,
windowManager,
accessibilityManager,
@@ -155,7 +152,6 @@
keyguardStateController,
unlockedScreenOffAnimationController,
udfpsDisplayMode,
- secureSettings,
REQUEST_ID,
reason,
controllerCallback,
@@ -165,7 +161,6 @@
primaryBouncerInteractor,
alternateBouncerInteractor,
isDebuggable,
- udfpsUtils,
udfpsKeyguardAccessibilityDelegate,
udfpsKeyguardViewModels,
)
@@ -212,8 +207,8 @@
val lp = layoutParamsCaptor.value
assertThat(lp.x).isEqualTo(0)
assertThat(lp.y).isEqualTo(0)
- assertThat(lp.width).isEqualTo(SENSOR_WIDTH)
- assertThat(lp.height).isEqualTo(SENSOR_HEIGHT)
+ assertThat(lp.width).isEqualTo(DISPLAY_WIDTH)
+ assertThat(lp.height).isEqualTo(DISPLAY_HEIGHT)
}
}
@@ -230,8 +225,8 @@
val lp = layoutParamsCaptor.value
assertThat(lp.x).isEqualTo(0)
assertThat(lp.y).isEqualTo(0)
- assertThat(lp.width).isEqualTo(SENSOR_WIDTH)
- assertThat(lp.height).isEqualTo(SENSOR_HEIGHT)
+ assertThat(lp.width).isEqualTo(DISPLAY_WIDTH)
+ assertThat(lp.height).isEqualTo(DISPLAY_HEIGHT)
}
}
@@ -247,9 +242,9 @@
// Sensor should be in the bottom left corner in ROTATION_90.
val lp = layoutParamsCaptor.value
assertThat(lp.x).isEqualTo(0)
- assertThat(lp.y).isEqualTo(DISPLAY_WIDTH - SENSOR_WIDTH)
- assertThat(lp.width).isEqualTo(SENSOR_HEIGHT)
- assertThat(lp.height).isEqualTo(SENSOR_WIDTH)
+ assertThat(lp.y).isEqualTo(0)
+ assertThat(lp.width).isEqualTo(DISPLAY_HEIGHT)
+ assertThat(lp.height).isEqualTo(DISPLAY_WIDTH)
}
}
@@ -264,10 +259,10 @@
// Sensor should be in the top right corner in ROTATION_270.
val lp = layoutParamsCaptor.value
- assertThat(lp.x).isEqualTo(DISPLAY_HEIGHT - SENSOR_HEIGHT)
+ assertThat(lp.x).isEqualTo(0)
assertThat(lp.y).isEqualTo(0)
- assertThat(lp.width).isEqualTo(SENSOR_HEIGHT)
- assertThat(lp.height).isEqualTo(SENSOR_WIDTH)
+ assertThat(lp.width).isEqualTo(DISPLAY_HEIGHT)
+ assertThat(lp.height).isEqualTo(DISPLAY_WIDTH)
}
}
@@ -345,11 +340,10 @@
}
@Test
- fun smallOverlayOnEnrollmentWithA11y() = withRotation(ROTATION_0) {
+ fun smallOverlayOnEnrollmentWithA11y() = withRotation(Surface.ROTATION_0) {
withReason(REASON_ENROLL_ENROLLING) {
// When a11y enabled during enrollment
whenever(accessibilityManager.isTouchExplorationEnabled).thenReturn(true)
- whenever(featureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)).thenReturn(true)
controllerOverlay.show(udfpsController, overlayParams)
verify(windowManager).addView(
@@ -363,22 +357,4 @@
assertThat(lp.height).isEqualTo(overlayParams.sensorBounds.height())
}
}
-
- @Test
- fun fullScreenOverlayWithNewTouchDetectionEnabled() = withRotation(ROTATION_0) {
- withReason(REASON_AUTH_KEYGUARD) {
- whenever(featureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)).thenReturn(true)
-
- controllerOverlay.show(udfpsController, overlayParams)
- verify(windowManager).addView(
- eq(controllerOverlay.overlayView),
- layoutParamsCaptor.capture()
- )
-
- // Layout params should use natural display width and height
- val lp = layoutParamsCaptor.value
- assertThat(lp.width).isEqualTo(overlayParams.naturalDisplayWidth)
- assertThat(lp.height).isEqualTo(overlayParams.naturalDisplayHeight)
- }
- }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
index a36f4e9..dcb5398 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -88,7 +88,6 @@
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor;
import com.android.systemui.keyguard.ui.viewmodel.UdfpsKeyguardViewModels;
@@ -105,7 +104,6 @@
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.concurrency.FakeExecution;
import com.android.systemui.util.concurrency.FakeExecutor;
-import com.android.systemui.util.settings.SecureSettings;
import com.android.systemui.util.time.FakeSystemClock;
import com.android.systemui.util.time.SystemClock;
@@ -122,7 +120,6 @@
import java.util.ArrayList;
import java.util.List;
-import java.util.Optional;
import javax.inject.Provider;
@@ -206,8 +203,6 @@
@Mock
private ActivityLaunchAnimator mActivityLaunchAnimator;
@Mock
- private AlternateUdfpsTouchProvider mAlternateTouchProvider;
- @Mock
private PrimaryBouncerInteractor mPrimaryBouncerInteractor;
@Mock
private SinglePointerTouchProcessor mSinglePointerTouchProcessor;
@@ -216,8 +211,6 @@
@Mock
private AlternateBouncerInteractor mAlternateBouncerInteractor;
@Mock
- private SecureSettings mSecureSettings;
- @Mock
private UdfpsKeyguardAccessibilityDelegate mUdfpsKeyguardAccessibilityDelegate;
@Mock
private Provider<UdfpsKeyguardViewModels> mUdfpsKeyguardViewModels;
@@ -239,7 +232,6 @@
private ScreenLifecycle.Observer mScreenObserver;
private FingerprintSensorPropertiesInternal mOpticalProps;
private FingerprintSensorPropertiesInternal mUltrasonicProps;
- private UdfpsUtils mUdfpsUtils;
@Mock
private InputManager mInputManager;
@Mock
@@ -250,8 +242,6 @@
mContext.getOrCreateTestableResources()
.addOverride(com.android.internal.R.bool.config_ignoreUdfpsVote, false);
- mUdfpsUtils = new UdfpsUtils();
-
when(mLayoutInflater.inflate(R.layout.udfps_view, null, false))
.thenReturn(mUdfpsView);
when(mLayoutInflater.inflate(R.layout.udfps_keyguard_view_legacy, null))
@@ -292,24 +282,13 @@
// Create a fake background executor.
mBiometricExecutor = new FakeExecutor(new FakeSystemClock());
- initUdfpsController(true /* hasAlternateTouchProvider */);
+ initUdfpsController(mOpticalProps);
}
-
- private void initUdfpsController(boolean hasAlternateTouchProvider) {
- initUdfpsController(mOpticalProps, hasAlternateTouchProvider);
- }
-
- private void initUdfpsController(FingerprintSensorPropertiesInternal sensorProps,
- boolean hasAlternateTouchProvider) {
+ private void initUdfpsController(FingerprintSensorPropertiesInternal sensorProps) {
reset(mFingerprintManager);
reset(mScreenLifecycle);
- final Optional<Provider<AlternateUdfpsTouchProvider>> alternateTouchProvider =
- hasAlternateTouchProvider ? Optional.of(
- (Provider<AlternateUdfpsTouchProvider>) () -> mAlternateTouchProvider)
- : Optional.empty();
-
mUdfpsController = new UdfpsController(
mContext,
new FakeExecution(),
@@ -339,15 +318,12 @@
mSystemUIDialogManager,
mLatencyTracker,
mActivityLaunchAnimator,
- alternateTouchProvider,
mBiometricExecutor,
mPrimaryBouncerInteractor,
mSinglePointerTouchProcessor,
mSessionTracker,
mAlternateBouncerInteractor,
- mSecureSettings,
mInputManager,
- mUdfpsUtils,
mock(KeyguardFaceAuthInteractor.class),
mUdfpsKeyguardAccessibilityDelegate,
mUdfpsKeyguardViewModels
@@ -374,17 +350,15 @@
public void onActionDownTouch_whenCanDismissLockScreen_entersDevice() throws RemoteException {
// GIVEN can dismiss lock screen and the current animation is an UdfpsKeyguardViewController
when(mKeyguardStateController.canDismissLockScreen()).thenReturn(true);
- when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true);
when(mUdfpsView.getAnimationViewController()).thenReturn(mUdfpsKeyguardViewController);
- // GIVEN that the overlay is showing
- mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
- BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
- mFgExecutor.runAllReady();
+ final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult =
+ givenFingerEvent(InteractionEvent.DOWN, InteractionEvent.UP, false);
// WHEN ACTION_DOWN is received
- verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
- MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0);
+ when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
+ touchProcessorResult.first);
+ MotionEvent downEvent = obtainMotionEvent(ACTION_DOWN, 0, 0, 0, 0);
mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent);
mBiometricExecutor.runAllReady();
downEvent.recycle();
@@ -408,16 +382,14 @@
throws RemoteException {
// GIVEN can dismiss lock screen and the current animation is an UdfpsKeyguardViewController
when(mKeyguardStateController.canDismissLockScreen()).thenReturn(true);
- when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true);
when(mUdfpsView.getAnimationViewController()).thenReturn(mUdfpsKeyguardViewController);
- // GIVEN that the overlay is showing
- mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
- BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
- mFgExecutor.runAllReady();
+ final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult =
+ givenFingerEvent(InteractionEvent.DOWN, InteractionEvent.UP, false);
// WHEN ACTION_MOVE is received
- verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
+ when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
+ touchProcessorResult.first);
MotionEvent moveEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 0, 0, 0);
if (stale) {
mOverlayController.hideUdfpsOverlay(mOpticalProps.sensorId);
@@ -436,22 +408,22 @@
public void onMultipleTouch_whenCanDismissLockScreen_entersDeviceOnce() throws RemoteException {
// GIVEN can dismiss lock screen and the current animation is an UdfpsKeyguardViewController
when(mKeyguardStateController.canDismissLockScreen()).thenReturn(true);
- when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true);
when(mUdfpsView.getAnimationViewController()).thenReturn(mUdfpsKeyguardViewController);
- // GIVEN that the overlay is showing
- mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
- BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
- mFgExecutor.runAllReady();
+ final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult =
+ givenFingerEvent(InteractionEvent.DOWN, InteractionEvent.UNCHANGED, false);
- // WHEN multiple touches are received
- verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
+ // GIVEN that the overlay is showing
+ when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
+ touchProcessorResult.first);
MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0);
mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent);
mBiometricExecutor.runAllReady();
downEvent.recycle();
+
MotionEvent moveEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 0, 0, 0);
- mTouchListenerCaptor.getValue().onTouch(mUdfpsView, moveEvent);
+ when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
+ touchProcessorResult.second);
mTouchListenerCaptor.getValue().onTouch(mUdfpsView, moveEvent);
mBiometricExecutor.runAllReady();
moveEvent.recycle();
@@ -593,22 +565,17 @@
private static class TestParams {
public final FingerprintSensorPropertiesInternal sensorProps;
- public final boolean hasAlternateTouchProvider;
- TestParams(FingerprintSensorPropertiesInternal sensorProps,
- boolean hasAlternateTouchProvider) {
+ TestParams(FingerprintSensorPropertiesInternal sensorProps) {
this.sensorProps = sensorProps;
- this.hasAlternateTouchProvider = hasAlternateTouchProvider;
}
}
private void runWithAllParams(ThrowingConsumer<TestParams> testParamsConsumer) {
for (FingerprintSensorPropertiesInternal sensorProps : List.of(mOpticalProps,
mUltrasonicProps)) {
- for (boolean hasAlternateTouchProvider : new boolean[]{false, true}) {
- initUdfpsController(sensorProps, hasAlternateTouchProvider);
- testParamsConsumer.accept(new TestParams(sensorProps, hasAlternateTouchProvider));
- }
+ initUdfpsController(sensorProps);
+ testParamsConsumer.accept(new TestParams(sensorProps));
}
}
@@ -621,23 +588,33 @@
private void onTouch_propagatesTouchInNativeOrientationAndResolutionParameterized(
TestParams testParams) throws RemoteException {
reset(mUdfpsView);
+ when(mUdfpsView.getViewRootImpl()).thenReturn(mViewRootImpl);
final Rect sensorBounds = new Rect(1000, 1900, 1080, 1920); // Bottom right corner.
+ final int pointerId = 0;
final int displayWidth = 1080;
final int displayHeight = 1920;
- final float scaleFactor = 0.75f; // This means the native resolution is 1440x2560.
+ final float scaleFactor = 1f; // This means the native resolution is 1440x2560.
final float touchMinor = 10f;
final float touchMajor = 20f;
+ final float orientation = 30f;
// Expecting a touch at the very bottom right corner in native orientation and resolution.
- final int expectedX = (int) (displayWidth / scaleFactor);
- final int expectedY = (int) (displayHeight / scaleFactor);
+ final float expectedX = displayWidth / scaleFactor;
+ final float expectedY = displayHeight / scaleFactor;
final float expectedMinor = touchMinor / scaleFactor;
final float expectedMajor = touchMajor / scaleFactor;
// Configure UdfpsView to accept the ACTION_DOWN event
when(mUdfpsView.isDisplayConfigured()).thenReturn(false);
- when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true);
+
+ // GIVEN a valid touch on sensor
+ NormalizedTouchData touchData = new NormalizedTouchData(pointerId, displayWidth,
+ displayHeight, touchMinor, touchMajor, orientation, 0L, 0L);
+ TouchProcessorResult processorDownResult = new TouchProcessorResult.ProcessedTouch(
+ InteractionEvent.DOWN, 1, touchData);
+ when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
+ processorDownResult);
// Show the overlay.
mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId,
@@ -654,21 +631,12 @@
mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event);
mBiometricExecutor.runAllReady();
event.recycle();
- event = obtainMotionEvent(ACTION_MOVE, displayWidth, displayHeight, touchMinor, touchMajor);
- mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event);
- mBiometricExecutor.runAllReady();
- event.recycle();
- if (testParams.hasAlternateTouchProvider) {
- verify(mAlternateTouchProvider).onPointerDown(eq(TEST_REQUEST_ID), eq(expectedX),
- eq(expectedY), eq(expectedMinor), eq(expectedMajor));
- } else {
- verify(mFingerprintManager).onPointerDown(eq(TEST_REQUEST_ID),
- eq(testParams.sensorProps.sensorId), eq(expectedX), eq(expectedY),
- eq(expectedMinor), eq(expectedMajor));
- }
+ verify(mFingerprintManager).onPointerDown(eq(TEST_REQUEST_ID),
+ eq(testParams.sensorProps.sensorId), eq(pointerId), eq(expectedX), eq(expectedY),
+ eq(expectedMinor), eq(expectedMajor), eq(orientation), anyLong(), anyLong(),
+ anyBoolean());
// Test ROTATION_90
- reset(mAlternateTouchProvider);
reset(mFingerprintManager);
mUdfpsController.updateOverlayParams(testParams.sensorProps,
new UdfpsOverlayParams(sensorBounds, sensorBounds, displayWidth, displayHeight,
@@ -677,21 +645,12 @@
mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event);
mBiometricExecutor.runAllReady();
event.recycle();
- event = obtainMotionEvent(ACTION_MOVE, displayHeight, 0, touchMinor, touchMajor);
- mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event);
- mBiometricExecutor.runAllReady();
- event.recycle();
- if (testParams.hasAlternateTouchProvider) {
- verify(mAlternateTouchProvider).onPointerDown(eq(TEST_REQUEST_ID), eq(expectedX),
- eq(expectedY), eq(expectedMinor), eq(expectedMajor));
- } else {
- verify(mFingerprintManager).onPointerDown(eq(TEST_REQUEST_ID),
- eq(testParams.sensorProps.sensorId), eq(expectedX), eq(expectedY),
- eq(expectedMinor), eq(expectedMajor));
- }
+ verify(mFingerprintManager).onPointerDown(eq(TEST_REQUEST_ID),
+ eq(testParams.sensorProps.sensorId), eq(pointerId), eq(expectedX), eq(expectedY),
+ eq(expectedMinor), eq(expectedMajor), eq(orientation), anyLong(), anyLong(),
+ anyBoolean());
// Test ROTATION_270
- reset(mAlternateTouchProvider);
reset(mFingerprintManager);
mUdfpsController.updateOverlayParams(testParams.sensorProps,
new UdfpsOverlayParams(sensorBounds, sensorBounds, displayWidth, displayHeight,
@@ -700,21 +659,12 @@
mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event);
mBiometricExecutor.runAllReady();
event.recycle();
- event = obtainMotionEvent(ACTION_MOVE, 0, displayWidth, touchMinor, touchMajor);
- mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event);
- mBiometricExecutor.runAllReady();
- event.recycle();
- if (testParams.hasAlternateTouchProvider) {
- verify(mAlternateTouchProvider).onPointerDown(eq(TEST_REQUEST_ID), eq(expectedX),
- eq(expectedY), eq(expectedMinor), eq(expectedMajor));
- } else {
- verify(mFingerprintManager).onPointerDown(eq(TEST_REQUEST_ID),
- eq(testParams.sensorProps.sensorId), eq(expectedX), eq(expectedY),
- eq(expectedMinor), eq(expectedMajor));
- }
+ verify(mFingerprintManager).onPointerDown(eq(TEST_REQUEST_ID),
+ eq(testParams.sensorProps.sensorId), eq(pointerId), eq(expectedX), eq(expectedY),
+ eq(expectedMinor), eq(expectedMajor), eq(orientation), anyLong(), anyLong(),
+ anyBoolean());
// Test ROTATION_180
- reset(mAlternateTouchProvider);
reset(mFingerprintManager);
mUdfpsController.updateOverlayParams(testParams.sensorProps,
new UdfpsOverlayParams(sensorBounds, sensorBounds, displayWidth, displayHeight,
@@ -724,18 +674,10 @@
mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event);
mBiometricExecutor.runAllReady();
event.recycle();
- event = obtainMotionEvent(ACTION_MOVE, displayWidth, displayHeight, touchMinor, touchMajor);
- mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event);
- mBiometricExecutor.runAllReady();
- event.recycle();
- if (testParams.hasAlternateTouchProvider) {
- verify(mAlternateTouchProvider).onPointerDown(eq(TEST_REQUEST_ID), eq(expectedX),
- eq(expectedY), eq(expectedMinor), eq(expectedMajor));
- } else {
- verify(mFingerprintManager).onPointerDown(eq(TEST_REQUEST_ID),
- eq(testParams.sensorProps.sensorId), eq(expectedX), eq(expectedY),
- eq(expectedMinor), eq(expectedMajor));
- }
+ verify(mFingerprintManager).onPointerDown(eq(TEST_REQUEST_ID),
+ eq(testParams.sensorProps.sensorId), eq(pointerId), eq(expectedX), eq(expectedY),
+ eq(expectedMinor), eq(expectedMajor), eq(orientation), anyLong(), anyLong(),
+ anyBoolean());
}
@Test
@@ -744,46 +686,36 @@
}
private void fingerDownParameterized(TestParams testParams) throws RemoteException {
- reset(mUdfpsView, mAlternateTouchProvider, mFingerprintManager, mLatencyTracker,
+ reset(mUdfpsView, mFingerprintManager, mLatencyTracker,
mKeyguardUpdateMonitor);
+ when(mUdfpsView.getViewRootImpl()).thenReturn(mViewRootImpl);
// Configure UdfpsView to accept the ACTION_DOWN event
when(mUdfpsView.isDisplayConfigured()).thenReturn(false);
- when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true);
when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(true);
- // GIVEN that the overlay is showing
+ final NormalizedTouchData touchData = new NormalizedTouchData(0, 0f, 0f, 0f, 0f, 0f, 0L,
+ 0L);
+ final TouchProcessorResult processorResultDown = new TouchProcessorResult.ProcessedTouch(
+ InteractionEvent.DOWN, 1 /* pointerId */, touchData);
+
+ initUdfpsController(testParams.sensorProps);
mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId,
BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
mFgExecutor.runAllReady();
- verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
// WHEN ACTION_DOWN is received
+ verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
+ when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
+ processorResultDown);
MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0);
mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent);
mBiometricExecutor.runAllReady();
downEvent.recycle();
- MotionEvent moveEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 0, 0, 0);
- mTouchListenerCaptor.getValue().onTouch(mUdfpsView, moveEvent);
- mBiometricExecutor.runAllReady();
- moveEvent.recycle();
-
- mFgExecutor.runAllReady();
-
// THEN the touch provider is notified about onPointerDown.
- if (testParams.hasAlternateTouchProvider) {
- verify(mAlternateTouchProvider).onPointerDown(eq(TEST_REQUEST_ID), eq(0), eq(0), eq(0f),
- eq(0f));
- verify(mFingerprintManager, never()).onPointerDown(anyLong(), anyInt(), anyInt(),
- anyInt(), anyFloat(), anyFloat());
- verify(mKeyguardUpdateMonitor).onUdfpsPointerDown(eq((int) TEST_REQUEST_ID));
- } else {
- verify(mFingerprintManager).onPointerDown(eq(TEST_REQUEST_ID),
- eq(testParams.sensorProps.sensorId), eq(0), eq(0), eq(0f), eq(0f));
- verify(mAlternateTouchProvider, never()).onPointerDown(anyInt(), anyInt(), anyInt(),
- anyFloat(), anyFloat());
- }
+ verify(mFingerprintManager).onPointerDown(anyLong(), anyInt(), anyInt(), anyFloat(),
+ anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyLong(), anyLong(), anyBoolean());
// AND display configuration begins
if (testParams.sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) {
@@ -800,33 +732,20 @@
// AND onDisplayConfigured notifies FingerprintManager about onUiReady
mOnDisplayConfiguredCaptor.getValue().run();
mBiometricExecutor.runAllReady();
- if (testParams.hasAlternateTouchProvider) {
- InOrder inOrder = inOrder(mAlternateTouchProvider, mLatencyTracker);
- inOrder.verify(mAlternateTouchProvider).onUiReady();
- inOrder.verify(mLatencyTracker).onActionEnd(
- eq(LatencyTracker.ACTION_UDFPS_ILLUMINATE));
- verify(mFingerprintManager, never()).onUdfpsUiEvent(
- eq(FingerprintManager.UDFPS_UI_READY), anyLong(), anyInt());
- } else {
- InOrder inOrder = inOrder(mFingerprintManager, mLatencyTracker);
- inOrder.verify(mFingerprintManager).onUdfpsUiEvent(
- eq(FingerprintManager.UDFPS_UI_READY), eq(TEST_REQUEST_ID),
- eq(testParams.sensorProps.sensorId));
- inOrder.verify(mLatencyTracker).onActionEnd(
- eq(LatencyTracker.ACTION_UDFPS_ILLUMINATE));
- verify(mAlternateTouchProvider, never()).onUiReady();
- }
+ InOrder inOrder = inOrder(mFingerprintManager, mLatencyTracker);
+ inOrder.verify(mFingerprintManager).onUdfpsUiEvent(
+ eq(FingerprintManager.UDFPS_UI_READY), eq(TEST_REQUEST_ID),
+ eq(testParams.sensorProps.sensorId));
+ inOrder.verify(mLatencyTracker).onActionEnd(
+ eq(LatencyTracker.ACTION_UDFPS_ILLUMINATE));
} else {
verify(mFingerprintManager, never()).onUdfpsUiEvent(
eq(FingerprintManager.UDFPS_UI_READY), anyLong(), anyInt());
- verify(mAlternateTouchProvider, never()).onUiReady();
verify(mLatencyTracker, never()).onActionEnd(
eq(LatencyTracker.ACTION_UDFPS_ILLUMINATE));
}
}
-
-
@Test
public void aodInterrupt() {
runWithAllParams(this::aodInterruptParameterized);
@@ -834,8 +753,9 @@
private void aodInterruptParameterized(TestParams testParams) throws RemoteException {
mUdfpsController.cancelAodSendFingerUpAction();
- reset(mUdfpsView, mAlternateTouchProvider, mFingerprintManager, mKeyguardUpdateMonitor);
+ reset(mUdfpsView, mFingerprintManager, mKeyguardUpdateMonitor);
when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(true);
+ when(mUdfpsView.getViewRootImpl()).thenReturn(mViewRootImpl);
// GIVEN that the overlay is showing and screen is on and fp is running
mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId,
@@ -855,19 +775,8 @@
}
mBiometricExecutor.runAllReady();
- if (testParams.hasAlternateTouchProvider) {
- verify(mAlternateTouchProvider).onPointerDown(eq(TEST_REQUEST_ID), eq(0), eq(0),
- eq(3f) /* minor */, eq(2f) /* major */);
- verify(mFingerprintManager, never()).onPointerDown(anyLong(), anyInt(), anyInt(),
- anyInt(), anyFloat(), anyFloat());
- verify(mKeyguardUpdateMonitor).onUdfpsPointerDown(eq((int) TEST_REQUEST_ID));
- } else {
- verify(mFingerprintManager).onPointerDown(eq(TEST_REQUEST_ID),
- eq(testParams.sensorProps.sensorId), eq(0), eq(0), eq(3f) /* minor */,
- eq(2f) /* major */);
- verify(mAlternateTouchProvider, never()).onPointerDown(anyLong(), anyInt(), anyInt(),
- anyFloat(), anyFloat());
- }
+ verify(mFingerprintManager).onPointerDown(anyLong(), anyInt(), anyInt(), anyFloat(),
+ anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyLong(), anyLong(), anyBoolean());
}
@Test
@@ -907,10 +816,12 @@
private void onFingerUp_displayConfigurationParameterized(TestParams testParams)
throws RemoteException {
reset(mUdfpsView);
+ when(mUdfpsView.getViewRootImpl()).thenReturn(mViewRootImpl);
+
+ final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult =
+ givenFingerEvent(InteractionEvent.DOWN, InteractionEvent.UP, false);
// GIVEN AOD interrupt
- mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId,
- BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
mScreenObserver.onScreenTurnedOn();
mFgExecutor.runAllReady();
mUdfpsController.onAodInterrupt(0, 0, 0f, 0f);
@@ -918,7 +829,8 @@
when(mUdfpsView.isDisplayConfigured()).thenReturn(true);
// WHEN up-action received
- verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
+ when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
+ touchProcessorResult.second);
MotionEvent upEvent = MotionEvent.obtain(0, 0, ACTION_UP, 0, 0, 0);
mTouchListenerCaptor.getValue().onTouch(mUdfpsView, upEvent);
mBiometricExecutor.runAllReady();
@@ -931,7 +843,8 @@
when(mUdfpsView.isDisplayConfigured()).thenReturn(false);
// WHEN up-action received
- verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
+ when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
+ touchProcessorResult.second);
MotionEvent upEvent = MotionEvent.obtain(0, 0, ACTION_UP, 0, 0, 0);
mTouchListenerCaptor.getValue().onTouch(mUdfpsView, upEvent);
mBiometricExecutor.runAllReady();
@@ -1015,16 +928,19 @@
private void aodInterruptCancelTimeoutActionOnFingerUpParameterized(TestParams testParams)
throws RemoteException {
reset(mUdfpsView);
- when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true);
+ when(mUdfpsView.getViewRootImpl()).thenReturn(mViewRootImpl);
// GIVEN AOD interrupt
- mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId,
- BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
mScreenObserver.onScreenTurnedOn();
mFgExecutor.runAllReady();
mUdfpsController.onAodInterrupt(0, 0, 0f, 0f);
mFgExecutor.runAllReady();
+ final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult =
+ givenFingerEvent(InteractionEvent.DOWN, InteractionEvent.UP, false);
+ mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId,
+ BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
+
if (testParams.sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) {
// Configure UdfpsView to accept the ACTION_UP event
when(mUdfpsView.isDisplayConfigured()).thenReturn(true);
@@ -1033,7 +949,8 @@
}
// WHEN ACTION_UP is received
- verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
+ when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
+ touchProcessorResult.second);
MotionEvent upEvent = MotionEvent.obtain(0, 0, ACTION_UP, 0, 0, 0);
mTouchListenerCaptor.getValue().onTouch(mUdfpsView, upEvent);
mBiometricExecutor.runAllReady();
@@ -1043,16 +960,13 @@
when(mUdfpsView.isDisplayConfigured()).thenReturn(false);
// WHEN ACTION_DOWN is received
+ when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
+ touchProcessorResult.first);
MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0);
mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent);
mBiometricExecutor.runAllReady();
downEvent.recycle();
- // WHEN ACTION_MOVE is received
- MotionEvent moveEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 0, 0, 0);
- mTouchListenerCaptor.getValue().onTouch(mUdfpsView, moveEvent);
- mBiometricExecutor.runAllReady();
- moveEvent.recycle();
mFgExecutor.runAllReady();
if (testParams.sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) {
@@ -1122,24 +1036,16 @@
@Test
public void playHapticOnTouchUdfpsArea_a11yTouchExplorationEnabled() throws RemoteException {
- // Configure UdfpsView to accept the ACTION_DOWN event
- when(mUdfpsView.isDisplayConfigured()).thenReturn(false);
- when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true);
-
- // GIVEN that the overlay is showing and a11y touch exploration enabled
- when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(true);
- mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
- BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
- mFgExecutor.runAllReady();
+ final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult =
+ givenFingerEvent(InteractionEvent.DOWN, InteractionEvent.UP, true);
// WHEN ACTION_HOVER is received
+ when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
+ touchProcessorResult.first);
verify(mUdfpsView).setOnHoverListener(mHoverListenerCaptor.capture());
MotionEvent enterEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_HOVER_ENTER, 0, 0, 0);
mHoverListenerCaptor.getValue().onHover(mUdfpsView, enterEvent);
enterEvent.recycle();
- MotionEvent moveEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_HOVER_MOVE, 0, 0, 0);
- mHoverListenerCaptor.getValue().onHover(mUdfpsView, moveEvent);
- moveEvent.recycle();
// THEN tick haptic is played
verify(mVibrator).vibrate(
@@ -1159,24 +1065,16 @@
public void playHapticOnTouchUdfpsArea_a11yTouchExplorationEnabled_oneWayHapticsEnabled()
throws RemoteException {
when(mFeatureFlags.isEnabled(ONE_WAY_HAPTICS_API_MIGRATION)).thenReturn(true);
- // Configure UdfpsView to accept the ACTION_DOWN event
- when(mUdfpsView.isDisplayConfigured()).thenReturn(false);
- when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true);
- // GIVEN that the overlay is showing and a11y touch exploration enabled
- when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(true);
- mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
- BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
- mFgExecutor.runAllReady();
+ final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult =
+ givenFingerEvent(InteractionEvent.DOWN, InteractionEvent.UP, true);
// WHEN ACTION_HOVER is received
- verify(mUdfpsView).setOnHoverListener(mHoverListenerCaptor.capture());
+ when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
+ touchProcessorResult.first);
MotionEvent enterEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_HOVER_ENTER, 0, 0, 0);
mHoverListenerCaptor.getValue().onHover(mUdfpsView, enterEvent);
enterEvent.recycle();
- MotionEvent moveEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_HOVER_MOVE, 0, 0, 0);
- mHoverListenerCaptor.getValue().onHover(mUdfpsView, moveEvent);
- moveEvent.recycle();
// THEN context click haptic is played
verify(mVibrator).performHapticFeedback(
@@ -1187,26 +1085,16 @@
@Test
public void noHapticOnTouchUdfpsArea_a11yTouchExplorationDisabled() throws RemoteException {
- // Configure UdfpsView to accept the ACTION_DOWN event
- when(mUdfpsView.isDisplayConfigured()).thenReturn(false);
- when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true);
-
- // GIVEN that the overlay is showing and a11y touch exploration NOT enabled
- when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(false);
- mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
- BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
- mFgExecutor.runAllReady();
+ final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult =
+ givenFingerEvent(InteractionEvent.DOWN, InteractionEvent.UP, false);
// WHEN ACTION_DOWN is received
- verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
+ when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
+ touchProcessorResult.first);
MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0);
mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent);
mBiometricExecutor.runAllReady();
downEvent.recycle();
- MotionEvent moveEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 0, 0, 0);
- mTouchListenerCaptor.getValue().onTouch(mUdfpsView, moveEvent);
- mBiometricExecutor.runAllReady();
- moveEvent.recycle();
// THEN NO haptic played
verify(mVibrator, never()).vibrate(
@@ -1221,80 +1109,26 @@
public void noHapticOnTouchUdfpsArea_a11yTouchExplorationDisabled__oneWayHapticsEnabled()
throws RemoteException {
when(mFeatureFlags.isEnabled(ONE_WAY_HAPTICS_API_MIGRATION)).thenReturn(true);
- // Configure UdfpsView to accept the ACTION_DOWN event
- when(mUdfpsView.isDisplayConfigured()).thenReturn(false);
- when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true);
- // GIVEN that the overlay is showing and a11y touch exploration NOT enabled
- when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(false);
- mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
- BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
- mFgExecutor.runAllReady();
+ final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult =
+ givenFingerEvent(InteractionEvent.DOWN, InteractionEvent.UP, false);
// WHEN ACTION_DOWN is received
- verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
+ when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
+ touchProcessorResult.first);
MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0);
mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent);
mBiometricExecutor.runAllReady();
downEvent.recycle();
- MotionEvent moveEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 0, 0, 0);
- mTouchListenerCaptor.getValue().onTouch(mUdfpsView, moveEvent);
- mBiometricExecutor.runAllReady();
- moveEvent.recycle();
// THEN NO haptic played
verify(mVibrator, never()).performHapticFeedback(any(), anyInt());
}
@Test
- public void onTouch_withoutNewTouchDetection_shouldCallOldFingerprintManagerPath()
- throws RemoteException {
- // Disable new touch detection.
- when(mFeatureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)).thenReturn(false);
-
- // Configure UdfpsController to use FingerprintManager as opposed to AlternateTouchProvider.
- initUdfpsController(mOpticalProps, false /* hasAlternateTouchProvider */);
-
- // Configure UdfpsView to accept the ACTION_DOWN event
- when(mUdfpsView.isDisplayConfigured()).thenReturn(false);
- when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true);
-
- // GIVEN that the overlay is showing and a11y touch exploration NOT enabled
- when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(false);
- mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
- BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
- mFgExecutor.runAllReady();
-
- verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
-
- // WHEN ACTION_DOWN is received
- MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0);
- mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent);
- mBiometricExecutor.runAllReady();
- downEvent.recycle();
-
- // AND ACTION_MOVE is received
- MotionEvent moveEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 0, 0, 0);
- mTouchListenerCaptor.getValue().onTouch(mUdfpsView, moveEvent);
- mBiometricExecutor.runAllReady();
- moveEvent.recycle();
-
- // AND ACTION_UP is received
- MotionEvent upEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_UP, 0, 0, 0);
- mTouchListenerCaptor.getValue().onTouch(mUdfpsView, upEvent);
- mBiometricExecutor.runAllReady();
- upEvent.recycle();
-
- // THEN the old FingerprintManager path is invoked.
- verify(mFingerprintManager).onPointerDown(anyLong(), anyInt(), anyInt(), anyInt(),
- anyFloat(), anyFloat());
- verify(mFingerprintManager).onPointerUp(anyLong(), anyInt());
- }
-
- @Test
public void fingerDown_falsingManagerInformed() throws RemoteException {
final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult =
- givenAcceptFingerDownEvent();
+ givenFingerEvent(InteractionEvent.DOWN, InteractionEvent.UP, false);
// WHEN ACTION_DOWN is received
when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
@@ -1308,85 +1142,46 @@
verify(mFalsingManager).isFalseTouch(UDFPS_AUTHENTICATION);
}
- @Test
- public void onTouch_withNewTouchDetection_shouldCallNewFingerprintManagerPath()
- throws RemoteException {
- final Pair<TouchProcessorResult, TouchProcessorResult> processorResultDownAndUp =
- givenAcceptFingerDownEvent();
-
- // WHEN ACTION_DOWN is received
- when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
- processorResultDownAndUp.first);
- MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0);
- mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent);
- mBiometricExecutor.runAllReady();
- downEvent.recycle();
-
- // AND ACTION_UP is received
- when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
- processorResultDownAndUp.second);
- MotionEvent upEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_UP, 0, 0, 0);
- mTouchListenerCaptor.getValue().onTouch(mUdfpsView, upEvent);
- mBiometricExecutor.runAllReady();
- upEvent.recycle();
-
- // THEN the new FingerprintManager path is invoked.
- verify(mFingerprintManager).onPointerDown(anyLong(), anyInt(), anyInt(), anyFloat(),
- anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyLong(), anyLong(), anyBoolean());
- verify(mFingerprintManager).onPointerUp(anyLong(), anyInt(), anyInt(), anyFloat(),
- anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyLong(), anyLong(), anyBoolean());
- }
-
- private Pair<TouchProcessorResult, TouchProcessorResult> givenAcceptFingerDownEvent()
+ private Pair<TouchProcessorResult, TouchProcessorResult> givenFingerEvent(
+ InteractionEvent event1, InteractionEvent event2, boolean a11y)
throws RemoteException {
final NormalizedTouchData touchData = new NormalizedTouchData(0, 0f, 0f, 0f, 0f, 0f, 0L,
0L);
final TouchProcessorResult processorResultDown = new TouchProcessorResult.ProcessedTouch(
- InteractionEvent.DOWN, 1 /* pointerId */, touchData);
+ event1, 1 /* pointerId */, touchData);
final TouchProcessorResult processorResultUp = new TouchProcessorResult.ProcessedTouch(
- InteractionEvent.UP, 1 /* pointerId */, touchData);
-
- // Enable new touch detection.
- when(mFeatureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)).thenReturn(true);
+ event2, 1 /* pointerId */, touchData);
// Configure UdfpsController to use FingerprintManager as opposed to AlternateTouchProvider.
- initUdfpsController(mOpticalProps, false /* hasAlternateTouchProvider */);
+ initUdfpsController(mOpticalProps);
// Configure UdfpsView to accept the ACTION_DOWN event
when(mUdfpsView.isDisplayConfigured()).thenReturn(false);
- when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true);
// GIVEN that the overlay is showing and a11y touch exploration NOT enabled
- when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(false);
+ when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(a11y);
mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
mFgExecutor.runAllReady();
- verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
+ if (a11y) {
+ verify(mUdfpsView).setOnHoverListener(mHoverListenerCaptor.capture());
+ } else {
+ verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
+ }
return new Pair<>(processorResultDown, processorResultUp);
}
@Test
- public void onTouch_WithNewTouchDetection_forwardToKeyguard() throws RemoteException {
+ public void onTouch_forwardToKeyguard() throws RemoteException {
final NormalizedTouchData touchData = new NormalizedTouchData(0, 0f, 0f, 0f, 0f, 0f, 0L,
0L);
final TouchProcessorResult processorResultDown = new TouchProcessorResult.ProcessedTouch(
InteractionEvent.UNCHANGED, -1 /* pointerOnSensorId */, touchData);
- // Enable new touch detection.
- when(mFeatureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)).thenReturn(true);
-
- // Configure UdfpsController to use FingerprintManager as opposed to AlternateTouchProvider.
- initUdfpsController(mOpticalProps, false /* hasAlternateTouchProvider */);
-
- // Configure UdfpsView to accept the ACTION_DOWN event
- when(mUdfpsView.isDisplayConfigured()).thenReturn(false);
- when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(false);
-
// GIVEN that the overlay is showing and a11y touch exploration NOT enabled
when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
- when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(false);
mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
mFgExecutor.runAllReady();
@@ -1402,47 +1197,23 @@
// THEN the touch is forwarded to Keyguard
verify(mStatusBarKeyguardViewManager).onTouch(downEvent);
- downEvent.recycle();
}
@Test
- public void onTouch_withNewTouchDetection_pilferPointer() throws RemoteException {
- final NormalizedTouchData touchData = new NormalizedTouchData(0, 0f, 0f, 0f, 0f, 0f, 0L,
- 0L);
- final TouchProcessorResult processorResultDown = new TouchProcessorResult.ProcessedTouch(
- InteractionEvent.DOWN, 1 /* pointerId */, touchData);
-
- // Enable new touch detection.
- when(mFeatureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)).thenReturn(true);
-
- // Configure UdfpsController to use FingerprintManager as opposed to AlternateTouchProvider.
- initUdfpsController(mOpticalProps, false /* hasAlternateTouchProvider */);
-
- // Configure UdfpsView to accept the ACTION_DOWN event
- when(mUdfpsView.isDisplayConfigured()).thenReturn(false);
- when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true);
-
- // GIVEN that the overlay is showing and a11y touch exploration NOT enabled
- when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(false);
- mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
- BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
- mFgExecutor.runAllReady();
-
- verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
+ public void onTouch_pilferPointer() throws RemoteException {
+ final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult =
+ givenFingerEvent(InteractionEvent.DOWN, InteractionEvent.UNCHANGED, false);
// WHEN ACTION_DOWN is received
when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
- processorResultDown);
+ touchProcessorResult.first);
MotionEvent event = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0);
mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event);
mBiometricExecutor.runAllReady();
// WHEN ACTION_MOVE is received after
- final TouchProcessorResult processorResultUnchanged =
- new TouchProcessorResult.ProcessedTouch(
- InteractionEvent.UNCHANGED, 1 /* pointerId */, touchData);
when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
- processorResultUnchanged);
+ touchProcessorResult.second);
event.setAction(ACTION_MOVE);
mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event);
mBiometricExecutor.runAllReady();
@@ -1453,25 +1224,13 @@
}
@Test
- public void onTouch_withNewTouchDetection_doNotPilferPointer() throws RemoteException {
+ public void onTouch_doNotPilferPointer() throws RemoteException {
final NormalizedTouchData touchData = new NormalizedTouchData(0, 0f, 0f, 0f, 0f, 0f, 0L,
0L);
final TouchProcessorResult processorResultUnchanged =
new TouchProcessorResult.ProcessedTouch(InteractionEvent.UNCHANGED,
- 1 /* pointerId */, touchData);
+ -1 /* pointerId */, touchData);
- // Enable new touch detection.
- when(mFeatureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)).thenReturn(true);
-
- // Configure UdfpsController to use FingerprintManager as opposed to AlternateTouchProvider.
- initUdfpsController(mOpticalProps, false /* hasAlternateTouchProvider */);
-
- // Configure UdfpsView to not accept the ACTION_DOWN event
- when(mUdfpsView.isDisplayConfigured()).thenReturn(false);
- when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(false);
-
- // GIVEN that the overlay is showing and a11y touch exploration NOT enabled
- when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(false);
mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
mFgExecutor.runAllReady();
@@ -1491,36 +1250,17 @@
}
@Test
- public void onTouch_withNewTouchDetection_pilferPointerWhenAltBouncerShowing()
+ public void onTouch_pilferPointerWhenAltBouncerShowing()
throws RemoteException {
- final NormalizedTouchData touchData = new NormalizedTouchData(0, 0f, 0f, 0f, 0f, 0f, 0L,
- 0L);
- final TouchProcessorResult processorResultUnchanged =
- new TouchProcessorResult.ProcessedTouch(InteractionEvent.UNCHANGED,
- 1 /* pointerId */, touchData);
+ final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult =
+ givenFingerEvent(InteractionEvent.UNCHANGED, InteractionEvent.UP, false);
- // Enable new touch detection.
- when(mFeatureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)).thenReturn(true);
-
- // Configure UdfpsController to use FingerprintManager as opposed to AlternateTouchProvider.
- initUdfpsController(mOpticalProps, false /* hasAlternateTouchProvider */);
-
- // Configure UdfpsView to not accept the ACTION_DOWN event
- when(mUdfpsView.isDisplayConfigured()).thenReturn(false);
- when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(false);
-
- // GIVEN that the alternate bouncer is showing and a11y touch exploration NOT enabled
- when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(false);
+ // WHEN alternate bouncer is showing
when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
- mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
- BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
- mFgExecutor.runAllReady();
-
- verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
// WHEN ACTION_DOWN is received and touch is not within sensor
when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
- processorResultUnchanged);
+ touchProcessorResult.first);
MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0);
mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent);
mBiometricExecutor.runAllReady();
@@ -1531,32 +1271,10 @@
}
@Test
- public void onTouch_withNewTouchDetection_doNotProcessTouchWhenPullingUpBouncer()
+ public void onTouch_doNotProcessTouchWhenPullingUpBouncer()
throws RemoteException {
- final NormalizedTouchData touchData = new NormalizedTouchData(0, 0f, 0f, 0f, 0f, 0f, 0L,
- 0L);
- final TouchProcessorResult processorResultMove =
- new TouchProcessorResult.ProcessedTouch(InteractionEvent.DOWN,
- 1 /* pointerId */, touchData);
-
- // Enable new touch detection.
- when(mFeatureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)).thenReturn(true);
-
- // Configure UdfpsController to use FingerprintManager as opposed to AlternateTouchProvider.
- initUdfpsController(mOpticalProps, false /* hasAlternateTouchProvider */);
-
- // Configure UdfpsView to accept the ACTION_MOVE event
- when(mUdfpsView.isDisplayConfigured()).thenReturn(false);
- when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true);
-
- // GIVEN that the alternate bouncer is not showing and a11y touch exploration NOT enabled
- when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(false);
- when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(false);
- mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
- BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
- mFgExecutor.runAllReady();
-
- verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
+ final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult =
+ givenFingerEvent(InteractionEvent.UNCHANGED, InteractionEvent.UP, false);
// GIVEN a swipe up to bring up primary bouncer is in progress or swipe down for QS
when(mPrimaryBouncerInteractor.isInTransit()).thenReturn(true);
@@ -1564,7 +1282,7 @@
// WHEN ACTION_MOVE is received and touch is within sensor
when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
- processorResultMove);
+ touchProcessorResult.first);
MotionEvent moveEvent = MotionEvent.obtain(0, 0, ACTION_MOVE, 0, 0, 0);
mTouchListenerCaptor.getValue().onTouch(mUdfpsView, moveEvent);
mBiometricExecutor.runAllReady();
@@ -1578,40 +1296,19 @@
@Test
- public void onTouch_withNewTouchDetection_qsDrag_processesTouchWhenAlternateBouncerVisible()
+ public void onTouch_qsDrag_processesTouchWhenAlternateBouncerVisible()
throws RemoteException {
- final NormalizedTouchData touchData = new NormalizedTouchData(0, 0f, 0f, 0f, 0f, 0f, 0L,
- 0L);
- final TouchProcessorResult processorResultMove =
- new TouchProcessorResult.ProcessedTouch(InteractionEvent.DOWN,
- 1 /* pointerId */, touchData);
+ final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult =
+ givenFingerEvent(InteractionEvent.DOWN, InteractionEvent.UP, false);
- // Enable new touch detection.
- when(mFeatureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)).thenReturn(true);
-
- // Configure UdfpsController to use FingerprintManager as opposed to AlternateTouchProvider.
- initUdfpsController(mOpticalProps, false /* hasAlternateTouchProvider */);
-
- // Configure UdfpsView to accept the ACTION_MOVE event
- when(mUdfpsView.isDisplayConfigured()).thenReturn(false);
- when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true);
-
- // GIVEN that the alternate bouncer is showing and a11y touch exploration NOT enabled
- when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(false);
when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
- mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
- BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
- mFgExecutor.runAllReady();
-
- verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
-
// GIVEN swipe down for QS
when(mPrimaryBouncerInteractor.isInTransit()).thenReturn(false);
when(mLockscreenShadeTransitionController.getQSDragProgress()).thenReturn(1f);
// WHEN ACTION_MOVE is received and touch is within sensor
when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
- processorResultMove);
+ touchProcessorResult.first);
MotionEvent moveEvent = MotionEvent.obtain(0, 0, ACTION_MOVE, 0, 0, 0);
mTouchListenerCaptor.getValue().onTouch(mUdfpsView, moveEvent);
mBiometricExecutor.runAllReady();
@@ -1689,43 +1386,4 @@
// THEN vibrate is used
verify(mVibrator).performHapticFeedback(any(), eq(UdfpsController.LONG_PRESS));
}
-
- @Test
- public void aodInterrupt_withNewTouchDetection() throws RemoteException {
- mUdfpsController.cancelAodSendFingerUpAction();
- final NormalizedTouchData touchData = new NormalizedTouchData(0, 0f, 0f, 0f, 0f, 0f, 0L,
- 0L);
- final TouchProcessorResult processorResultDown =
- new TouchProcessorResult.ProcessedTouch(InteractionEvent.DOWN,
- 1 /* pointerId */, touchData);
-
- // Enable new touch detection.
- when(mFeatureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)).thenReturn(true);
-
- // Configure UdfpsController to use FingerprintManager as opposed to AlternateTouchProvider.
- initUdfpsController(mOpticalProps, false /* hasAlternateTouchProvider */);
-
- // GIVEN that the overlay is showing and screen is on and fp is running
- mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, 0,
- BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
- mScreenObserver.onScreenTurnedOn();
- mFgExecutor.runAllReady();
-
- // WHEN fingerprint is requested because of AOD interrupt
- mUdfpsController.onAodInterrupt(0, 0, 2f, 3f);
-
- // Check case where touch driver sends touch to UdfpsView as well
- verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
- when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true);
- when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
- processorResultDown);
- MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0);
- mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent);
-
- mBiometricExecutor.runAllReady();
-
- // THEN only one onPointerDown is sent
- verify(mFingerprintManager).onPointerDown(anyLong(), anyInt(), anyInt(), anyFloat(),
- anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyLong(), anyLong(), anyBoolean());
- }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerBaseTest.java
index 3276e66..e512adc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerBaseTest.java
@@ -30,7 +30,6 @@
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FakeFeatureFlags;
-import com.android.systemui.flags.Flags;
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shade.ShadeExpansionChangeEvent;
@@ -116,7 +115,7 @@
}
public UdfpsKeyguardViewControllerLegacy createUdfpsKeyguardViewController() {
- return createUdfpsKeyguardViewController(false, false);
+ return createUdfpsKeyguardViewController(false);
}
public void captureKeyGuardViewManagerCallback() {
@@ -126,8 +125,7 @@
}
protected UdfpsKeyguardViewControllerLegacy createUdfpsKeyguardViewController(
- boolean useModernBouncer, boolean useExpandedOverlay) {
- mFeatureFlags.set(Flags.UDFPS_NEW_TOUCH_DETECTION, useExpandedOverlay);
+ boolean useModernBouncer) {
UdfpsKeyguardViewControllerLegacy controller = new UdfpsKeyguardViewControllerLegacy(
mView,
mStatusBarStateController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerTest.java
index b018a3e..21928cd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerTest.java
@@ -18,15 +18,12 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.atLeast;
-import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.testing.TestableLooper;
-import android.view.MotionEvent;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
@@ -44,8 +41,7 @@
UdfpsKeyguardViewLegacyControllerBaseTest {
@Override
public UdfpsKeyguardViewControllerLegacy createUdfpsKeyguardViewController() {
- return createUdfpsKeyguardViewController(/* useModernBouncer */ false,
- /* useExpandedOverlay */ false);
+ return createUdfpsKeyguardViewController(/* useModernBouncer */ false);
}
@Test
@@ -216,37 +212,4 @@
sendStatusBarStateChanged(StatusBarState.SHADE_LOCKED);
assertTrue(mController.shouldPauseAuth());
}
-
- @Test
- // TODO(b/259264861): Tracking Bug
- public void testUdfpsExpandedOverlayOn() {
- // GIVEN view is attached and useExpandedOverlay is true
- mController = createUdfpsKeyguardViewController(false, true);
- mController.onViewAttached();
- captureKeyGuardViewManagerCallback();
-
- // WHEN a touch is received
- mKeyguardViewManagerCallback.onTouch(
- MotionEvent.obtain(0, 0, 0, 0, 0, 0));
-
- // THEN udfpsController onTouch is not called
- assertTrue(mView.mUseExpandedOverlay);
- verify(mUdfpsController, never()).onTouch(any());
- }
-
- @Test
- // TODO(b/259264861): Tracking Bug
- public void testUdfpsExpandedOverlayOff() {
- // GIVEN view is attached and useExpandedOverlay is false
- mController.onViewAttached();
- captureKeyGuardViewManagerCallback();
-
- // WHEN a touch is received
- mKeyguardViewManagerCallback.onTouch(
- MotionEvent.obtain(0, 0, 0, 0, 0, 0));
-
- // THEN udfpsController onTouch is called
- assertFalse(mView.mUseExpandedOverlay);
- verify(mUdfpsController).onTouch(any());
- }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt
index da4548b..02ee53879 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt
@@ -106,10 +106,7 @@
mock(SystemClock::class.java),
mKeyguardUpdateMonitor,
)
- return createUdfpsKeyguardViewController(
- /* useModernBouncer */ true, /* useExpandedOverlay */
- false
- )
+ return createUdfpsKeyguardViewController(/* useModernBouncer */ true)
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsViewTest.kt
index 0c8e7a5..9fbe096 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsViewTest.kt
@@ -16,8 +16,6 @@
package com.android.systemui.biometrics
-import android.graphics.PointF
-import android.graphics.RectF
import android.hardware.biometrics.SensorLocationInternal
import android.testing.TestableLooper
import android.testing.ViewUtils
@@ -42,7 +40,6 @@
import org.mockito.Mockito.nullable
import org.mockito.Mockito.verify
import org.mockito.junit.MockitoJUnit
-import org.mockito.Mockito.`when` as whenever
private const val SENSOR_X = 50
private const val SENSOR_Y = 250
@@ -80,56 +77,7 @@
ViewUtils.detachView(view)
}
- @Test
- fun layoutSizeFitsSensor() {
- val params = withArgCaptor<RectF> {
- verify(animationViewController).onSensorRectUpdated(capture())
- }
- assertThat(params.width()).isAtLeast(2f * SENSOR_RADIUS)
- assertThat(params.height()).isAtLeast(2f * SENSOR_RADIUS)
- }
-
- @Test
- fun isWithinSensorAreaAndPaused() = isWithinSensorArea(paused = true)
-
- @Test
- fun isWithinSensorAreaAndNotPaused() = isWithinSensorArea(paused = false)
-
- private fun isWithinSensorArea(paused: Boolean) {
- whenever(animationViewController.shouldPauseAuth()).thenReturn(paused)
- whenever(animationViewController.touchTranslation).thenReturn(PointF(0f, 0f))
- val end = (SENSOR_RADIUS * 2) - 1
- for (x in 1 until end) {
- for (y in 1 until end) {
- assertThat(view.isWithinSensorArea(x.toFloat(), y.toFloat())).isEqualTo(!paused)
- }
- }
- }
-
- @Test
- fun isWithinSensorAreaWhenTranslated() {
- val offset = PointF(100f, 200f)
- whenever(animationViewController.touchTranslation).thenReturn(offset)
- val end = (SENSOR_RADIUS * 2) - 1
- for (x in 0 until offset.x.toInt() step 2) {
- for (y in 0 until offset.y.toInt() step 2) {
- assertThat(view.isWithinSensorArea(x.toFloat(), y.toFloat())).isFalse()
- }
- }
- for (x in offset.x.toInt() + 1 until offset.x.toInt() + end) {
- for (y in offset.y.toInt() + 1 until offset.y.toInt() + end) {
- assertThat(view.isWithinSensorArea(x.toFloat(), y.toFloat())).isTrue()
- }
- }
- }
-
- @Test
- fun isNotWithinSensorArea() {
- whenever(animationViewController.touchTranslation).thenReturn(PointF(0f, 0f))
- assertThat(view.isWithinSensorArea(SENSOR_RADIUS * 2.5f, SENSOR_RADIUS.toFloat()))
- .isFalse()
- assertThat(view.isWithinSensorArea(SENSOR_RADIUS.toFloat(), SENSOR_RADIUS * 2.5f)).isFalse()
- }
+ // TODO: Add test to verify view is size of screen
@Test
fun startAndStopIllumination() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/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/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 3df9cbb..91409a3 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
@@ -11,11 +11,14 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.communal.data.model.CommunalWidgetMetadata
+import com.android.systemui.communal.shared.CommunalContentSize
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
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.util.mockito.any
import com.android.systemui.util.mockito.kotlinArgumentCaptor
@@ -59,9 +62,12 @@
@Mock private lateinit var stopwatchProviderInfo: AppWidgetProviderInfo
+ private lateinit var communalRepository: FakeCommunalRepository
+
private lateinit var logBuffer: LogBuffer
private val testDispatcher = StandardTestDispatcher()
+
private val testScope = TestScope(testDispatcher)
@Before
@@ -71,6 +77,14 @@
logBuffer = FakeLogBuffer.Factory.create()
featureFlagEnabled(true)
+ communalRepository = FakeCommunalRepository()
+ communalRepository.setIsCommunalEnabled(true)
+
+ overrideResource(
+ R.array.config_communalWidgetAllowlist,
+ arrayOf(componentName1, componentName2)
+ )
+
whenever(stopwatchProviderInfo.loadLabel(any())).thenReturn("Stopwatch")
whenever(userTracker.userHandle).thenReturn(userHandle)
}
@@ -219,11 +233,36 @@
Mockito.verify(appWidgetHost).stopListening()
}
+ @Test
+ fun getCommunalWidgetAllowList_onInit() {
+ testScope.runTest {
+ val repository = initCommunalWidgetRepository()
+ val communalWidgetAllowlist = repository.communalWidgetAllowlist
+ assertThat(
+ listOf(
+ CommunalWidgetMetadata(
+ componentName = componentName1,
+ priority = 2,
+ sizes = listOf(CommunalContentSize.HALF)
+ ),
+ CommunalWidgetMetadata(
+ componentName = componentName2,
+ priority = 1,
+ sizes = listOf(CommunalContentSize.HALF)
+ )
+ )
+ )
+ .containsExactly(*communalWidgetAllowlist.toTypedArray())
+ }
+ }
+
private fun initCommunalWidgetRepository(): CommunalWidgetRepositoryImpl {
return CommunalWidgetRepositoryImpl(
+ context,
appWidgetManager,
appWidgetHost,
broadcastDispatcher,
+ communalRepository,
packageManager,
userManager,
userTracker,
@@ -282,4 +321,9 @@
private fun installedProviders(providers: List<AppWidgetProviderInfo>) {
whenever(appWidgetManager.installedProviders).thenReturn(providers)
}
+
+ companion object {
+ const val componentName1 = "component name 1"
+ const val componentName2 = "component name 2"
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorTest.kt
new file mode 100644
index 0000000..0a9a15e
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorTest.kt
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.domain.interactor
+
+import android.provider.Settings.Secure.HUB_MODE_TUTORIAL_COMPLETED
+import android.provider.Settings.Secure.HUB_MODE_TUTORIAL_NOT_STARTED
+import android.provider.Settings.Secure.HUB_MODE_TUTORIAL_STARTED
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.communal.data.repository.FakeCommunalTutorialRepository
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
+import com.android.systemui.settings.UserTracker
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(JUnit4::class)
+class CommunalTutorialInteractorTest : SysuiTestCase() {
+
+ @Mock private lateinit var userTracker: UserTracker
+
+ private val testDispatcher = StandardTestDispatcher()
+ private val testScope = TestScope(testDispatcher)
+
+ private lateinit var underTest: CommunalTutorialInteractor
+ private lateinit var keyguardRepository: FakeKeyguardRepository
+ private lateinit var keyguardInteractor: KeyguardInteractor
+ private lateinit var communalTutorialRepository: FakeCommunalTutorialRepository
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ val withDeps = KeyguardInteractorFactory.create()
+ keyguardInteractor = withDeps.keyguardInteractor
+ keyguardRepository = withDeps.repository
+ communalTutorialRepository = FakeCommunalTutorialRepository()
+
+ underTest =
+ CommunalTutorialInteractor(
+ keyguardInteractor = keyguardInteractor,
+ communalTutorialRepository = communalTutorialRepository,
+ )
+
+ whenever(userTracker.userHandle).thenReturn(mock())
+ }
+
+ @Test
+ fun tutorialUnavailable_whenKeyguardNotVisible() =
+ testScope.runTest {
+ val isTutorialAvailable by collectLastValue(underTest.isTutorialAvailable)
+ communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_NOT_STARTED)
+ keyguardRepository.setKeyguardShowing(false)
+ assertThat(isTutorialAvailable).isFalse()
+ }
+
+ @Test
+ fun tutorialUnavailable_whenTutorialIsCompleted() =
+ testScope.runTest {
+ val isTutorialAvailable by collectLastValue(underTest.isTutorialAvailable)
+ keyguardRepository.setKeyguardShowing(true)
+ keyguardRepository.setKeyguardOccluded(false)
+ communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
+ assertThat(isTutorialAvailable).isFalse()
+ }
+
+ @Test
+ fun tutorialAvailable_whenTutorialNotStarted() =
+ testScope.runTest {
+ val isTutorialAvailable by collectLastValue(underTest.isTutorialAvailable)
+ keyguardRepository.setKeyguardShowing(true)
+ keyguardRepository.setKeyguardOccluded(false)
+ communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_NOT_STARTED)
+ assertThat(isTutorialAvailable).isTrue()
+ }
+
+ @Test
+ fun tutorialAvailable_whenTutorialIsStarted() =
+ testScope.runTest {
+ val isTutorialAvailable by collectLastValue(underTest.isTutorialAvailable)
+ keyguardRepository.setKeyguardShowing(true)
+ keyguardRepository.setKeyguardOccluded(false)
+ communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_STARTED)
+ assertThat(isTutorialAvailable).isTrue()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprintTest.kt b/packages/SystemUI/tests/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprintTest.kt
index 41a8be9..33a6667 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprintTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprintTest.kt
@@ -6,6 +6,7 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.communal.ui.view.layout.sections.DefaultCommunalHubSection
import com.android.systemui.communal.ui.view.layout.sections.DefaultCommunalWidgetSection
import org.junit.Before
import org.junit.Test
@@ -18,6 +19,7 @@
@TestableLooper.RunWithLooper(setAsMainLooper = true)
@SmallTest
class DefaultCommunalBlueprintTest : SysuiTestCase() {
+ @Mock private lateinit var hubSection: DefaultCommunalHubSection
@Mock private lateinit var widgetSection: DefaultCommunalWidgetSection
private lateinit var blueprint: DefaultCommunalBlueprint
@@ -25,13 +27,14 @@
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
- blueprint = DefaultCommunalBlueprint(widgetSection)
+ blueprint = DefaultCommunalBlueprint(hubSection, widgetSection)
}
@Test
fun addView() {
val constraintLayout = ConstraintLayout(context, null)
blueprint.replaceViews(null, constraintLayout)
+ verify(hubSection).addViews(constraintLayout)
verify(widgetSection).addViews(constraintLayout)
}
@@ -39,6 +42,7 @@
fun applyConstraints() {
val cs = ConstraintSet()
blueprint.applyConstraints(cs)
+ verify(hubSection).applyConstraints(cs)
verify(widgetSection).applyConstraints(cs)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/demomode/DemoModeControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/demomode/DemoModeControllerTest.kt
index 68ea7eb..6c2e136 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/demomode/DemoModeControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/demomode/DemoModeControllerTest.kt
@@ -25,7 +25,7 @@
import com.android.systemui.demomode.DemoMode.ACTION_DEMO
import com.android.systemui.demomode.DemoMode.COMMAND_STATUS
import com.android.systemui.dump.DumpManager
-import com.android.systemui.util.settings.FakeSettings
+import com.android.systemui.util.settings.FakeGlobalSettings
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.launch
@@ -48,7 +48,7 @@
@Mock private lateinit var dumpManager: DumpManager
- private val globalSettings = FakeSettings()
+ private val globalSettings = FakeGlobalSettings()
private val testDispatcher = UnconfinedTestDispatcher()
private val testScope = TestScope(testDispatcher)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FakeFeatureFlagsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/FakeFeatureFlagsTest.kt
index 37c70d8..2bd2bff 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/FakeFeatureFlagsTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FakeFeatureFlagsTest.kt
@@ -43,10 +43,10 @@
fun accessingUnspecifiedFlags_throwsException() {
val flags: FeatureFlags = FakeFeatureFlags()
try {
- assertThat(flags.isEnabled(Flags.TEAMFOOD)).isFalse()
+ assertThat(flags.isEnabled(Flags.NULL_FLAG)).isFalse()
fail("Expected an exception when accessing an unspecified flag.")
} catch (ex: IllegalStateException) {
- assertThat(ex.message).contains("UNKNOWN(teamfood)")
+ assertThat(ex.message).contains("UNKNOWN(null_flag)")
}
try {
assertThat(flags.isEnabled(unreleasedFlag)).isFalse()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsClassicDebugTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsClassicDebugTest.kt
index 14c5ec0..f51745b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsClassicDebugTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsClassicDebugTest.kt
@@ -22,6 +22,7 @@
import android.content.res.Resources
import android.content.res.Resources.NotFoundException
import android.test.suitebuilder.annotation.SmallTest
+import com.android.systemui.FakeFeatureFlagsImpl
import com.android.systemui.SysuiTestCase
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.eq
@@ -36,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
@@ -66,6 +66,7 @@
private lateinit var broadcastReceiver: BroadcastReceiver
private lateinit var clearCacheAction: Consumer<String>
private val serverFlagReader = ServerFlagReaderFake()
+ private val fakeGantryFlags = FakeFeatureFlagsImpl()
private val teamfoodableFlagA = UnreleasedFlag(name = "a", namespace = "test", teamfood = true)
private val teamfoodableFlagB = ReleasedFlag(name = "b", namespace = "test", teamfood = true)
@@ -73,7 +74,7 @@
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
- flagMap.put(Flags.TEAMFOOD.name, Flags.TEAMFOOD)
+ fakeGantryFlags.setFlag("com.android.systemui.sysui_teamfood", false)
flagMap.put(teamfoodableFlagA.name, teamfoodableFlagA)
flagMap.put(teamfoodableFlagB.name, teamfoodableFlagB)
mFeatureFlagsClassicDebug =
@@ -85,6 +86,7 @@
resources,
serverFlagReader,
flagMap,
+ fakeGantryFlags,
restarter
)
mFeatureFlagsClassicDebug.init()
@@ -122,8 +124,6 @@
@Test
fun teamFoodFlag_False() {
- whenever(flagManager.readFlagValue<Boolean>(eq(Flags.TEAMFOOD.name), any()))
- .thenReturn(false)
assertThat(mFeatureFlagsClassicDebug.isEnabled(teamfoodableFlagA)).isFalse()
assertThat(mFeatureFlagsClassicDebug.isEnabled(teamfoodableFlagB)).isTrue()
@@ -134,8 +134,7 @@
@Test
fun teamFoodFlag_True() {
- whenever(flagManager.readFlagValue<Boolean>(eq(Flags.TEAMFOOD.name), any()))
- .thenReturn(true)
+ fakeGantryFlags.setFlag("com.android.systemui.sysui_teamfood", true)
assertThat(mFeatureFlagsClassicDebug.isEnabled(teamfoodableFlagA)).isTrue()
assertThat(mFeatureFlagsClassicDebug.isEnabled(teamfoodableFlagB)).isTrue()
@@ -150,8 +149,7 @@
.thenReturn(true)
whenever(flagManager.readFlagValue<Boolean>(eq(teamfoodableFlagB.name), any()))
.thenReturn(false)
- whenever(flagManager.readFlagValue<Boolean>(eq(Flags.TEAMFOOD.name), any()))
- .thenReturn(true)
+ fakeGantryFlags.setFlag("com.android.systemui.sysui_teamfood", true)
assertThat(mFeatureFlagsClassicDebug.isEnabled(teamfoodableFlagA)).isTrue()
assertThat(mFeatureFlagsClassicDebug.isEnabled(teamfoodableFlagB)).isFalse()
@@ -479,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/flags/SetFlagsRuleExtensions.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/SetFlagsRuleExtensions.kt
new file mode 100644
index 0000000..bb6786a
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/SetFlagsRuleExtensions.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.flags
+
+import android.platform.test.flag.junit.SetFlagsRule
+
+fun SetFlagsRule.setFlagDefault(flagName: String) {
+ if (getFlagDefault(flagName)) {
+ enableFlags(flagName)
+ } else {
+ disableFlags(flagName)
+ }
+}
+
+// NOTE: This code uses reflection to gain access to private members of aconfig generated
+// classes (in the same way SetFlagsRule does internally) because this is the only way to get
+// at the underlying information and read the current value of the flag.
+// If aconfig had flag constants with accessible default values, this would be unnecessary.
+private fun getFlagDefault(name: String): Boolean {
+ val flagPackage = name.substringBeforeLast(".")
+ val featureFlagsImplClass = Class.forName("$flagPackage.FeatureFlagsImpl")
+ val featureFlagsImpl = featureFlagsImplClass.getConstructor().newInstance()
+ val flagMethodName = name.substringAfterLast(".").snakeToCamelCase()
+ val flagGetter = featureFlagsImplClass.getDeclaredMethod(flagMethodName)
+ return flagGetter.invoke(featureFlagsImpl) as Boolean
+}
+
+private fun String.snakeToCamelCase(): String {
+ val pattern = "_[a-z]".toRegex()
+ return replace(pattern) { it.value.last().uppercase() }
+}
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/haptics/slider/SliderHapticFeedbackProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/haptics/slider/SliderHapticFeedbackProviderTest.kt
index 0ee348e..7750d25 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/haptics/slider/SliderHapticFeedbackProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/haptics/slider/SliderHapticFeedbackProviderTest.kt
@@ -28,6 +28,8 @@
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.time.FakeSystemClock
+import kotlin.math.max
+import kotlin.test.assertEquals
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -149,26 +151,52 @@
}
@Test
- fun playHapticAtProgress_afterNextDragThreshold_playsLowTicksTwice() {
- // GIVEN max velocity and slider progress
- val progress = 1f
- val expectedScale = scaleAtProgressChange(config.maxVelocityToScale.toFloat(), progress)
- val ticks = VibrationEffect.startComposition()
- repeat(config.numberOfLowTicks) {
- ticks.addPrimitive(VibrationEffect.Composition.PRIMITIVE_LOW_TICK, expectedScale)
- }
+ fun playHapticAtProgress_beforeNextDragThreshold_playsLowTicksOnce() {
+ // GIVEN max velocity and a slider progress at half progress
+ val firstProgress = 0.5f
+ val firstTicks = generateTicksComposition(config.maxVelocityToScale, firstProgress)
+
+ // Given a second slider progress event smaller than the progress threshold
+ val secondProgress = firstProgress + max(0f, config.deltaProgressForDragThreshold - 0.01f)
// GIVEN system running for 1s
clock.advanceTime(1000)
- // WHEN two calls to play occur with the required threshold separation
- sliderHapticFeedbackProvider.onProgress(progress)
+ // WHEN two calls to play occur with the required threshold separation (time and progress)
+ sliderHapticFeedbackProvider.onProgress(firstProgress)
clock.advanceTime(dragTextureThresholdMillis.toLong())
- sliderHapticFeedbackProvider.onProgress(progress)
+ sliderHapticFeedbackProvider.onProgress(secondProgress)
- // THEN the correct composition plays two times
- verify(vibratorHelper, times(2))
- .vibrate(eq(ticks.compose()), any(VibrationAttributes::class.java))
+ // THEN Only the first compositions plays
+ verify(vibratorHelper, times(1))
+ .vibrate(eq(firstTicks), any(VibrationAttributes::class.java))
+ verify(vibratorHelper, times(1))
+ .vibrate(any(VibrationEffect::class.java), any(VibrationAttributes::class.java))
+ }
+
+ @Test
+ fun playHapticAtProgress_afterNextDragThreshold_playsLowTicksTwice() {
+ // GIVEN max velocity and a slider progress at half progress
+ val firstProgress = 0.5f
+ val firstTicks = generateTicksComposition(config.maxVelocityToScale, firstProgress)
+
+ // Given a second slider progress event beyond progress threshold
+ val secondProgress = firstProgress + config.deltaProgressForDragThreshold + 0.01f
+ val secondTicks = generateTicksComposition(config.maxVelocityToScale, secondProgress)
+
+ // GIVEN system running for 1s
+ clock.advanceTime(1000)
+
+ // WHEN two calls to play occur with the required threshold separation (time and progress)
+ sliderHapticFeedbackProvider.onProgress(firstProgress)
+ clock.advanceTime(dragTextureThresholdMillis.toLong())
+ sliderHapticFeedbackProvider.onProgress(secondProgress)
+
+ // THEN the correct compositions play
+ verify(vibratorHelper, times(1))
+ .vibrate(eq(firstTicks), any(VibrationAttributes::class.java))
+ verify(vibratorHelper, times(1))
+ .vibrate(eq(secondTicks), any(VibrationAttributes::class.java))
}
@Test
@@ -229,6 +257,38 @@
.vibrate(eq(bookendVibration), any(VibrationAttributes::class.java))
}
+ fun dragTextureLastProgress_afterDragTextureHaptics_keepsLastDragTextureProgress() {
+ // GIVEN max velocity and a slider progress at half progress
+ val progress = 0.5f
+
+ // GIVEN system running for 1s
+ clock.advanceTime(1000)
+
+ // WHEN a drag texture plays
+ sliderHapticFeedbackProvider.onProgress(progress)
+
+ // THEN the dragTextureLastProgress remembers the latest progress
+ assertEquals(progress, sliderHapticFeedbackProvider.dragTextureLastProgress)
+ }
+
+ @Test
+ fun dragTextureLastProgress_afterDragTextureHaptics_resetsOnHandleReleased() {
+ // GIVEN max velocity and a slider progress at half progress
+ val progress = 0.5f
+
+ // GIVEN system running for 1s
+ clock.advanceTime(1000)
+
+ // WHEN a drag texture plays
+ sliderHapticFeedbackProvider.onProgress(progress)
+
+ // WHEN the handle is released
+ sliderHapticFeedbackProvider.onHandleReleasedFromTouch()
+
+ // THEN the dragTextureLastProgress tracker is reset
+ assertEquals(-1f, sliderHapticFeedbackProvider.dragTextureLastProgress)
+ }
+
private fun scaleAtBookends(velocity: Float): Float {
val range = config.upperBookendScale - config.lowerBookendScale
val interpolatedVelocity =
@@ -244,4 +304,15 @@
val bump = interpolatedVelocity * config.additionalVelocityMaxBump
return interpolatedProgress * range + config.progressBasedDragMinScale + bump
}
+
+ private fun generateTicksComposition(velocity: Float, progress: Float): VibrationEffect {
+ val ticks = VibrationEffect.startComposition()
+ repeat(config.numberOfLowTicks) {
+ ticks.addPrimitive(
+ VibrationEffect.Composition.PRIMITIVE_LOW_TICK,
+ scaleAtProgressChange(velocity, progress)
+ )
+ }
+ return ticks.compose()
+ }
}
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/data/quickaffordance/DoNotDisturbQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfigTest.kt
index faf9751..977f1db 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfigTest.kt
@@ -25,13 +25,13 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.settingslib.notification.EnableZenModeDialog
-import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.Expandable
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.keyguard.shared.quickaffordance.ActivationState
+import com.android.systemui.res.R
import com.android.systemui.settings.UserTracker
import com.android.systemui.statusbar.policy.ZenModeController
import com.android.systemui.util.mockito.argumentCaptor
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncerTest.kt
index 1e80fb6..26fcb23 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncerTest.kt
@@ -22,8 +22,8 @@
import android.provider.Settings
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
+import com.android.systemui.res.R
import com.android.systemui.settings.FakeUserTracker
import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots
import com.android.systemui.util.FakeSharedPreferences
@@ -70,8 +70,7 @@
val resources: Resources = mock()
whenever(resources.getStringArray(R.array.config_keyguardQuickAffordanceDefaults))
.thenReturn(emptyArray())
- whenever(resources.getBoolean(R.bool.custom_lockscreen_shortcuts_enabled))
- .thenReturn(true)
+ whenever(resources.getBoolean(R.bool.custom_lockscreen_shortcuts_enabled)).thenReturn(true)
whenever(context.resources).thenReturn(resources)
testDispatcher = UnconfinedTestDispatcher()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt
index d8cdf29..90fd652 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
@@ -147,7 +147,7 @@
emptyMap()
)
verify(lockPatternUtils).registerStrongAuthTracker(strongAuthTracker.capture())
- verify(authController, atLeastOnce()).addCallback(authControllerCallback.capture())
+ verify(authController, times(2)).addCallback(authControllerCallback.capture())
}
@Test
@@ -314,18 +314,18 @@
fun faceEnrollmentChangeIsPropagatedForTheCurrentUser() =
testScope.runTest {
createBiometricSettingsRepository()
+ val faceAuthAllowed = collectLastValue(underTest.isFaceAuthEnrolledAndEnabled)
+
faceAuthIsEnabledByBiometricManager()
doNotDisableKeyguardAuthFeatures(PRIMARY_USER_ID)
runCurrent()
- clearInvocations(authController)
- whenever(authController.isFaceAuthEnrolled(PRIMARY_USER_ID)).thenReturn(false)
- val faceAuthAllowed = collectLastValue(underTest.isFaceAuthEnrolledAndEnabled)
+ enrollmentChange(FACE, PRIMARY_USER_ID, false)
assertThat(faceAuthAllowed()).isFalse()
- verify(authController).addCallback(authControllerCallback.capture())
+
enrollmentChange(REAR_FINGERPRINT, PRIMARY_USER_ID, true)
assertThat(faceAuthAllowed()).isFalse()
@@ -375,25 +375,20 @@
fun faceEnrollmentChangesArePropagatedAfterUserSwitch() =
testScope.runTest {
createBiometricSettingsRepository()
+ val faceAuthAllowed by collectLastValue(underTest.isFaceAuthEnrolledAndEnabled)
+
verify(biometricManager)
.registerEnabledOnKeyguardCallback(biometricManagerCallback.capture())
userRepository.setSelectedUserInfo(ANOTHER_USER)
doNotDisableKeyguardAuthFeatures(ANOTHER_USER_ID)
biometricManagerCallback.value.onChanged(true, ANOTHER_USER_ID)
-
- runCurrent()
- clearInvocations(authController)
-
- val faceAuthAllowed = collectLastValue(underTest.isFaceAuthEnrolledAndEnabled)
- runCurrent()
-
- verify(authController).addCallback(authControllerCallback.capture())
-
+ onNonStrongAuthChanged(true, ANOTHER_USER_ID)
whenever(authController.isFaceAuthEnrolled(ANOTHER_USER_ID)).thenReturn(true)
enrollmentChange(FACE, ANOTHER_USER_ID, true)
+ runCurrent()
- assertThat(faceAuthAllowed()).isTrue()
+ assertThat(faceAuthAllowed).isTrue()
}
@Test
@@ -637,6 +632,7 @@
val isFaceAuthCurrentlyAllowed by collectLastValue(underTest.isFaceAuthCurrentlyAllowed)
faceAuthIsEnrolled()
+ enrollmentChange(FACE, PRIMARY_USER_ID, true)
deviceIsInPostureThatSupportsFaceAuth()
doNotDisableKeyguardAuthFeatures()
faceAuthIsStrongBiometric()
@@ -660,6 +656,7 @@
val isFaceAuthCurrentlyAllowed by collectLastValue(underTest.isFaceAuthCurrentlyAllowed)
faceAuthIsEnrolled()
+ enrollmentChange(FACE, PRIMARY_USER_ID, true)
deviceIsInPostureThatSupportsFaceAuth()
doNotDisableKeyguardAuthFeatures()
faceAuthIsNonStrongBiometric()
@@ -753,7 +750,9 @@
}
private fun enrollmentChange(biometricType: BiometricType, userId: Int, enabled: Boolean) {
- authControllerCallback.value.onEnrollmentsChanged(biometricType, userId, enabled)
+ authControllerCallback.allValues.forEach {
+ it.onEnrollmentsChanged(biometricType, userId, enabled)
+ }
}
private fun doNotDisableKeyguardAuthFeatures(userId: Int = PRIMARY_USER_ID) {
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 06eb0dd..6ad2d73 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
@@ -43,7 +43,7 @@
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.DismissCallbackRegistry
-import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository
+import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFaceAuthRepository
import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
@@ -95,6 +95,7 @@
FakeDeviceEntryFingerprintAuthRepository
private lateinit var fakeKeyguardRepository: FakeKeyguardRepository
private lateinit var powerInteractor: PowerInteractor
+ private lateinit var fakeBiometricSettingsRepository: FakeBiometricSettingsRepository
@Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
@Mock private lateinit var faceWakeUpTriggersConfig: FaceWakeUpTriggersConfig
@@ -123,6 +124,8 @@
facePropertyRepository = FakeFacePropertyRepository()
fakeKeyguardRepository = FakeKeyguardRepository()
powerInteractor = PowerInteractorFactory.create().powerInteractor
+ fakeBiometricSettingsRepository = FakeBiometricSettingsRepository()
+
underTest =
SystemUIKeyguardFaceAuthInteractor(
mContext,
@@ -147,7 +150,7 @@
mock(StatusBarStateController::class.java),
mock(KeyguardStateController::class.java),
bouncerRepository,
- mock(BiometricSettingsRepository::class.java),
+ fakeBiometricSettingsRepository,
FakeSystemClock(),
keyguardUpdateMonitor,
),
@@ -160,6 +163,7 @@
facePropertyRepository,
faceWakeUpTriggersConfig,
powerInteractor,
+ fakeBiometricSettingsRepository,
)
}
@@ -481,6 +485,7 @@
fun faceUnlockIsDisabledWhenFpIsLockedOut() =
testScope.runTest {
underTest.start()
+ fakeBiometricSettingsRepository.setIsFaceAuthEnrolledAndEnabled(true)
fakeDeviceEntryFingerprintAuthRepository.setLockedOut(true)
runCurrent()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
index 9ee22c8..b32905f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
@@ -204,8 +204,7 @@
fromScene = SceneKey.Gone,
toScene = SceneKey.Lockscreen,
progress = flowOf(0f),
- isInitiatedByUserInput = false,
- isUserInputOngoing = flowOf(false),
+ isUserInputDriven = false,
)
runCurrent()
assertThat(isAnimate).isFalse()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractorTest.kt
index 900413c..a5d7457 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractorTest.kt
@@ -35,7 +35,6 @@
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.ExperimentalCoroutinesApi
import org.junit.Before
import org.junit.Rule
import org.junit.Test
@@ -47,7 +46,6 @@
import org.mockito.Mockito.verify
import org.mockito.junit.MockitoJUnit
-@ExperimentalCoroutinesApi
@SmallTest
@RunWith(AndroidJUnit4::class)
class KeyguardKeyEventInteractorTest : SysuiTestCase() {
@@ -59,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
@@ -75,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,
@@ -134,73 +128,58 @@
}
@Test
- fun dispatchKeyEvent_menuActionUp_awakeKeyguard_showsPrimaryBouncer() {
+ fun dispatchKeyEvent_menuActionUp_interactiveKeyguard_collapsesShade() {
powerInteractor.setAwakeForTest()
whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD)
whenever(statusBarKeyguardViewManager.shouldDismissOnMenuPressed()).thenReturn(true)
- verifyActionUpShowsPrimaryBouncer(KeyEvent.KEYCODE_MENU)
+ val actionUpMenuKeyEvent = KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_MENU)
+ assertThat(underTest.dispatchKeyEvent(actionUpMenuKeyEvent)).isTrue()
+ verify(shadeController).animateCollapseShadeForced()
}
@Test
- fun dispatchKeyEvent_menuActionUp_awakeShadeLocked_collapsesShade() {
+ fun dispatchKeyEvent_menuActionUp_interactiveShadeLocked_collapsesShade() {
powerInteractor.setAwakeForTest()
whenever(statusBarStateController.state).thenReturn(StatusBarState.SHADE_LOCKED)
whenever(statusBarKeyguardViewManager.shouldDismissOnMenuPressed()).thenReturn(true)
- verifyActionUpCollapsesTheShade(KeyEvent.KEYCODE_MENU)
+ // action down: does NOT collapse the shade
+ val actionDownMenuKeyEvent = KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MENU)
+ assertThat(underTest.dispatchKeyEvent(actionDownMenuKeyEvent)).isFalse()
+ verify(shadeController, never()).animateCollapseShadeForced()
+
+ // action up: collapses the shade
+ val actionUpMenuKeyEvent = KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_MENU)
+ assertThat(underTest.dispatchKeyEvent(actionUpMenuKeyEvent)).isTrue()
+ verify(shadeController).animateCollapseShadeForced()
}
@Test
- fun dispatchKeyEvent_menuActionUp_asleepKeyguard_neverCollapsesShade() {
+ fun dispatchKeyEvent_menuActionUp_nonInteractiveKeyguard_neverCollapsesShade() {
powerInteractor.setAsleepForTest()
whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD)
whenever(statusBarKeyguardViewManager.shouldDismissOnMenuPressed()).thenReturn(true)
- verifyActionsDoNothing(KeyEvent.KEYCODE_MENU)
+ val actionUpMenuKeyEvent = KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_MENU)
+ assertThat(underTest.dispatchKeyEvent(actionUpMenuKeyEvent)).isFalse()
+ verify(shadeController, never()).animateCollapseShadeForced()
}
@Test
- fun dispatchKeyEvent_spaceActionUp_awakeKeyguard_collapsesShade() {
+ fun dispatchKeyEvent_spaceActionUp_interactiveKeyguard_collapsesShade() {
powerInteractor.setAwakeForTest()
whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD)
- whenever(statusBarKeyguardViewManager.primaryBouncerIsShowing()).thenReturn(false)
- verifyActionUpShowsPrimaryBouncer(KeyEvent.KEYCODE_SPACE)
- }
+ // action down: does NOT collapse the shade
+ val actionDownMenuKeyEvent = KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_SPACE)
+ assertThat(underTest.dispatchKeyEvent(actionDownMenuKeyEvent)).isFalse()
+ verify(shadeController, never()).animateCollapseShadeForced()
- @Test
- fun dispatchKeyEvent_spaceActionUp_shadeLocked_collapsesShade() {
- powerInteractor.setAwakeForTest()
- whenever(statusBarStateController.state).thenReturn(StatusBarState.SHADE_LOCKED)
-
- verifyActionUpCollapsesTheShade(KeyEvent.KEYCODE_SPACE)
- }
-
- @Test
- fun dispatchKeyEvent_enterActionUp_awakeKeyguard_showsPrimaryBouncer() {
- powerInteractor.setAwakeForTest()
- whenever(statusBarKeyguardViewManager.primaryBouncerIsShowing()).thenReturn(false)
- whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD)
-
- verifyActionUpShowsPrimaryBouncer(KeyEvent.KEYCODE_ENTER)
- }
-
- @Test
- fun dispatchKeyEvent_enterActionUp_awakeKeyguard_primaryBouncerAlreadyShowing() {
- powerInteractor.setAwakeForTest()
- whenever(statusBarKeyguardViewManager.primaryBouncerIsShowing()).thenReturn(true)
- whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD)
-
- verifyActionsDoNothing(KeyEvent.KEYCODE_ENTER)
- }
-
- @Test
- fun dispatchKeyEvent_enterActionUp_shadeLocked_collapsesShade() {
- powerInteractor.setAwakeForTest()
- whenever(statusBarStateController.state).thenReturn(StatusBarState.SHADE_LOCKED)
-
- verifyActionUpCollapsesTheShade(KeyEvent.KEYCODE_ENTER)
+ // action up: collapses the shade
+ val actionUpMenuKeyEvent = KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_SPACE)
+ assertThat(underTest.dispatchKeyEvent(actionUpMenuKeyEvent)).isTrue()
+ verify(shadeController).animateCollapseShadeForced()
}
@Test
@@ -270,42 +249,4 @@
.isFalse()
verify(statusBarKeyguardViewManager, never()).interceptMediaKey(any())
}
-
- private fun verifyActionUpCollapsesTheShade(keycode: Int) {
- // action down: does NOT collapse the shade
- val actionDownMenuKeyEvent = KeyEvent(KeyEvent.ACTION_DOWN, keycode)
- assertThat(underTest.dispatchKeyEvent(actionDownMenuKeyEvent)).isFalse()
- verify(shadeController, never()).animateCollapseShadeForced()
-
- // action up: collapses the shade
- val actionUpMenuKeyEvent = KeyEvent(KeyEvent.ACTION_UP, keycode)
- assertThat(underTest.dispatchKeyEvent(actionUpMenuKeyEvent)).isTrue()
- verify(shadeController).animateCollapseShadeForced()
- }
-
- private fun verifyActionUpShowsPrimaryBouncer(keycode: Int) {
- // action down: does NOT collapse the shade
- val actionDownMenuKeyEvent = KeyEvent(KeyEvent.ACTION_DOWN, keycode)
- assertThat(underTest.dispatchKeyEvent(actionDownMenuKeyEvent)).isFalse()
- verify(statusBarKeyguardViewManager, never()).showPrimaryBouncer(any())
-
- // action up: collapses the shade
- val actionUpMenuKeyEvent = KeyEvent(KeyEvent.ACTION_UP, keycode)
- assertThat(underTest.dispatchKeyEvent(actionUpMenuKeyEvent)).isTrue()
- verify(statusBarKeyguardViewManager).showPrimaryBouncer(eq(true))
- }
-
- private fun verifyActionsDoNothing(keycode: Int) {
- // action down: does nothing
- val actionDownMenuKeyEvent = KeyEvent(KeyEvent.ACTION_DOWN, keycode)
- assertThat(underTest.dispatchKeyEvent(actionDownMenuKeyEvent)).isFalse()
- verify(shadeController, never()).animateCollapseShadeForced()
- verify(statusBarKeyguardViewManager, never()).showPrimaryBouncer(any())
-
- // action up: doesNothing
- val actionUpMenuKeyEvent = KeyEvent(KeyEvent.ACTION_UP, keycode)
- assertThat(underTest.dispatchKeyEvent(actionUpMenuKeyEvent)).isFalse()
- verify(shadeController, never()).animateCollapseShadeForced()
- verify(statusBarKeyguardViewManager, never()).showPrimaryBouncer(any())
- }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
index 2cf0e77..5d5ece0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
@@ -433,7 +433,9 @@
// GIVEN a prior transition has run to DREAMING_LOCKSCREEN_HOSTED
runTransitionAndSetWakefulness(
- KeyguardState.GONE, KeyguardState.DREAMING_LOCKSCREEN_HOSTED)
+ KeyguardState.GONE,
+ KeyguardState.DREAMING_LOCKSCREEN_HOSTED
+ )
// WHEN the lockscreen hosted dream stops
keyguardRepository.setIsActiveDreamLockscreenHosted(false)
@@ -457,7 +459,9 @@
testScope.runTest {
// GIVEN a prior transition has run to DREAMING_LOCKSCREEN_HOSTED
runTransitionAndSetWakefulness(
- KeyguardState.GONE, KeyguardState.DREAMING_LOCKSCREEN_HOSTED)
+ KeyguardState.GONE,
+ KeyguardState.DREAMING_LOCKSCREEN_HOSTED
+ )
// WHEN biometrics succeeds with wake and unlock from dream mode
keyguardRepository.setBiometricUnlockState(
@@ -487,7 +491,9 @@
// GIVEN a prior transition has run to DREAMING_LOCKSCREEN_HOSTED
runTransitionAndSetWakefulness(
- KeyguardState.GONE, KeyguardState.DREAMING_LOCKSCREEN_HOSTED)
+ KeyguardState.GONE,
+ KeyguardState.DREAMING_LOCKSCREEN_HOSTED
+ )
// WHEN the primary bouncer is set to show
bouncerRepository.setPrimaryShow(true)
@@ -515,7 +521,9 @@
// GIVEN a prior transition has run to DREAMING_LOCKSCREEN_HOSTED
runTransitionAndSetWakefulness(
- KeyguardState.GONE, KeyguardState.DREAMING_LOCKSCREEN_HOSTED)
+ KeyguardState.GONE,
+ KeyguardState.DREAMING_LOCKSCREEN_HOSTED
+ )
// WHEN the device begins to sleep
keyguardRepository.setIsActiveDreamLockscreenHosted(false)
@@ -547,7 +555,9 @@
// GIVEN a prior transition has run to DREAMING_LOCKSCREEN_HOSTED
runTransitionAndSetWakefulness(
- KeyguardState.GONE, KeyguardState.DREAMING_LOCKSCREEN_HOSTED)
+ KeyguardState.GONE,
+ KeyguardState.DREAMING_LOCKSCREEN_HOSTED
+ )
// WHEN the keyguard is occluded and the lockscreen hosted dream stops
keyguardRepository.setIsActiveDreamLockscreenHosted(false)
@@ -783,7 +793,9 @@
testScope.runTest {
// GIVEN a prior transition has run to ALTERNATE_BOUNCER
runTransitionAndSetWakefulness(
- KeyguardState.LOCKSCREEN, KeyguardState.ALTERNATE_BOUNCER)
+ KeyguardState.LOCKSCREEN,
+ KeyguardState.ALTERNATE_BOUNCER
+ )
// WHEN the alternateBouncer stops showing and then the primary bouncer shows
bouncerRepository.setPrimaryShow(true)
@@ -808,7 +820,9 @@
// GIVEN a prior transition has run to ALTERNATE_BOUNCER
bouncerRepository.setAlternateVisible(true)
runTransitionAndSetWakefulness(
- KeyguardState.LOCKSCREEN, KeyguardState.ALTERNATE_BOUNCER)
+ KeyguardState.LOCKSCREEN,
+ KeyguardState.ALTERNATE_BOUNCER
+ )
// GIVEN the primary bouncer isn't showing, aod available and starting to sleep
bouncerRepository.setPrimaryShow(false)
@@ -838,7 +852,9 @@
// GIVEN a prior transition has run to ALTERNATE_BOUNCER
bouncerRepository.setAlternateVisible(true)
runTransitionAndSetWakefulness(
- KeyguardState.LOCKSCREEN, KeyguardState.ALTERNATE_BOUNCER)
+ KeyguardState.LOCKSCREEN,
+ KeyguardState.ALTERNATE_BOUNCER
+ )
// GIVEN the primary bouncer isn't showing, aod not available and starting to sleep
// to sleep
@@ -869,7 +885,9 @@
// GIVEN a prior transition has run to ALTERNATE_BOUNCER
bouncerRepository.setAlternateVisible(true)
runTransitionAndSetWakefulness(
- KeyguardState.LOCKSCREEN, KeyguardState.ALTERNATE_BOUNCER)
+ KeyguardState.LOCKSCREEN,
+ KeyguardState.ALTERNATE_BOUNCER
+ )
// GIVEN the primary bouncer isn't showing and device not sleeping
bouncerRepository.setPrimaryShow(false)
@@ -980,7 +998,9 @@
// GIVEN a prior transition has run to PRIMARY_BOUNCER
bouncerRepository.setPrimaryShow(true)
runTransitionAndSetWakefulness(
- KeyguardState.DREAMING_LOCKSCREEN_HOSTED, KeyguardState.PRIMARY_BOUNCER)
+ KeyguardState.DREAMING_LOCKSCREEN_HOSTED,
+ KeyguardState.PRIMARY_BOUNCER
+ )
// WHEN the primary bouncer stops showing and lockscreen hosted dream still active
bouncerRepository.setPrimaryShow(false)
@@ -1161,6 +1181,57 @@
}
@Test
+ fun dreamingToOccluded() =
+ testScope.runTest {
+ // GIVEN a prior transition has run to DREAMING
+ keyguardRepository.setDreaming(true)
+ runTransitionAndSetWakefulness(KeyguardState.LOCKSCREEN, KeyguardState.DREAMING)
+ runCurrent()
+
+ // WHEN the keyguard is occluded and device wakes up and is no longer dreaming
+ keyguardRepository.setDreaming(false)
+ keyguardRepository.setKeyguardOccluded(true)
+ powerInteractor.setAwakeForTest()
+ runCurrent()
+
+ val info =
+ withArgCaptor<TransitionInfo> {
+ verify(transitionRepository).startTransition(capture(), anyBoolean())
+ }
+ // THEN a transition to OCCLUDED should occur
+ assertThat(info.ownerName).isEqualTo("FromDreamingTransitionInteractor")
+ assertThat(info.from).isEqualTo(KeyguardState.DREAMING)
+ assertThat(info.to).isEqualTo(KeyguardState.OCCLUDED)
+ assertThat(info.animator).isNotNull()
+
+ coroutineContext.cancelChildren()
+ }
+
+ @Test
+ fun lockscreenToOccluded() =
+ testScope.runTest {
+ // GIVEN a prior transition has run to LOCKSCREEN
+ runTransitionAndSetWakefulness(KeyguardState.GONE, KeyguardState.LOCKSCREEN)
+ runCurrent()
+
+ // WHEN the keyguard is occluded
+ keyguardRepository.setKeyguardOccluded(true)
+ runCurrent()
+
+ val info =
+ withArgCaptor<TransitionInfo> {
+ verify(transitionRepository).startTransition(capture(), anyBoolean())
+ }
+ // THEN a transition to OCCLUDED should occur
+ assertThat(info.ownerName).isEqualTo("FromLockscreenTransitionInteractor")
+ assertThat(info.from).isEqualTo(KeyguardState.LOCKSCREEN)
+ assertThat(info.to).isEqualTo(KeyguardState.OCCLUDED)
+ assertThat(info.animator).isNotNull()
+
+ coroutineContext.cancelChildren()
+ }
+
+ @Test
fun aodToOccluded() =
testScope.runTest {
// GIVEN a prior transition has run to AOD
@@ -1286,8 +1357,8 @@
}
private suspend fun TestScope.runTransitionAndSetWakefulness(
- from: KeyguardState,
- to: KeyguardState
+ from: KeyguardState,
+ to: KeyguardState
) {
transitionRepository.sendTransitionStep(
TransitionStep(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt
index 7940b45..50ee026 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt
@@ -23,6 +23,7 @@
import androidx.constraintlayout.widget.ConstraintSet
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.communal.ui.view.layout.sections.CommunalTutorialIndicatorSection
import com.android.systemui.keyguard.shared.model.KeyguardBlueprint
import com.android.systemui.keyguard.shared.model.KeyguardSection
import com.android.systemui.keyguard.ui.view.KeyguardRootView
@@ -65,6 +66,7 @@
@Mock private lateinit var splitShadeGuidelines: SplitShadeGuidelines
@Mock private lateinit var aodNotificationIconsSection: AodNotificationIconsSection
@Mock private lateinit var aodBurnInSection: AodBurnInSection
+ @Mock private lateinit var communalTutorialIndicatorSection: CommunalTutorialIndicatorSection
@Before
fun setup() {
@@ -83,6 +85,7 @@
splitShadeGuidelines,
aodNotificationIconsSection,
aodBurnInSection,
+ communalTutorialIndicatorSection,
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModelTest.kt
new file mode 100644
index 0000000..255f4df
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModelTest.kt
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
+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.google.common.collect.Range
+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
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class GoneToAodTransitionViewModelTest : SysuiTestCase() {
+ private lateinit var underTest: GoneToAodTransitionViewModel
+ private lateinit var repository: FakeKeyguardTransitionRepository
+ private lateinit var testScope: TestScope
+
+ @Before
+ fun setUp() {
+ val testDispatcher = StandardTestDispatcher()
+ testScope = TestScope(testDispatcher)
+
+ repository = FakeKeyguardTransitionRepository()
+ val interactor =
+ KeyguardTransitionInteractorFactory.create(
+ scope = testScope.backgroundScope,
+ repository = repository,
+ )
+ .keyguardTransitionInteractor
+ underTest = GoneToAodTransitionViewModel(interactor)
+ }
+
+ @Test
+ fun enterFromTopTranslationY() =
+ testScope.runTest {
+ val pixels = -100f
+ val enterFromTopTranslationY by
+ collectLastValue(underTest.enterFromTopTranslationY(pixels.toInt()))
+
+ // The animation should only start > halfway through
+ repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+ assertThat(enterFromTopTranslationY).isEqualTo(pixels)
+
+ repository.sendTransitionStep(step(0.5f))
+ assertThat(enterFromTopTranslationY).isEqualTo(pixels)
+
+ repository.sendTransitionStep(step(.85f))
+ assertThat(enterFromTopTranslationY).isIn(Range.closed(pixels, 0f))
+
+ // At the end, the translation should be complete and set to zero
+ repository.sendTransitionStep(step(1f))
+ assertThat(enterFromTopTranslationY).isEqualTo(0f)
+ }
+
+ @Test
+ fun enterFromTopAnimationAlpha() =
+ testScope.runTest {
+ val enterFromTopAnimationAlpha by collectLastValue(underTest.enterFromTopAnimationAlpha)
+
+ // The animation should only start > halfway through
+ repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+ assertThat(enterFromTopAnimationAlpha).isEqualTo(0f)
+
+ repository.sendTransitionStep(step(0.5f))
+ assertThat(enterFromTopAnimationAlpha).isEqualTo(0f)
+
+ repository.sendTransitionStep(step(.85f))
+ assertThat(enterFromTopAnimationAlpha).isIn(Range.closed(0f, 1f))
+
+ repository.sendTransitionStep(step(1f))
+ assertThat(enterFromTopAnimationAlpha).isEqualTo(1f)
+ }
+
+ private fun step(
+ value: Float,
+ state: TransitionState = TransitionState.RUNNING
+ ): TransitionStep {
+ return TransitionStep(
+ from = KeyguardState.GONE,
+ to = KeyguardState.AOD,
+ value = value,
+ transitionState = state,
+ ownerName = "GoneToAodTransitionViewModelTest"
+ )
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
index 71688db..4f545cb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
@@ -17,8 +17,10 @@
package com.android.systemui.keyguard.ui.viewmodel
+import android.view.View
import androidx.test.filters.SmallTest
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.flags.Flags
@@ -26,12 +28,17 @@
import com.android.systemui.keyguard.domain.interactor.BurnInInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.BurnInModel
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.plugins.ClockController
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import javax.inject.Provider
+import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runTest
@@ -41,6 +48,7 @@
import org.junit.runners.JUnit4
import org.mockito.Answers
import org.mockito.Mock
+import org.mockito.Mockito.anyInt
import org.mockito.MockitoAnnotations
@SmallTest
@@ -51,10 +59,20 @@
private lateinit var testScope: TestScope
private lateinit var repository: FakeKeyguardRepository
private lateinit var keyguardInteractor: KeyguardInteractor
+ private lateinit var configurationRepository: FakeConfigurationRepository
@Mock private lateinit var burnInInteractor: BurnInInteractor
+ @Mock private lateinit var keyguardTransitionInteractor: KeyguardTransitionInteractor
+ @Mock private lateinit var goneToAodTransitionViewModel: GoneToAodTransitionViewModel
+ @Mock
+ private lateinit var aodToLockscreenTransitionViewModel: AodToLockscreenTransitionViewModel
@Mock(answer = Answers.RETURNS_DEEP_STUBS) private lateinit var clockController: ClockController
private val burnInFlow = MutableStateFlow(BurnInModel())
+ private val goneToAodTransitionViewModelVisibility = MutableStateFlow(0)
+ private val enterFromTopAnimationAlpha = MutableStateFlow(0f)
+ private val goneToAodTransitionStep = MutableSharedFlow<TransitionStep>(replay = 1)
+ private val dozeAmountTransitionStep = MutableSharedFlow<TransitionStep>(replay = 1)
+ private val startedKeyguardState = MutableStateFlow(KeyguardState.GONE)
@Before
fun setUp() {
@@ -71,9 +89,30 @@
val withDeps = KeyguardInteractorFactory.create(featureFlags = featureFlags)
keyguardInteractor = withDeps.keyguardInteractor
repository = withDeps.repository
+ configurationRepository = withDeps.configurationRepository
+
+ whenever(goneToAodTransitionViewModel.enterFromTopTranslationY(anyInt()))
+ .thenReturn(emptyFlow<Float>())
+ whenever(goneToAodTransitionViewModel.enterFromTopAnimationAlpha)
+ .thenReturn(enterFromTopAnimationAlpha)
whenever(burnInInteractor.keyguardBurnIn).thenReturn(burnInFlow)
- underTest = KeyguardRootViewModel(keyguardInteractor, burnInInteractor)
+
+ whenever(keyguardTransitionInteractor.goneToAodTransition)
+ .thenReturn(goneToAodTransitionStep)
+ whenever(keyguardTransitionInteractor.dozeAmountTransition)
+ .thenReturn(dozeAmountTransitionStep)
+ whenever(keyguardTransitionInteractor.startedKeyguardState).thenReturn(startedKeyguardState)
+
+ underTest =
+ KeyguardRootViewModel(
+ context,
+ keyguardInteractor,
+ burnInInteractor,
+ goneToAodTransitionViewModel,
+ aodToLockscreenTransitionViewModel,
+ keyguardTransitionInteractor,
+ )
underTest.clockControllerProvider = Provider { clockController }
}
@@ -118,7 +157,7 @@
val scale by collectLastValue(underTest.scale)
// Set to not dozing (on lockscreen)
- repository.setDozeAmount(0f)
+ dozeAmountTransitionStep.emit(TransitionStep(value = 0f))
// Trigger a change to the burn-in model
burnInFlow.value =
@@ -141,8 +180,7 @@
val scale by collectLastValue(underTest.scale)
// Set to dozing (on AOD)
- repository.setDozeAmount(1f)
-
+ dozeAmountTransitionStep.emit(TransitionStep(value = 1f))
// Trigger a change to the burn-in model
burnInFlow.value =
BurnInModel(
@@ -150,10 +188,15 @@
translationY = 30,
scale = 0.5f,
)
-
assertThat(translationX).isEqualTo(20)
assertThat(translationY).isEqualTo(30)
assertThat(scale).isEqualTo(Pair(0.5f, true /* scaleClockOnly */))
+
+ // Set to the beginning of GONE->AOD transition
+ goneToAodTransitionStep.emit(TransitionStep(value = 0f))
+ assertThat(translationX).isEqualTo(0)
+ assertThat(translationY).isEqualTo(0)
+ assertThat(scale).isEqualTo(Pair(1f, true /* scaleClockOnly */))
}
@Test
@@ -166,7 +209,7 @@
val scale by collectLastValue(underTest.scale)
// Set to dozing (on AOD)
- repository.setDozeAmount(1f)
+ dozeAmountTransitionStep.emit(TransitionStep(value = 1f))
// Trigger a change to the burn-in model
burnInFlow.value =
@@ -180,4 +223,28 @@
assertThat(translationY).isEqualTo(0)
assertThat(scale).isEqualTo(Pair(0.5f, false /* scaleClockOnly */))
}
+
+ @Test
+ fun burnInLayerVisibility() =
+ testScope.runTest {
+ val burnInLayerVisibility by collectLastValue(underTest.burnInLayerVisibility)
+
+ startedKeyguardState.value = KeyguardState.OCCLUDED
+ assertThat(burnInLayerVisibility).isNull()
+
+ startedKeyguardState.value = KeyguardState.AOD
+ assertThat(burnInLayerVisibility).isEqualTo(View.VISIBLE)
+ }
+
+ @Test
+ fun burnInLayerAlpha() =
+ testScope.runTest {
+ val burnInLayerAlpha by collectLastValue(underTest.burnInLayerAlpha)
+
+ enterFromTopAnimationAlpha.value = 0.2f
+ assertThat(burnInLayerAlpha).isEqualTo(0.2f)
+
+ enterFromTopAnimationAlpha.value = 1f
+ assertThat(burnInLayerAlpha).isEqualTo(1f)
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt
index 9364097..d7802aa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt
@@ -60,10 +60,7 @@
MockitoAnnotations.initMocks(this)
repository = FakeKeyguardTransitionRepository()
val featureFlags =
- FakeFeatureFlags().apply {
- set(Flags.REFACTOR_KEYGUARD_DISMISS_INTENT, false)
- set(Flags.UDFPS_NEW_TOUCH_DETECTION, true)
- }
+ FakeFeatureFlags().apply { set(Flags.REFACTOR_KEYGUARD_DISMISS_INTENT, false) }
val interactor =
KeyguardTransitionInteractorFactory.create(
scope = TestScope().backgroundScope,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataManagerTest.kt
index fb0a4be..59d8104 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataManagerTest.kt
@@ -48,7 +48,6 @@
import com.android.internal.logging.InstanceId
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.InstanceIdSequenceFake
-import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.dump.DumpManager
@@ -63,6 +62,7 @@
import com.android.systemui.media.controls.util.MediaFlags
import com.android.systemui.media.controls.util.MediaUiEventLogger
import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.res.R
import com.android.systemui.statusbar.SbnBuilder
import com.android.systemui.tuner.TunerService
import com.android.systemui.util.concurrency.FakeExecutor
@@ -2204,6 +2204,85 @@
}
@Test
+ fun testSessionPlayer_sessionDestroyed_noResume_fullyRemoved() {
+ whenever(mediaFlags.areMediaSessionActionsEnabled(any(), any())).thenReturn(true)
+ addPlaybackStateAction()
+
+ // When a media control with PlaybackState actions is added, times out,
+ // and then the session is destroyed
+ addNotificationAndLoad()
+ val data = mediaDataCaptor.value
+ assertThat(data.active).isTrue()
+ mediaDataManager.setTimedOut(KEY, timedOut = true)
+ sessionCallbackCaptor.value.invoke(KEY)
+
+ // It is fully removed.
+ verify(listener).onMediaDataRemoved(eq(KEY))
+ verify(logger).logMediaRemoved(anyInt(), eq(PACKAGE_NAME), eq(data.instanceId))
+ verify(listener, never())
+ .onMediaDataLoaded(
+ eq(PACKAGE_NAME),
+ eq(KEY),
+ capture(mediaDataCaptor),
+ eq(true),
+ eq(0),
+ eq(false)
+ )
+ }
+
+ @Test
+ fun testSessionPlayer_destroyedWhileActive_noResume_fullyRemoved() {
+ whenever(mediaFlags.areMediaSessionActionsEnabled(any(), any())).thenReturn(true)
+ addPlaybackStateAction()
+
+ // When a media control using session actions is added, and then the session is destroyed
+ // without timing out first
+ addNotificationAndLoad()
+ val data = mediaDataCaptor.value
+ assertThat(data.active).isTrue()
+ sessionCallbackCaptor.value.invoke(KEY)
+
+ // It is fully removed
+ verify(listener).onMediaDataRemoved(eq(KEY))
+ verify(logger).logMediaRemoved(anyInt(), eq(PACKAGE_NAME), eq(data.instanceId))
+ verify(listener, never())
+ .onMediaDataLoaded(eq(PACKAGE_NAME), any(), any(), anyBoolean(), anyInt(), anyBoolean())
+ }
+
+ @Test
+ fun testSessionPlayer_canResume_destroyedWhileActive_setToResume() {
+ whenever(mediaFlags.areMediaSessionActionsEnabled(any(), any())).thenReturn(true)
+ addPlaybackStateAction()
+
+ // When a media control using session actions and that does allow resumption is added,
+ addNotificationAndLoad()
+ val dataResumable = mediaDataCaptor.value.copy(resumeAction = Runnable {})
+ mediaDataManager.onMediaDataLoaded(KEY, null, dataResumable)
+
+ // And then the session is destroyed without timing out first
+ sessionCallbackCaptor.value.invoke(KEY)
+
+ // It is converted to a resume player
+ verify(listener)
+ .onMediaDataLoaded(
+ eq(PACKAGE_NAME),
+ eq(KEY),
+ capture(mediaDataCaptor),
+ eq(true),
+ eq(0),
+ eq(false)
+ )
+ assertThat(mediaDataCaptor.value.resumption).isTrue()
+ assertThat(mediaDataCaptor.value.active).isFalse()
+ verify(logger)
+ .logActiveConvertedToResume(
+ anyInt(),
+ eq(PACKAGE_NAME),
+ eq(mediaDataCaptor.value.instanceId)
+ )
+ }
+
+ @Test
fun testSessionDestroyed_noNotificationKey_stillRemoved() {
whenever(mediaFlags.isRetainingPlayersEnabled()).thenReturn(true)
whenever(mediaFlags.areMediaSessionActionsEnabled(any(), any())).thenReturn(true)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDeviceManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDeviceManagerTest.kt
index 85d3fba..deefab6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDeviceManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDeviceManagerTest.kt
@@ -21,6 +21,7 @@
import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
import android.graphics.drawable.Drawable
+import android.media.MediaRoute2Info
import android.media.MediaRouter2Manager
import android.media.RoutingSessionInfo
import android.media.session.MediaController
@@ -34,15 +35,18 @@
import com.android.settingslib.bluetooth.LocalBluetoothProfileManager
import com.android.settingslib.media.LocalMediaManager
import com.android.settingslib.media.MediaDevice
-import com.android.systemui.res.R
+import com.android.settingslib.media.PhoneMediaDevice
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
+import com.android.systemui.flags.FakeFeatureFlagsClassic
+import com.android.systemui.flags.Flags
import com.android.systemui.media.controls.MediaTestUtils
import com.android.systemui.media.controls.models.player.MediaData
import com.android.systemui.media.controls.models.player.MediaDeviceData
import com.android.systemui.media.controls.util.MediaControllerFactory
import com.android.systemui.media.muteawait.MediaMuteAwaitConnectionManager
import com.android.systemui.media.muteawait.MediaMuteAwaitConnectionManagerFactory
+import com.android.systemui.res.R
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.eq
@@ -95,6 +99,7 @@
@Mock private lateinit var device: MediaDevice
@Mock private lateinit var icon: Drawable
@Mock private lateinit var route: RoutingSessionInfo
+ @Mock private lateinit var selectedRoute: MediaRoute2Info
@Mock private lateinit var controller: MediaController
@Mock private lateinit var playbackInfo: PlaybackInfo
@Mock private lateinit var configurationController: ConfigurationController
@@ -107,6 +112,7 @@
private lateinit var session: MediaSession
private lateinit var mediaData: MediaData
@JvmField @Rule val mockito = MockitoJUnit.rule()
+ private val featureFlags = FakeFeatureFlagsClassic()
@Before
fun setUp() {
@@ -124,7 +130,8 @@
localBluetoothManager,
fakeFgExecutor,
fakeBgExecutor,
- dumpster
+ dumpster,
+ featureFlags,
)
manager.addListener(listener)
@@ -143,6 +150,7 @@
MediaTestUtils.emptyMediaData.copy(packageName = PACKAGE, token = session.sessionToken)
whenever(controllerFactory.create(session.sessionToken)).thenReturn(controller)
setupLeAudioConfiguration(false)
+ featureFlags.set(Flags.MEDIA_DEVICE_NAME_FIX, false)
}
@After
@@ -454,9 +462,54 @@
}
@Test
- fun mr2ReturnsRouteWithNullName_useLocalDeviceName() {
+ fun mr2ReturnsSystemRouteWithNullName_isPhone_usePhoneName() {
+ featureFlags.set(Flags.MEDIA_DEVICE_NAME_FIX, true)
+ // When the routing session name is null, and is a system session for a PhoneMediaDevice
+ val phoneDevice = mock(PhoneMediaDevice::class.java)
+ whenever(phoneDevice.iconWithoutBackground).thenReturn(icon)
+ whenever(lmm.currentConnectedDevice).thenReturn(phoneDevice)
+ whenever(route.isSystemSession).thenReturn(true)
+
+ whenever(route.name).thenReturn(null)
+ whenever(mr2.getSelectedRoutes(any())).thenReturn(listOf(selectedRoute))
+ whenever(selectedRoute.name).thenReturn(REMOTE_DEVICE_NAME)
+ whenever(selectedRoute.type).thenReturn(MediaRoute2Info.TYPE_BUILTIN_SPEAKER)
+
+ manager.onMediaDataLoaded(KEY, null, mediaData)
+ fakeBgExecutor.runAllReady()
+ fakeFgExecutor.runAllReady()
+
+ // Then the device name is the PhoneMediaDevice string
+ val data = captureDeviceData(KEY)
+ assertThat(data.name)
+ .isEqualTo(
+ context.getString(com.android.settingslib.R.string.media_transfer_this_device_name)
+ )
+ }
+
+ @Test
+ fun mr2ReturnsSystemRouteWithNullName_useSelectedRouteName() {
+ featureFlags.set(Flags.MEDIA_DEVICE_NAME_FIX, true)
+ // When the routing session does not have a name, and is a system session
+ whenever(route.name).thenReturn(null)
+ whenever(mr2.getSelectedRoutes(any())).thenReturn(listOf(selectedRoute))
+ whenever(selectedRoute.name).thenReturn(REMOTE_DEVICE_NAME)
+ whenever(route.isSystemSession).thenReturn(true)
+
+ manager.onMediaDataLoaded(KEY, null, mediaData)
+ fakeBgExecutor.runAllReady()
+ fakeFgExecutor.runAllReady()
+
+ // Then the device name is the selected route name
+ val data = captureDeviceData(KEY)
+ assertThat(data.name).isEqualTo(REMOTE_DEVICE_NAME)
+ }
+
+ @Test
+ fun mr2ReturnsNonSystemRouteWithNullName_useLocalDeviceName() {
// GIVEN that MR2Manager returns a routing session that does not have a name
whenever(route.name).thenReturn(null)
+ whenever(route.isSystemSession).thenReturn(false)
// WHEN a notification is added
manager.onMediaDataLoaded(KEY, null, mediaData)
fakeBgExecutor.runAllReady()
@@ -672,13 +725,108 @@
assertThat(data.showBroadcastButton).isFalse()
}
- fun captureCallback(): LocalMediaManager.DeviceCallback {
+ // Duplicates of above tests with MEDIA_DEVICE_NAME_FIX enabled
+
+ @Test
+ fun loadMediaDataWithNullToken_withNameFix() {
+ featureFlags.set(Flags.MEDIA_DEVICE_NAME_FIX, true)
+ manager.onMediaDataLoaded(KEY, null, mediaData.copy(token = null))
+ fakeBgExecutor.runAllReady()
+ fakeFgExecutor.runAllReady()
+ val data = captureDeviceData(KEY)
+ assertThat(data.enabled).isTrue()
+ assertThat(data.name).isEqualTo(DEVICE_NAME)
+ }
+
+ @Test
+ fun onAboutToConnectDeviceAdded_findsDeviceInfoFromAddress_withNameFix() {
+ featureFlags.set(Flags.MEDIA_DEVICE_NAME_FIX, true)
+ manager.onMediaDataLoaded(KEY, null, mediaData)
+ // Run and reset the executors and listeners so we only focus on new events.
+ fakeBgExecutor.runAllReady()
+ fakeFgExecutor.runAllReady()
+ reset(listener)
+
+ // Ensure we'll get device info when using the address
+ val fullMediaDevice = mock(MediaDevice::class.java)
+ val address = "fakeAddress"
+ val nameFromDevice = "nameFromDevice"
+ val iconFromDevice = mock(Drawable::class.java)
+ whenever(lmm.getMediaDeviceById(eq(address))).thenReturn(fullMediaDevice)
+ whenever(fullMediaDevice.name).thenReturn(nameFromDevice)
+ whenever(fullMediaDevice.iconWithoutBackground).thenReturn(iconFromDevice)
+
+ // WHEN the about-to-connect device changes to non-null
+ val deviceCallback = captureCallback()
+ val nameFromParam = "nameFromParam"
+ val iconFromParam = mock(Drawable::class.java)
+ deviceCallback.onAboutToConnectDeviceAdded(address, nameFromParam, iconFromParam)
+ assertThat(fakeFgExecutor.runAllReady()).isEqualTo(1)
+
+ // THEN the about-to-connect device based on the address is returned
+ val data = captureDeviceData(KEY)
+ assertThat(data.enabled).isTrue()
+ assertThat(data.name).isEqualTo(nameFromDevice)
+ assertThat(data.name).isNotEqualTo(nameFromParam)
+ assertThat(data.icon).isEqualTo(iconFromDevice)
+ assertThat(data.icon).isNotEqualTo(iconFromParam)
+ }
+
+ @Test
+ fun deviceNameFromMR2RouteInfo_withNameFix() {
+ featureFlags.set(Flags.MEDIA_DEVICE_NAME_FIX, true)
+ // GIVEN that MR2Manager returns a valid routing session
+ whenever(route.name).thenReturn(REMOTE_DEVICE_NAME)
+ // WHEN a notification is added
+ manager.onMediaDataLoaded(KEY, null, mediaData)
+ fakeBgExecutor.runAllReady()
+ fakeFgExecutor.runAllReady()
+ // THEN it uses the route name (instead of device name)
+ val data = captureDeviceData(KEY)
+ assertThat(data.enabled).isTrue()
+ assertThat(data.name).isEqualTo(REMOTE_DEVICE_NAME)
+ }
+
+ @Test
+ fun deviceDisabledWhenMR2ReturnsNullRouteInfo_withNameFix() {
+ featureFlags.set(Flags.MEDIA_DEVICE_NAME_FIX, true)
+ // GIVEN that MR2Manager returns null for routing session
+ whenever(mr2.getRoutingSessionForMediaController(any())).thenReturn(null)
+ // WHEN a notification is added
+ manager.onMediaDataLoaded(KEY, null, mediaData)
+ fakeBgExecutor.runAllReady()
+ fakeFgExecutor.runAllReady()
+ // THEN the device is disabled and name is set to null
+ val data = captureDeviceData(KEY)
+ assertThat(data.enabled).isFalse()
+ assertThat(data.name).isNull()
+ }
+
+ @Test
+ fun mr2ReturnsNonSystemRouteWithNullName_useLocalDeviceName_withNameFix() {
+ featureFlags.set(Flags.MEDIA_DEVICE_NAME_FIX, true)
+ // GIVEN that MR2Manager returns a routing session that does not have a name
+ whenever(route.name).thenReturn(null)
+ whenever(route.isSystemSession).thenReturn(false)
+ // WHEN a notification is added
+ manager.onMediaDataLoaded(KEY, null, mediaData)
+ fakeBgExecutor.runAllReady()
+ fakeFgExecutor.runAllReady()
+ // THEN the device is enabled and uses the current connected device name
+ val data = captureDeviceData(KEY)
+ assertThat(data.name).isEqualTo(DEVICE_NAME)
+ assertThat(data.enabled).isTrue()
+ }
+
+ // End duplicate tests
+
+ private fun captureCallback(): LocalMediaManager.DeviceCallback {
val captor = ArgumentCaptor.forClass(LocalMediaManager.DeviceCallback::class.java)
verify(lmm).registerCallback(captor.capture())
return captor.getValue()
}
- fun setupBroadcastCallback(): BluetoothLeBroadcast.Callback {
+ private fun setupBroadcastCallback(): BluetoothLeBroadcast.Callback {
val callback: BluetoothLeBroadcast.Callback =
object : BluetoothLeBroadcast.Callback {
override fun onBroadcastStarted(reason: Int, broadcastId: Int) {}
@@ -699,7 +847,7 @@
return callback
}
- fun setupLeAudioConfiguration(isLeAudio: Boolean) {
+ private fun setupLeAudioConfiguration(isLeAudio: Boolean) {
whenever(localBluetoothManager.profileManager).thenReturn(localBluetoothProfileManager)
whenever(localBluetoothProfileManager.leAudioBroadcastProfile)
.thenReturn(localBluetoothLeBroadcast)
@@ -707,7 +855,7 @@
whenever(localBluetoothLeBroadcast.appSourceName).thenReturn(BROADCAST_APP_NAME)
}
- fun setupBroadcastPackage(currentName: String) {
+ private fun setupBroadcastPackage(currentName: String) {
whenever(lmm.packageName).thenReturn(PACKAGE)
whenever(packageManager.getApplicationInfo(eq(PACKAGE), anyInt()))
.thenReturn(applicationInfo)
@@ -715,7 +863,7 @@
context.setMockPackageManager(packageManager)
}
- fun captureDeviceData(key: String, oldKey: String? = null): MediaDeviceData {
+ private fun captureDeviceData(key: String, oldKey: String? = null): MediaDeviceData {
val captor = ArgumentCaptor.forClass(MediaDeviceData::class.java)
verify(listener).onMediaDeviceChanged(eq(key), eq(oldKey), captor.capture())
return captor.getValue()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaHierarchyManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaHierarchyManagerTest.kt
index de57b60..5296f1a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaHierarchyManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaHierarchyManagerTest.kt
@@ -24,7 +24,6 @@
import android.widget.FrameLayout
import androidx.test.filters.SmallTest
import com.android.keyguard.KeyguardViewController
-import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.controls.controller.ControlsControllerImplTest.Companion.eq
import com.android.systemui.dreams.DreamOverlayStateController
@@ -32,6 +31,7 @@
import com.android.systemui.media.controls.pipeline.MediaDataManager
import com.android.systemui.media.dream.MediaDreamComplication
import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.res.R
import com.android.systemui.shade.ShadeExpansionStateManager
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.SysuiStatusBarStateController
diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorControllerTest.kt
index f25cd24..6cdf4ef 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
@@ -4,18 +4,25 @@
import android.os.UserHandle
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
+import com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_APP_SELECTOR_DISPLAYED as STATE_APP_SELECTOR_DISPLAYED
import com.android.systemui.SysuiTestCase
+import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger
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
+import org.mockito.Mockito.never
import org.mockito.Mockito.verify
@RunWith(AndroidTestingRunner::class)
@@ -33,8 +40,11 @@
private val view: MediaProjectionAppSelectorView = mock()
private val policyResolver: ScreenCaptureDevicePolicyResolver = mock()
+ private val logger = mock<MediaProjectionMetricsLogger>()
- private val controller =
+ private val thumbnailLoader = FakeThumbnailLoader()
+
+ private fun createController(isFirstStart: Boolean = true) =
MediaProjectionAppSelectorController(
taskListProvider,
view,
@@ -42,7 +52,10 @@
personalUserHandle,
scope,
appSelectorComponentName,
- callerPackageName
+ callerPackageName,
+ thumbnailLoader,
+ isFirstStart,
+ logger
)
@Before
@@ -54,7 +67,7 @@
fun initNoRecentTasks_bindsEmptyList() {
taskListProvider.tasks = emptyList()
- controller.init()
+ createController().init()
verify(view).bind(emptyList())
}
@@ -63,12 +76,28 @@
fun initOneRecentTask_bindsList() {
taskListProvider.tasks = listOf(createRecentTask(taskId = 1))
- controller.init()
+ createController().init()
verify(view).bind(listOf(createRecentTask(taskId = 1)))
}
@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
+
+ createController().init()
+
+ assertThat(thumbnailLoader.capturedTaskIds).containsExactly(2, 3)
+ }
+
+ @Test
fun initMultipleRecentTasksWithoutAppSelectorTask_bindsListInTheSameOrder() {
val tasks =
listOf(
@@ -78,7 +107,7 @@
)
taskListProvider.tasks = tasks
- controller.init()
+ createController().init()
verify(view)
.bind(
@@ -101,7 +130,7 @@
)
taskListProvider.tasks = tasks
- controller.init()
+ createController().init()
verify(view)
.bind(
@@ -124,7 +153,7 @@
)
taskListProvider.tasks = tasks
- controller.init()
+ createController().init()
verify(view)
.bind(
@@ -149,7 +178,7 @@
)
taskListProvider.tasks = tasks
- controller.init()
+ createController().init()
verify(view)
.bind(
@@ -176,11 +205,29 @@
taskListProvider.tasks = tasks
givenCaptureAllowed(isAllow = false)
- controller.init()
+ createController().init()
verify(view).bind(emptyList())
}
+ @Test
+ fun init_firstStart_logsAppSelectorDisplayed() {
+ val controller = createController(isFirstStart = true)
+
+ controller.init()
+
+ verify(logger).notifyPermissionProgress(STATE_APP_SELECTOR_DISPLAYED)
+ }
+
+ @Test
+ fun init_notFirstStart_doesNotLogAppSelectorDisplayed() {
+ val controller = createController(isFirstStart = false)
+
+ controller.init()
+
+ verify(logger, never()).notifyPermissionProgress(STATE_APP_SELECTOR_DISPLAYED)
+ }
+
private fun givenCaptureAllowed(isAllow: Boolean) {
whenever(policyResolver.isScreenCaptureAllowed(any(), any())).thenReturn(isAllow)
}
@@ -188,14 +235,17 @@
private fun createRecentTask(
taskId: Int,
topActivityComponent: ComponentName? = null,
- userId: Int = personalUserHandle.identifier
+ userId: Int = personalUserHandle.identifier,
+ isForegroundTask: Boolean = false
): RecentTask {
return RecentTask(
taskId = taskId,
+ displayId = 0,
topActivityComponent = topActivityComponent,
baseIntentComponent = ComponentName("com", "Test"),
userId = userId,
- colorBackground = 0
+ colorBackground = 0,
+ isForegroundTask = isForegroundTask,
)
}
@@ -205,4 +255,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..d75553f 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")
@@ -103,10 +128,12 @@
private fun createRecentTask(taskId: Int): RecentTask =
RecentTask(
taskId = taskId,
+ displayId = 0,
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/SettingObserverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/UserSettingObserverTest.kt
similarity index 88%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/SettingObserverTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/qs/UserSettingObserverTest.kt
index 4be6890..8f06fe2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/SettingObserverTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/UserSettingObserverTest.kt
@@ -35,7 +35,7 @@
@SmallTest
@RunWith(AndroidTestingRunner::class)
@TestableLooper.RunWithLooper
-class SettingObserverTest : SysuiTestCase() {
+class UserSettingObserverTest : SysuiTestCase() {
companion object {
private const val TEST_SETTING = "setting"
@@ -46,7 +46,7 @@
}
private lateinit var testableLooper: TestableLooper
- private lateinit var setting: SettingObserver
+ private lateinit var setting: UserSettingObserver
private lateinit var secureSettings: SecureSettings
private lateinit var callback: Callback
@@ -56,17 +56,19 @@
testableLooper = TestableLooper.get(this)
secureSettings = FakeSettings()
- setting = object : SettingObserver(
- secureSettings,
- Handler(testableLooper.looper),
- TEST_SETTING,
- USER,
- DEFAULT_VALUE
- ) {
- override fun handleValueChanged(value: Int, observedChange: Boolean) {
- callback(value, observedChange)
+ setting =
+ object :
+ UserSettingObserver(
+ secureSettings,
+ Handler(testableLooper.looper),
+ TEST_SETTING,
+ USER,
+ DEFAULT_VALUE
+ ) {
+ override fun handleValueChanged(value: Int, observedChange: Boolean) {
+ callback(value, observedChange)
+ }
}
- }
// Default empty callback
callback = { _, _ -> Unit }
@@ -162,4 +164,4 @@
setting.isListening = true
assertThat(setting.value).isEqualTo(DEFAULT_VALUE)
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTest.java
index 67587e3..6cc52d7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTest.java
@@ -29,12 +29,14 @@
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.anyString;
+import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.app.ActivityManager;
import android.app.compat.CompatChanges;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -73,16 +75,18 @@
@SmallTest
@RunWith(AndroidJUnit4.class)
public class TileLifecycleManagerTest extends SysuiTestCase {
- private static final int TEST_FAIL_TIMEOUT = 5000;
private final PackageManagerAdapter mMockPackageManagerAdapter =
mock(PackageManagerAdapter.class);
private final BroadcastDispatcher mMockBroadcastDispatcher =
mock(BroadcastDispatcher.class);
private final IQSTileService.Stub mMockTileService = mock(IQSTileService.Stub.class);
+ private final ActivityManager mActivityManager = mock(ActivityManager.class);
+
private ComponentName mTileServiceComponentName;
private Intent mTileServiceIntent;
private UserHandle mUser;
+ private FakeSystemClock mClock;
private FakeExecutor mExecutor;
private HandlerThread mThread;
private Handler mHandler;
@@ -112,13 +116,15 @@
mThread = new HandlerThread("TestThread");
mThread.start();
mHandler = Handler.createAsync(mThread.getLooper());
- mExecutor = new FakeExecutor(new FakeSystemClock());
+ mClock = new FakeSystemClock();
+ mExecutor = new FakeExecutor(mClock);
mStateManager = new TileLifecycleManager(mHandler, mWrappedContext,
mock(IQSService.class),
mMockPackageManagerAdapter,
mMockBroadcastDispatcher,
mTileServiceIntent,
mUser,
+ mActivityManager,
mExecutor);
}
@@ -294,11 +300,32 @@
mStateManager.onStartListening();
mStateManager.executeSetBindService(true);
mExecutor.runAllReady();
- mStateManager.setBindRetryDelay(0);
+ mStateManager.onServiceDisconnected(mTileServiceComponentName);
+ mClock.advanceTime(5000);
+
+ // Two calls: one for the first bind, one for the restart.
+ verifyBind(2);
+ verify(mMockTileService, times(2)).onStartListening();
+ }
+
+ @Test
+ public void testKillProcessLowMemory() throws Exception {
+ doAnswer(invocation -> {
+ ActivityManager.MemoryInfo memoryInfo = invocation.getArgument(0);
+ memoryInfo.lowMemory = true;
+ return null;
+ }).when(mActivityManager).getMemoryInfo(any());
+ mStateManager.onStartListening();
+ mStateManager.executeSetBindService(true);
mExecutor.runAllReady();
mStateManager.onServiceDisconnected(mTileServiceComponentName);
- mExecutor.runAllReady();
+ // Longer delay than a regular one
+ mClock.advanceTime(5000);
+ verifyBind(1);
+ verify(mMockTileService, times(1)).onStartListening();
+
+ mClock.advanceTime(20000);
// Two calls: one for the first bind, one for the restart.
verifyBind(2);
verify(mMockTileService, times(2)).onStartListening();
@@ -319,6 +346,7 @@
mMockBroadcastDispatcher,
mTileServiceIntent,
mUser,
+ mActivityManager,
mExecutor);
manager.executeSetBindService(true);
@@ -340,6 +368,7 @@
mMockBroadcastDispatcher,
mTileServiceIntent,
mUser,
+ mActivityManager,
mExecutor);
manager.executeSetBindService(true);
@@ -361,6 +390,7 @@
mMockBroadcastDispatcher,
mTileServiceIntent,
mUser,
+ mActivityManager,
mExecutor);
manager.executeSetBindService(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java
index 4bc16a5..d011821 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java
@@ -304,7 +304,7 @@
CustomTileAddedRepository customTileAddedRepository, DelayableExecutor executor) {
super(host, handlerProvider, broadcastDispatcher, userTracker, keyguardStateController,
commandQueue, statusBarIconController, panelInteractor,
- customTileAddedRepository, executor);
+ mTileLifecycleManagerFactory, customTileAddedRepository, executor);
}
@Override
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModelTest.kt
index 4ada44c..9b1f830 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModelTest.kt
@@ -25,7 +25,6 @@
import androidx.test.filters.SmallTest
import com.android.settingslib.Utils
import com.android.settingslib.drawable.UserIconDrawable
-import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.common.shared.model.ContentDescription
@@ -35,6 +34,7 @@
import com.android.systemui.qs.QSSecurityFooterUtils
import com.android.systemui.qs.footer.FooterActionsTestUtils
import com.android.systemui.qs.footer.domain.model.SecurityButtonConfig
+import com.android.systemui.res.R
import com.android.systemui.security.data.model.SecurityModel
import com.android.systemui.settings.FakeUserTracker
import com.android.systemui.statusbar.policy.FakeSecurityController
@@ -44,7 +44,7 @@
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.nullable
-import com.android.systemui.util.settings.FakeSettings
+import com.android.systemui.util.settings.FakeGlobalSettings
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.launch
@@ -128,7 +128,7 @@
fun userSwitcher() = runTest {
val picture: Drawable = mock()
val userInfoController = FakeUserInfoController(FakeInfo(picture = picture))
- val settings = FakeSettings()
+ val settings = FakeGlobalSettings()
val userId = 42
val userTracker = FakeUserTracker(userId)
val userSwitcherControllerWrapper =
@@ -167,14 +167,9 @@
// for the current user will be fired to notify us of that change.
isUserSwitcherEnabled = true
- // Update the setting for a random user: nothing should change, given that at this point we
- // weren't notified of the change yet.
- utils.setUserSwitcherEnabled(settings, true, 3)
- assertThat(currentUserSwitcher()).isNull()
-
// Update the setting for the observed user: now we will be notified and the button should
// be there.
- utils.setUserSwitcherEnabled(settings, true, userId)
+ utils.setUserSwitcherEnabled(settings, true)
val userSwitcher = currentUserSwitcher()
assertThat(userSwitcher).isNotNull()
assertThat(userSwitcher!!.icon)
@@ -372,9 +367,7 @@
),
)
- val job = launch {
- underTest.observeDeviceMonitoringDialogRequests(quickSettingsContext = mock())
- }
+ val job = launch { underTest.observeDeviceMonitoringDialogRequests(mock()) }
advanceUntilIdle()
assertThat(nDialogRequests).isEqualTo(3)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/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/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/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 2e16577..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
@@ -52,7 +54,6 @@
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.launch
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
@@ -138,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(
@@ -150,6 +152,7 @@
FakeConnectivityRepository(),
),
constants = mock(),
+ flags,
scope = testScope.backgroundScope,
)
@@ -463,8 +466,7 @@
fromScene = getCurrentSceneInUi(),
toScene = to.key,
progress = progressFlow,
- isInitiatedByUserInput = false,
- isUserInputOngoing = flowOf(false),
+ isUserInputDriven = false,
)
runCurrent()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt
index 740c6d9..432bd0f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt
@@ -29,7 +29,6 @@
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
@@ -120,8 +119,7 @@
fromScene = SceneKey.Lockscreen,
toScene = SceneKey.Shade,
progress = progress,
- isInitiatedByUserInput = false,
- isUserInputOngoing = flowOf(false),
+ isUserInputDriven = false,
)
assertThat(reflectedTransitionState).isEqualTo(transitionState.value)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt
index 31d26c0..8b23d18 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt
@@ -83,8 +83,7 @@
fromScene = SceneKey.Lockscreen,
toScene = SceneKey.Shade,
progress = progress,
- isInitiatedByUserInput = false,
- isUserInputOngoing = flowOf(false),
+ isUserInputDriven = false,
)
assertThat(reflectedTransitionState).isEqualTo(transitionState.value)
@@ -122,8 +121,7 @@
fromScene = underTest.desiredScene.value.key,
toScene = SceneKey.Shade,
progress = progress,
- isInitiatedByUserInput = false,
- isUserInputOngoing = flowOf(false),
+ isUserInputDriven = false,
)
assertThat(transitionTo).isEqualTo(SceneKey.Shade)
@@ -160,8 +158,7 @@
fromScene = SceneKey.Gone,
toScene = SceneKey.Lockscreen,
progress = flowOf(0.5f),
- isInitiatedByUserInput = false,
- isUserInputOngoing = flowOf(false),
+ isUserInputDriven = false,
)
)
val transitioning by
@@ -180,8 +177,7 @@
fromScene = SceneKey.Shade,
toScene = SceneKey.QuickSettings,
progress = flowOf(0.5f),
- isInitiatedByUserInput = false,
- isUserInputOngoing = flowOf(false),
+ isUserInputDriven = false,
)
)
underTest.setTransitionState(transitionState)
@@ -198,8 +194,7 @@
fromScene = SceneKey.Shade,
toScene = SceneKey.Lockscreen,
progress = flowOf(0.5f),
- isInitiatedByUserInput = false,
- isUserInputOngoing = flowOf(false),
+ isUserInputDriven = false,
)
)
val transitioning by
@@ -227,8 +222,7 @@
fromScene = SceneKey.Shade,
toScene = SceneKey.Lockscreen,
progress = flowOf(0.5f),
- isInitiatedByUserInput = false,
- isUserInputOngoing = flowOf(false),
+ isUserInputDriven = false,
)
assertThat(transitioning).isTrue()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
index 3b9621e..7b13de6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
@@ -109,8 +109,7 @@
fromScene = SceneKey.Gone,
toScene = SceneKey.Shade,
progress = flowOf(0.5f),
- isInitiatedByUserInput = false,
- isUserInputOngoing = flowOf(false),
+ isUserInputDriven = false,
)
assertThat(isVisible).isTrue()
sceneInteractor.onSceneChanged(SceneModel(SceneKey.Shade), "reason")
@@ -123,8 +122,7 @@
fromScene = SceneKey.Shade,
toScene = SceneKey.Gone,
progress = flowOf(0.5f),
- isInitiatedByUserInput = false,
- isUserInputOngoing = flowOf(false),
+ isUserInputDriven = false,
)
assertThat(isVisible).isTrue()
sceneInteractor.onSceneChanged(SceneModel(SceneKey.Gone), "reason")
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java
index 6e6833d..90d2e78 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java
@@ -17,8 +17,10 @@
package com.android.systemui.screenrecord;
import static com.google.common.truth.Truth.assertThat;
+
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
+
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verify;
@@ -37,6 +39,8 @@
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.flags.FakeFeatureFlags;
import com.android.systemui.flags.Flags;
+import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger;
+import com.android.systemui.mediaprojection.SessionCreationSource;
import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDevicePolicyResolver;
import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDisabledDialog;
import com.android.systemui.plugins.ActivityStarter;
@@ -76,6 +80,8 @@
private ActivityStarter mActivityStarter;
@Mock
private UserTracker mUserTracker;
+ @Mock
+ private MediaProjectionMetricsLogger mMediaProjectionMetricsLogger;
private FakeFeatureFlags mFeatureFlags;
private RecordingController mController;
@@ -86,8 +92,15 @@
public void setUp() {
MockitoAnnotations.initMocks(this);
mFeatureFlags = new FakeFeatureFlags();
- mController = new RecordingController(mMainExecutor, mBroadcastDispatcher, mContext,
- mFeatureFlags, mUserContextProvider, () -> mDevicePolicyResolver, mUserTracker);
+ mController = new RecordingController(
+ mMainExecutor,
+ mBroadcastDispatcher,
+ mContext,
+ mFeatureFlags,
+ mUserContextProvider,
+ () -> mDevicePolicyResolver,
+ mUserTracker,
+ mMediaProjectionMetricsLogger);
mController.addCallback(mCallback);
}
@@ -269,4 +282,21 @@
assertThat(dialog).isInstanceOf(ScreenRecordPermissionDialog.class);
}
+
+ @Test
+ public void testPoliciesFlagEnabled_screenCapturingAllowed_logsProjectionInitiated() {
+ if (Looper.myLooper() == null) {
+ Looper.prepare();
+ }
+
+ mFeatureFlags.set(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING, true);
+ mFeatureFlags.set(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING_ENTERPRISE_POLICIES, true);
+ when(mDevicePolicyResolver.isScreenCaptureCompletelyDisabled((any()))).thenReturn(false);
+
+ mController.createScreenRecordDialog(mContext, mFeatureFlags,
+ mDialogLaunchAnimator, mActivityStarter, /* onStartRecordingClicked= */ null);
+
+ verify(mMediaProjectionMetricsLogger)
+ .notifyProjectionInitiated(SessionCreationSource.SYSTEM_UI_SCREEN_RECORDER);
+ }
}
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/RequestProcessorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/RequestProcessorTest.kt
index 0d694ee..7e41745 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/RequestProcessorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/RequestProcessorTest.kt
@@ -28,7 +28,6 @@
import android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN
import android.view.WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE
import com.android.internal.util.ScreenshotRequest
-import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.screenshot.ScreenshotPolicy.DisplayContentInfo
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.CoroutineScope
@@ -47,7 +46,6 @@
private val scope = CoroutineScope(Dispatchers.Unconfined)
private val policy = FakeScreenshotPolicy()
- private val flags = FakeFeatureFlags()
/** Tests the Java-compatible function wrapper, ensures callback is invoked. */
@Test
@@ -58,7 +56,7 @@
.setBitmap(Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888))
.build()
)
- val processor = RequestProcessor(imageCapture, policy, flags, scope)
+ val processor = RequestProcessor(imageCapture, policy, scope)
var result: ScreenshotData? = null
var callbackCount = 0
@@ -86,7 +84,7 @@
val request =
ScreenshotRequest.Builder(TAKE_SCREENSHOT_FULLSCREEN, SCREENSHOT_OTHER).build()
- val processor = RequestProcessor(imageCapture, policy, flags, scope)
+ val processor = RequestProcessor(imageCapture, policy, scope)
val processedData = processor.process(ScreenshotData.fromRequest(request))
@@ -111,7 +109,7 @@
val request =
ScreenshotRequest.Builder(TAKE_SCREENSHOT_FULLSCREEN, SCREENSHOT_KEY_OTHER).build()
- val processor = RequestProcessor(imageCapture, policy, flags, scope)
+ val processor = RequestProcessor(imageCapture, policy, scope)
val processedData = processor.process(ScreenshotData.fromRequest(request))
@@ -138,7 +136,7 @@
val request =
ScreenshotRequest.Builder(TAKE_SCREENSHOT_FULLSCREEN, SCREENSHOT_KEY_OTHER).build()
- val processor = RequestProcessor(imageCapture, policy, flags, scope)
+ val processor = RequestProcessor(imageCapture, policy, scope)
Assert.assertThrows(IllegalStateException::class.java) {
runBlocking { processor.process(ScreenshotData.fromRequest(request)) }
@@ -148,7 +146,7 @@
@Test
fun testProvidedImageScreenshot() = runBlocking {
val bounds = Rect(50, 50, 150, 150)
- val processor = RequestProcessor(imageCapture, policy, flags, scope)
+ val processor = RequestProcessor(imageCapture, policy, scope)
policy.setManagedProfile(USER_ID, false)
@@ -173,7 +171,7 @@
@Test
fun testProvidedImageScreenshot_managedProfile() = runBlocking {
val bounds = Rect(50, 50, 150, 150)
- val processor = RequestProcessor(imageCapture, policy, flags, scope)
+ val processor = RequestProcessor(imageCapture, policy, scope)
// Indicate that the screenshot belongs to a manged profile
policy.setManagedProfile(USER_ID, true)
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..3dc9037 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotExecutorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotExecutorTest.kt
@@ -25,6 +25,7 @@
import com.android.systemui.util.mockito.nullable
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
+import java.lang.IllegalStateException
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runCurrent
@@ -43,8 +44,11 @@
private val controller0 = mock<ScreenshotController>()
private val controller1 = mock<ScreenshotController>()
+ private val notificationsController0 = mock<ScreenshotNotificationsController>()
+ private val notificationsController1 = mock<ScreenshotNotificationsController>()
private val controllerFactory = mock<ScreenshotController.Factory>()
private val callback = mock<TakeScreenshotService.RequestCallback>()
+ private val notificationControllerFactory = mock<ScreenshotNotificationsController.Factory>()
private val fakeDisplayRepository = FakeDisplayRepository()
private val requestProcessor = FakeRequestProcessor()
@@ -59,12 +63,15 @@
testScope,
requestProcessor,
eventLogger,
+ notificationControllerFactory
)
@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)
+ whenever(notificationControllerFactory.create(eq(0))).thenReturn(notificationsController0)
+ whenever(notificationControllerFactory.create(eq(1))).thenReturn(notificationsController1)
}
@Test
@@ -74,8 +81,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 +114,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 +146,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),
@@ -310,6 +317,123 @@
screenshotExecutor.onDestroy()
}
+ @Test
+ fun executeScreenshots_errorFromProcessor_logsScreenshotRequested() =
+ testScope.runTest {
+ setDisplays(display(TYPE_INTERNAL, id = 0), display(TYPE_EXTERNAL, id = 1))
+ val onSaved = { _: Uri -> }
+ requestProcessor.shouldThrowException = true
+
+ screenshotExecutor.executeScreenshots(createScreenshotRequest(), onSaved, callback)
+
+ val screenshotRequested =
+ eventLogger.logs.filter {
+ it.eventId == ScreenshotEvent.SCREENSHOT_REQUESTED_KEY_OTHER.id
+ }
+ assertThat(screenshotRequested).hasSize(2)
+ screenshotExecutor.onDestroy()
+ }
+
+ @Test
+ fun executeScreenshots_errorFromProcessor_logsUiError() =
+ testScope.runTest {
+ setDisplays(display(TYPE_INTERNAL, id = 0), display(TYPE_EXTERNAL, id = 1))
+ val onSaved = { _: Uri -> }
+ requestProcessor.shouldThrowException = true
+
+ screenshotExecutor.executeScreenshots(createScreenshotRequest(), onSaved, callback)
+
+ val screenshotRequested =
+ eventLogger.logs.filter {
+ it.eventId == ScreenshotEvent.SCREENSHOT_CAPTURE_FAILED.id
+ }
+ assertThat(screenshotRequested).hasSize(2)
+ screenshotExecutor.onDestroy()
+ }
+
+ @Test
+ fun executeScreenshots_errorFromProcessorOnDefaultDisplay_showsErrorNotification() =
+ testScope.runTest {
+ setDisplays(display(TYPE_INTERNAL, id = 0), display(TYPE_EXTERNAL, id = 1))
+ val onSaved = { _: Uri -> }
+ requestProcessor.shouldThrowException = true
+
+ screenshotExecutor.executeScreenshots(createScreenshotRequest(), onSaved, callback)
+
+ verify(notificationsController0).notifyScreenshotError(any())
+ screenshotExecutor.onDestroy()
+ }
+
+ @Test
+ fun executeScreenshots_errorFromProcessorOnSecondaryDisplay_showsErrorNotification() =
+ testScope.runTest {
+ setDisplays(display(TYPE_INTERNAL, id = 0))
+ val onSaved = { _: Uri -> }
+ requestProcessor.shouldThrowException = true
+
+ screenshotExecutor.executeScreenshots(createScreenshotRequest(), onSaved, callback)
+
+ verify(notificationsController0).notifyScreenshotError(any())
+ screenshotExecutor.onDestroy()
+ }
+
+ @Test
+ fun executeScreenshots_errorFromScreenshotController_reportsRequested() =
+ testScope.runTest {
+ setDisplays(display(TYPE_INTERNAL, id = 0), display(TYPE_EXTERNAL, id = 1))
+ val onSaved = { _: Uri -> }
+ whenever(controller0.handleScreenshot(any(), any(), any()))
+ .thenThrow(IllegalStateException::class.java)
+ whenever(controller1.handleScreenshot(any(), any(), any()))
+ .thenThrow(IllegalStateException::class.java)
+
+ screenshotExecutor.executeScreenshots(createScreenshotRequest(), onSaved, callback)
+
+ val screenshotRequested =
+ eventLogger.logs.filter {
+ it.eventId == ScreenshotEvent.SCREENSHOT_REQUESTED_KEY_OTHER.id
+ }
+ assertThat(screenshotRequested).hasSize(2)
+ screenshotExecutor.onDestroy()
+ }
+
+ @Test
+ fun executeScreenshots_errorFromScreenshotController_reportsError() =
+ testScope.runTest {
+ setDisplays(display(TYPE_INTERNAL, id = 0), display(TYPE_EXTERNAL, id = 1))
+ val onSaved = { _: Uri -> }
+ whenever(controller0.handleScreenshot(any(), any(), any()))
+ .thenThrow(IllegalStateException::class.java)
+ whenever(controller1.handleScreenshot(any(), any(), any()))
+ .thenThrow(IllegalStateException::class.java)
+
+ screenshotExecutor.executeScreenshots(createScreenshotRequest(), onSaved, callback)
+
+ val screenshotRequested =
+ eventLogger.logs.filter {
+ it.eventId == ScreenshotEvent.SCREENSHOT_CAPTURE_FAILED.id
+ }
+ assertThat(screenshotRequested).hasSize(2)
+ screenshotExecutor.onDestroy()
+ }
+
+ @Test
+ fun executeScreenshots_errorFromScreenshotController_showsErrorNotification() =
+ testScope.runTest {
+ setDisplays(display(TYPE_INTERNAL, id = 0), display(TYPE_EXTERNAL, id = 1))
+ val onSaved = { _: Uri -> }
+ whenever(controller0.handleScreenshot(any(), any(), any()))
+ .thenThrow(IllegalStateException::class.java)
+ whenever(controller1.handleScreenshot(any(), any(), any()))
+ .thenThrow(IllegalStateException::class.java)
+
+ screenshotExecutor.executeScreenshots(createScreenshotRequest(), onSaved, callback)
+
+ verify(notificationsController0).notifyScreenshotError(any())
+ verify(notificationsController1).notifyScreenshotError(any())
+ screenshotExecutor.onDestroy()
+ }
+
private suspend fun TestScope.setDisplays(vararg displays: Display) {
fakeDisplayRepository.emit(displays.toSet())
runCurrent()
@@ -328,8 +452,9 @@
private class FakeRequestProcessor : ScreenshotRequestProcessor {
var processed: ScreenshotData? = null
var toReturn: ScreenshotData? = null
-
+ var shouldThrowException = false
override suspend fun process(screenshot: ScreenshotData): ScreenshotData {
+ if (shouldThrowException) throw RequestProcessorException("")
processed = screenshot
return toReturn ?: screenshot
}
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..f3809aa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotServiceTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotServiceTest.kt
@@ -65,6 +65,7 @@
private val requestProcessor = mock<RequestProcessor>()
private val devicePolicyManager = mock<DevicePolicyManager>()
private val devicePolicyResourcesManager = mock<DevicePolicyResourcesManager>()
+ private val notificationsControllerFactory = mock<ScreenshotNotificationsController.Factory>()
private val notificationsController = mock<ScreenshotNotificationsController>()
private val callback = mock<RequestCallback>()
@@ -86,7 +87,8 @@
)
.thenReturn(false)
whenever(userManager.isUserUnlocked).thenReturn(true)
- whenever(controllerFactory.create(any())).thenReturn(controller)
+ whenever(controllerFactory.create(any(), any())).thenReturn(controller)
+ whenever(notificationsControllerFactory.create(any())).thenReturn(notificationsController)
// Stub request processor as a synchronous no-op for tests with the flag enabled
doAnswer {
@@ -323,7 +325,7 @@
userManager,
devicePolicyManager,
eventLogger,
- notificationsController,
+ notificationsControllerFactory,
mContext,
Runnable::run,
flags,
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 8d8c70e..6223e25 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
@@ -157,6 +157,7 @@
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;
@@ -328,6 +329,7 @@
@Mock private CastController mCastController;
@Mock private KeyguardRootView mKeyguardRootView;
@Mock private SharedNotificationContainerInteractor mSharedNotificationContainerInteractor;
+ @Mock private KeyguardClockPositionAlgorithm mKeyguardClockPositionAlgorithm;
protected final int mMaxUdfpsBurnInOffsetY = 5;
protected KeyguardBottomAreaInteractor mKeyguardBottomAreaInteractor;
@@ -388,6 +390,7 @@
mFeatureFlags,
mInteractionJankMonitor,
mKeyguardInteractor,
+ mKeyguardTransitionInteractor,
mDumpManager,
mPowerInteractor));
@@ -667,7 +670,8 @@
mKeyguardViewConfigurator,
mKeyguardFaceAuthInteractor,
new ResourcesSplitShadeStateController(),
- mPowerInteractor);
+ mPowerInteractor,
+ mKeyguardClockPositionAlgorithm);
mNotificationPanelViewController.initDependencies(
mCentralSurfaces,
null,
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 b4f9e8d..677d9db 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
@@ -49,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
@@ -144,7 +144,7 @@
@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()
@@ -251,7 +251,7 @@
securityModel = mock(KeyguardSecurityModel::class.java),
),
BouncerLogger(logcatLogBuffer("BouncerLog")),
- keyEventInteractor,
+ sysUIKeyEventHandler,
primaryBouncerInteractor,
alternateBouncerInteractor,
)
@@ -475,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 189c9e2..a4a2ca0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
@@ -48,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
@@ -253,7 +253,7 @@
securityModel = Mockito.mock(KeyguardSecurityModel::class.java),
),
BouncerLogger(logcatLogBuffer("BouncerLog")),
- Mockito.mock(KeyEventInteractor::class.java),
+ Mockito.mock(SysUIKeyEventHandler::class.java),
primaryBouncerInteractor,
alternateBouncerInteractor,
)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt
index bcb060d..81382a4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt
@@ -60,7 +60,6 @@
import dagger.Component
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
@@ -595,8 +594,7 @@
fromScene = SceneKey.Lockscreen,
toScene = key,
progress = progress,
- isInitiatedByUserInput = false,
- isUserInputOngoing = flowOf(false),
+ isUserInputDriven = false,
)
)
sceneInteractor.setTransitionState(transitionState)
@@ -633,8 +631,7 @@
fromScene = key,
toScene = SceneKey.Lockscreen,
progress = progress,
- isInitiatedByUserInput = false,
- isUserInputOngoing = flowOf(false),
+ isUserInputDriven = false,
)
)
sceneInteractor.setTransitionState(transitionState)
@@ -670,8 +667,7 @@
fromScene = SceneKey.Lockscreen,
toScene = SceneKey.Shade,
progress = progress,
- isInitiatedByUserInput = false,
- isUserInputOngoing = flowOf(false),
+ isUserInputDriven = false,
)
)
sceneInteractor.setTransitionState(transitionState)
@@ -947,8 +943,7 @@
fromScene = SceneKey.Lockscreen,
toScene = key,
progress = progress,
- isInitiatedByUserInput = false,
- isUserInputOngoing = flowOf(false),
+ isUserInputDriven = false,
)
)
sceneInteractor.setTransitionState(transitionState)
@@ -985,8 +980,7 @@
fromScene = SceneKey.Lockscreen,
toScene = key,
progress = progress,
- isInitiatedByUserInput = true,
- isUserInputOngoing = flowOf(false),
+ isUserInputDriven = true,
)
)
sceneInteractor.setTransitionState(transitionState)
@@ -1023,8 +1017,7 @@
fromScene = key,
toScene = SceneKey.Lockscreen,
progress = progress,
- isInitiatedByUserInput = false,
- isUserInputOngoing = flowOf(false),
+ isUserInputDriven = false,
)
)
sceneInteractor.setTransitionState(transitionState)
@@ -1061,8 +1054,7 @@
fromScene = key,
toScene = SceneKey.Lockscreen,
progress = progress,
- isInitiatedByUserInput = true,
- isUserInputOngoing = flowOf(false),
+ isUserInputDriven = true,
)
)
sceneInteractor.setTransitionState(transitionState)
@@ -1097,9 +1089,8 @@
ObservableTransitionState.Transition(
fromScene = SceneKey.Lockscreen,
toScene = SceneKey.QuickSettings,
- progress = MutableStateFlow(0f),
- isInitiatedByUserInput = true,
- isUserInputOngoing = flowOf(false),
+ progress = progress,
+ isUserInputDriven = true,
)
)
sceneInteractor.setTransitionState(transitionState)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt
index bb20d94..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
@@ -17,7 +19,6 @@
import com.android.systemui.util.mockito.mock
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
@@ -32,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(
@@ -44,6 +46,7 @@
FakeConnectivityRepository(),
),
constants = mock(),
+ flags,
scope = testScope.backgroundScope,
)
@@ -85,8 +88,7 @@
fromScene = SceneKey.Shade,
toScene = SceneKey.QuickSettings,
progress = MutableStateFlow(0.5f),
- isInitiatedByUserInput = false,
- isUserInputOngoing = flowOf(false),
+ isUserInputDriven = false,
)
)
)
@@ -104,8 +106,7 @@
fromScene = SceneKey.QuickSettings,
toScene = SceneKey.Shade,
progress = MutableStateFlow(0.5f),
- isInitiatedByUserInput = false,
- isUserInputOngoing = flowOf(false),
+ isUserInputDriven = false,
)
)
)
@@ -123,8 +124,7 @@
fromScene = SceneKey.Gone,
toScene = SceneKey.Shade,
progress = MutableStateFlow(0.5f),
- isInitiatedByUserInput = false,
- isUserInputOngoing = flowOf(false),
+ isUserInputDriven = false,
)
)
)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/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/statusbar/MediaArtworkProcessorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/MediaArtworkProcessorTest.kt
deleted file mode 100644
index e4da53a..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/MediaArtworkProcessorTest.kt
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.systemui.statusbar
-
-import com.google.common.truth.Truth.assertThat
-
-import android.graphics.Bitmap
-import android.graphics.Canvas
-import android.graphics.Color
-import android.graphics.Point
-import android.testing.AndroidTestingRunner
-import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import org.junit.After
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-
-private const val WIDTH = 200
-private const val HEIGHT = 200
-
-@RunWith(AndroidTestingRunner::class)
-@SmallTest
-class MediaArtworkProcessorTest : SysuiTestCase() {
-
- private var screenWidth = 0
- private var screenHeight = 0
-
- private lateinit var processor: MediaArtworkProcessor
-
- @Before
- fun setUp() {
- processor = MediaArtworkProcessor()
-
- val point = Point()
- checkNotNull(context.display).getSize(point)
- screenWidth = point.x
- screenHeight = point.y
- }
-
- @After
- fun tearDown() {
- processor.clearCache()
- }
-
- @Test
- fun testProcessArtwork() {
- // GIVEN some "artwork", which is just a solid blue image
- val artwork = Bitmap.createBitmap(WIDTH, HEIGHT, Bitmap.Config.ARGB_8888)
- Canvas(artwork).drawColor(Color.BLUE)
- // WHEN the background is created from the artwork
- val background = processor.processArtwork(context, artwork)!!
- // THEN the background has the size of the screen that has been downsamples
- assertThat(background.height).isLessThan(screenHeight)
- assertThat(background.width).isLessThan(screenWidth)
- assertThat(background.config).isEqualTo(Bitmap.Config.ARGB_8888)
- }
-
- @Test
- fun testCache() {
- // GIVEN a solid blue image
- val artwork = Bitmap.createBitmap(WIDTH, HEIGHT, Bitmap.Config.ARGB_8888)
- Canvas(artwork).drawColor(Color.BLUE)
- // WHEN the background is processed twice
- val background1 = processor.processArtwork(context, artwork)!!
- val background2 = processor.processArtwork(context, artwork)!!
- // THEN the two bitmaps are the same
- // Note: This is currently broken and trying to use caching causes issues
- assertThat(background1).isNotSameInstanceAs(background2)
- }
-
- @Test
- fun testConfig() {
- // GIVEN some which is not ARGB_8888
- val artwork = Bitmap.createBitmap(WIDTH, HEIGHT, Bitmap.Config.ALPHA_8)
- Canvas(artwork).drawColor(Color.BLUE)
- // WHEN the background is created from the artwork
- val background = processor.processArtwork(context, artwork)!!
- // THEN the background has Config ARGB_8888
- assertThat(background.config).isEqualTo(Bitmap.Config.ARGB_8888)
- }
-
- @Test
- fun testRecycledArtwork() {
- // GIVEN some "artwork", which is just a solid blue image
- val artwork = Bitmap.createBitmap(WIDTH, HEIGHT, Bitmap.Config.ARGB_8888)
- Canvas(artwork).drawColor(Color.BLUE)
- // AND the artwork is recycled
- artwork.recycle()
- // WHEN the background is created from the artwork
- val background = processor.processArtwork(context, artwork)
- // THEN the processed bitmap is null
- assertThat(background).isNull()
- }
-}
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/NotificationMediaManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationMediaManagerTest.kt
index 9d6ea85..cfcf425 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationMediaManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationMediaManagerTest.kt
@@ -48,9 +48,7 @@
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
- doCallRealMethod()
- .whenever(notificationMediaManager)
- .updateMediaMetaData(anyBoolean(), anyBoolean())
+ doCallRealMethod().whenever(notificationMediaManager).updateMediaMetaData(anyBoolean())
doReturn(mockBackDropView).whenever(notificationMediaManager).backDropView
}
@@ -62,7 +60,7 @@
notificationMediaManager.mIsLockscreenLiveWallpaperEnabled = true
for (metaDataChanged in listOf(true, false)) {
for (allowEnterAnimation in listOf(true, false)) {
- notificationMediaManager.updateMediaMetaData(metaDataChanged, allowEnterAnimation)
+ notificationMediaManager.updateMediaMetaData(metaDataChanged)
verify(notificationMediaManager, never()).mediaMetadata
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/MediaNotificationProcessorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/MediaNotificationProcessorTest.java
deleted file mode 100644
index aeb5b03..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/MediaNotificationProcessorTest.java
+++ /dev/null
@@ -1,87 +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.systemui.statusbar.notification;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.annotation.Nullable;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.test.suitebuilder.annotation.SmallTest;
-
-import androidx.palette.graphics.Palette;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.systemui.SysuiTestCase;
-
-import org.junit.After;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class MediaNotificationProcessorTest extends SysuiTestCase {
-
- private static final int BITMAP_WIDTH = 10;
- private static final int BITMAP_HEIGHT = 10;
-
- /**
- * Color tolerance is borrowed from the AndroidX test utilities for Palette.
- */
- private static final int COLOR_TOLERANCE = 8;
-
- @Nullable private Bitmap mArtwork;
-
- @After
- public void tearDown() {
- if (mArtwork != null) {
- mArtwork.recycle();
- mArtwork = null;
- }
- }
-
- @Test
- public void findBackgroundSwatch_white() {
- // Given artwork that is completely white.
- mArtwork = Bitmap.createBitmap(BITMAP_WIDTH, BITMAP_HEIGHT, Bitmap.Config.ARGB_8888);
- Canvas canvas = new Canvas(mArtwork);
- canvas.drawColor(Color.WHITE);
- // WHEN the background swatch is computed
- Palette.Swatch swatch = MediaNotificationProcessor.findBackgroundSwatch(mArtwork);
- // THEN the swatch color is white
- assertCloseColors(swatch.getRgb(), Color.WHITE);
- }
-
- @Test
- public void findBackgroundSwatch_red() {
- // Given artwork that is completely red.
- mArtwork = Bitmap.createBitmap(BITMAP_WIDTH, BITMAP_HEIGHT, Bitmap.Config.ARGB_8888);
- Canvas canvas = new Canvas(mArtwork);
- canvas.drawColor(Color.RED);
- // WHEN the background swatch is computed
- Palette.Swatch swatch = MediaNotificationProcessor.findBackgroundSwatch(mArtwork);
- // THEN the swatch color is red
- assertCloseColors(swatch.getRgb(), Color.RED);
- }
-
- static void assertCloseColors(int expected, int actual) {
- assertThat((float) Color.red(expected)).isWithin(COLOR_TOLERANCE).of(Color.red(actual));
- assertThat((float) Color.green(expected)).isWithin(COLOR_TOLERANCE).of(Color.green(actual));
- assertThat((float) Color.blue(expected)).isWithin(COLOR_TOLERANCE).of(Color.blue(actual));
- }
-}
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/wrapper/NotificationTemplateViewWrapperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapperTest.kt
index 8c3bfd5..f7632aa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapperTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapperTest.kt
@@ -30,9 +30,12 @@
import androidx.test.filters.SmallTest
import com.android.internal.R
import com.android.systemui.SysuiTestCase
+import com.android.systemui.TestUiOffloadThread
+import com.android.systemui.UiOffloadThread
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.row.NotificationTestHelper
import com.android.systemui.statusbar.notification.row.wrapper.NotificationTemplateViewWrapper.ActionPendingIntentCancellationHandler
+import com.android.systemui.util.leak.ReferenceTestUtils.waitForCondition
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
@@ -60,6 +63,12 @@
fun setUp() {
looper = TestableLooper.get(this)
allowTestableLooperAsMainThread()
+ // Use main thread instead of UI offload thread to fix flakes.
+ mDependency.injectTestDependency(
+ UiOffloadThread::class.java,
+ TestUiOffloadThread(looper.looper)
+ )
+
helper = NotificationTestHelper(mContext, mDependency, looper)
row = helper.createRow()
// Some code in the view iterates through parents so we need some extra containers around
@@ -88,12 +97,11 @@
val action2 = createActionWithPendingIntent()
val action3 = createActionWithPendingIntent()
wrapper.onContentUpdated(row)
- waitForUiOffloadThread() // Wait for cancellation registration to execute.
val pi3 = getPendingIntent(action3)
pi3.cancel()
- looper.processAllMessages() // Wait for listener callbacks to execute
+ waitForActionDisabled(action3)
assertThat(action1.isEnabled).isTrue()
assertThat(action2.isEnabled).isTrue()
assertThat(action3.isEnabled).isFalse()
@@ -109,12 +117,12 @@
val wrapper = NotificationTemplateViewWrapper(mContext, view, row)
val action = createActionWithPendingIntent()
wrapper.onContentUpdated(row)
- waitForUiOffloadThread() // Wait for cancellation registration to execute.
// Cancel the intent and check action is now false.
val pi = getPendingIntent(action)
pi.cancel()
- looper.processAllMessages() // Wait for listener callbacks to execute
+
+ waitForActionDisabled(action)
assertThat(action.isEnabled).isFalse()
// Create a NEW action and make sure that one will also be cancelled with same PI.
@@ -134,12 +142,13 @@
val action2 = createActionWithPendingIntent()
val action3 = createActionWithPendingIntent(getPendingIntent(action2))
wrapper.onContentUpdated(row)
- waitForUiOffloadThread() // Wait for cancellation registration to execute.
+ looper.processAllMessages()
val pi = getPendingIntent(action2)
pi.cancel()
- looper.processAllMessages() // Wait for listener callbacks to execute
+ waitForActionDisabled(action2)
+ waitForActionDisabled(action3)
assertThat(action1.isEnabled).isTrue()
assertThat(action2.isEnabled).isFalse()
assertThat(action3.isEnabled).isFalse()
@@ -152,10 +161,12 @@
val action = createActionWithPendingIntent()
wrapper.onContentUpdated(row)
getPendingIntent(action).cancel()
- ViewUtils.attachView(root)
- waitForUiOffloadThread()
looper.processAllMessages()
+ ViewUtils.attachView(root)
+ looper.processAllMessages()
+
+ waitForActionDisabled(action)
assertThat(action.isEnabled).isFalse()
}
@@ -173,7 +184,6 @@
val wrapper = NotificationTemplateViewWrapper(mContext, view, row)
wrapper.onContentUpdated(row)
ViewUtils.detachView(root)
- waitForUiOffloadThread()
looper.processAllMessages()
val captor = ArgumentCaptor.forClass(CancelListener::class.java)
@@ -194,7 +204,6 @@
val action = createActionWithPendingIntent(spy)
val wrapper = NotificationTemplateViewWrapper(mContext, view, row)
wrapper.onContentUpdated(row)
- waitForUiOffloadThread()
looper.processAllMessages()
// Grab set attach listener
@@ -213,7 +222,6 @@
)
action.setTagInternal(R.id.pending_intent_tag, newPi)
wrapper.onContentUpdated(row)
- waitForUiOffloadThread()
looper.processAllMessages()
// Listeners for original pending intent need to be cleaned up now.
@@ -251,4 +259,11 @@
assertThat(pendingIntent).isNotNull()
return pendingIntent
}
+
+ private fun waitForActionDisabled(action: View) {
+ waitForCondition {
+ looper.processAllMessages()
+ !action.isEnabled
+ }
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index a2be8b0..033c96a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -164,6 +164,7 @@
mFeatureFlags.setDefault(Flags.ANIMATED_NOTIFICATION_SHADE_INSETS);
mFeatureFlags.setDefault(Flags.NOTIFICATION_SHELF_REFACTOR);
mFeatureFlags.setDefault(Flags.NEW_AOD_TRANSITION);
+ mFeatureFlags.setDefault(Flags.UNCLEARED_TRANSIENT_HUN_FIX);
// Inject dependencies before initializing the layout
mDependency.injectTestDependency(FeatureFlags.class, mFeatureFlags);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
index 416694b..1d8a346 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
@@ -50,15 +50,15 @@
import androidx.test.filters.SmallTest;
-import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dagger.NightDisplayListenerModule;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.qs.AutoAddTracker;
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.ReduceBrightColorsController;
-import com.android.systemui.qs.SettingObserver;
+import com.android.systemui.qs.UserSettingObserver;
import com.android.systemui.qs.external.CustomTile;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.policy.CastController;
import com.android.systemui.statusbar.policy.CastController.CastDevice;
import com.android.systemui.statusbar.policy.DataSaverController;
@@ -288,7 +288,7 @@
inOrderSafety.verify(mSafetyController).removeCallback(any());
inOrderSafety.verify(mSafetyController).addCallback(any());
- SettingObserver setting = mAutoTileManager.getSecureSettingForKey(TEST_SETTING);
+ UserSettingObserver setting = mAutoTileManager.getSecureSettingForKey(TEST_SETTING);
assertEquals(USER + 1, setting.getCurrentUser());
assertTrue(setting.isListening());
}
@@ -342,7 +342,7 @@
inOrderSafety.verify(mSafetyController).removeCallback(any());
inOrderSafety.verify(mSafetyController).addCallback(any());
- SettingObserver setting = mAutoTileManager.getSecureSettingForKey(TEST_SETTING);
+ UserSettingObserver setting = mAutoTileManager.getSecureSettingForKey(TEST_SETTING);
assertEquals(USER + 1, setting.getCurrentUser());
assertFalse(setting.isListening());
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
index f18af61..c8cbe42 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
@@ -18,6 +18,8 @@
import static android.app.NotificationManager.IMPORTANCE_HIGH;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
+import static android.app.StatusBarManager.WINDOW_STATE_HIDDEN;
+import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
import static com.android.systemui.statusbar.StatusBarState.SHADE;
@@ -1110,6 +1112,16 @@
// THEN no NPE when fingerprintManager is null
}
+ @Test
+ public void bubbleBarVisibility() {
+ createCentralSurfaces();
+ mCentralSurfaces.onStatusBarWindowStateChanged(WINDOW_STATE_HIDDEN);
+ verify(mBubbles).onStatusBarVisibilityChanged(false);
+
+ mCentralSurfaces.onStatusBarWindowStateChanged(WINDOW_STATE_SHOWING);
+ verify(mBubbles).onStatusBarVisibilityChanged(true);
+ }
+
/**
* Configures the appropriate mocks and then calls {@link CentralSurfacesImpl#updateIsKeyguard}
* to reconfigure the keyguard to reflect the requested showing/occluded states.
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/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 b36d09d..45e9224 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -19,8 +19,6 @@
import static com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants.EXPANSION_HIDDEN;
import static com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants.EXPANSION_VISIBLE;
-import static kotlinx.coroutines.test.TestCoroutineDispatchersKt.StandardTestDispatcher;
-
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
@@ -37,6 +35,8 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import static kotlinx.coroutines.test.TestCoroutineDispatchersKt.StandardTestDispatcher;
+
import android.service.trust.TrustAgentService;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -175,7 +175,6 @@
mFeatureFlags = new FakeFeatureFlags();
mFeatureFlags.set(Flags.WM_ENABLE_PREDICTIVE_BACK_BOUNCER_ANIM, true);
mFeatureFlags.set(Flags.REFACTOR_KEYGUARD_DISMISS_INTENT, false);
- mFeatureFlags.set(Flags.UDFPS_NEW_TOUCH_DETECTION, true);
mFeatureFlags.set(Flags.KEYGUARD_WM_STATE_REFACTOR, false);
mFeatureFlags.set(Flags.ALTERNATE_BOUNCER_VIEW, false);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationControllerTest.kt
index e6f8c48..9aafee4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationControllerTest.kt
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.phone
+import com.android.systemui.flags.FakeFeatureFlags
+import com.android.systemui.flags.Flags
import android.os.Handler
import android.os.PowerManager
import android.testing.AndroidTestingRunner
@@ -80,10 +82,14 @@
@Mock
private lateinit var handler: Handler
+ private lateinit var featureFlags: FakeFeatureFlags
+
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
-
+ featureFlags = FakeFeatureFlags().apply {
+ set(Flags.MIGRATE_KEYGUARD_STATUS_VIEW, false)
+ }
controller = UnlockedScreenOffAnimationController(
context,
wakefulnessLifecycle,
@@ -95,7 +101,8 @@
dagger.Lazy<NotificationShadeWindowController> { notifShadeWindowController },
interactionJankMonitor,
powerManager,
- handler = handler
+ handler = handler,
+ featureFlags,
)
controller.initialize(centralSurfaces, shadeViewController, lightRevealScrim)
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/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/policy/BluetoothControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BluetoothControllerImplTest.java
index e761635..a1da167 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/KeyguardQsUserSwitchControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchControllerTest.kt
index 1250228..d33806e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchControllerTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.policy
+import com.android.systemui.flags.FakeFeatureFlags
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.testing.ViewUtils
@@ -77,11 +78,13 @@
private lateinit var view: FrameLayout
private lateinit var testableLooper: TestableLooper
private lateinit var keyguardQsUserSwitchController: KeyguardQsUserSwitchController
+ private lateinit var featureFlags: FakeFeatureFlags
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
testableLooper = TestableLooper.get(this)
+ featureFlags = FakeFeatureFlags()
view = LayoutInflater.from(context)
.inflate(R.layout.keyguard_qs_user_switch, null) as FrameLayout
@@ -98,6 +101,7 @@
dozeParameters,
screenOffAnimationController,
userSwitchDialogController,
+ featureFlags,
uiEventLogger)
ViewUtils.attachView(view)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ZenModeControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ZenModeControllerImplTest.java
index 66c5aaa..6825f65 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ZenModeControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ZenModeControllerImplTest.java
@@ -38,7 +38,7 @@
import com.android.systemui.dump.DumpManager;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.policy.ZenModeController.Callback;
-import com.android.systemui.util.settings.FakeSettings;
+import com.android.systemui.util.settings.FakeGlobalSettings;
import org.junit.Before;
import org.junit.Test;
@@ -67,7 +67,7 @@
UserTracker mUserTracker;
private ZenModeControllerImpl mController;
- private final FakeSettings mGlobalSettings = new FakeSettings();
+ private final FakeGlobalSettings mGlobalSettings = new FakeGlobalSettings();
@Before
public void setUp() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt
index e249cec..0d78ae9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt
@@ -29,7 +29,7 @@
import com.android.systemui.user.data.model.SelectedUserModel
import com.android.systemui.user.data.model.SelectionStatus
import com.android.systemui.user.data.model.UserSwitcherSettingsModel
-import com.android.systemui.util.settings.FakeSettings
+import com.android.systemui.util.settings.FakeGlobalSettings
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
@@ -56,14 +56,14 @@
private lateinit var underTest: UserRepositoryImpl
- private lateinit var globalSettings: FakeSettings
+ private lateinit var globalSettings: FakeGlobalSettings
private lateinit var tracker: FakeUserTracker
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
- globalSettings = FakeSettings()
+ globalSettings = FakeGlobalSettings()
tracker = FakeUserTracker()
}
@@ -282,20 +282,17 @@
com.android.internal.R.bool.config_expandLockScreenUserSwitcher,
true,
)
- globalSettings.putIntForUser(
+ globalSettings.putInt(
UserRepositoryImpl.SETTING_SIMPLE_USER_SWITCHER,
if (isSimpleUserSwitcher) 1 else 0,
- UserHandle.USER_SYSTEM,
)
- globalSettings.putIntForUser(
+ globalSettings.putInt(
Settings.Global.ADD_USERS_WHEN_LOCKED,
if (isAddUsersFromLockscreen) 1 else 0,
- UserHandle.USER_SYSTEM,
)
- globalSettings.putIntForUser(
+ globalSettings.putInt(
Settings.Global.USER_SWITCHER_ENABLED,
if (isUserSwitcherEnabled) 1 else 0,
- UserHandle.USER_SYSTEM,
)
}
diff --git a/packages/SystemUI/tests/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/utils/src/android/animation/AnimatorTestRule.java b/packages/SystemUI/tests/utils/src/android/animation/AnimatorTestRule.java
index 41dbc14..b820ca6 100644
--- a/packages/SystemUI/tests/utils/src/android/animation/AnimatorTestRule.java
+++ b/packages/SystemUI/tests/utils/src/android/animation/AnimatorTestRule.java
@@ -68,13 +68,26 @@
private final Object mLock = new Object();
private final TestHandler mTestHandler = new TestHandler();
+ private final long mStartTime;
+ private long mTotalTimeDelta = 0;
+
/**
- * initializing the start time with {@link SystemClock#uptimeMillis()} reduces the discrepancies
- * with various internals of classes like ValueAnimator which can sometimes read that clock via
+ * Construct an AnimatorTestRule with a custom start time.
+ * @see #AnimatorTestRule()
+ */
+ public AnimatorTestRule(long startTime) {
+ mStartTime = startTime;
+ }
+
+ /**
+ * Construct an AnimatorTestRule with a start time of {@link SystemClock#uptimeMillis()}.
+ * Initializing the start time with this clock reduces the discrepancies with various internals
+ * of classes like ValueAnimator which can sometimes read that clock via
* {@link android.view.animation.AnimationUtils#currentAnimationTimeMillis()}.
*/
- private final long mStartTime = SystemClock.uptimeMillis();
- private long mTotalTimeDelta = 0;
+ public AnimatorTestRule() {
+ this(SystemClock.uptimeMillis());
+ }
@NonNull
@Override
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java
index a882f89..6ef812b 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java
@@ -15,7 +15,7 @@
*/
package com.android.systemui;
-import static com.android.systemui.animation.FakeDialogLaunchAnimatorKt.fakeDialogLaunchAnimator;
+import static com.android.systemui.Flags.FLAG_EXAMPLE_FLAG;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
@@ -27,6 +27,7 @@
import android.os.Looper;
import android.os.MessageQueue;
import android.os.ParcelFileDescriptor;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.testing.DexmakerShareClassLoaderRule;
import android.testing.LeakCheck;
import android.testing.TestWithLooperRule;
@@ -37,14 +38,11 @@
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;
@@ -73,6 +71,9 @@
new AndroidXAnimatorIsolationRule();
@Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
+ @Rule
public SysuiTestableContext mContext = new SysuiTestableContext(
InstrumentationRegistry.getContext(), getLeakCheck());
@Rule
@@ -93,10 +94,11 @@
if (isRobolectricTest()) {
mContext = mContext.createDefaultDisplayContext();
}
- SystemUIInitializer initializer = new SystemUIInitializerImpl(mContext);
- initializer.init(true);
- mDependency = new TestableDependency(initializer.getSysUIComponent().createDependency());
- Dependency.setInstance(mDependency);
+ // Set the value of a single gantry flag inside the com.android.systemui package to
+ // ensure all flags in that package are faked (and thus require a value to be set).
+ mSetFlagsRule.disableFlags(FLAG_EXAMPLE_FLAG);
+
+ mDependency = SysuiTestDependencyKt.installSysuiTestDependency(mContext);
mFakeBroadcastDispatcher = new FakeBroadcastDispatcher(
mContext,
mContext.getMainExecutor(),
@@ -123,13 +125,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/common/ui/data/repository/FakeConfigurationRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/common/ui/data/repository/FakeConfigurationRepository.kt
index 10b284a..5dcc742 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/common/ui/data/repository/FakeConfigurationRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/common/ui/data/repository/FakeConfigurationRepository.kt
@@ -47,7 +47,7 @@
}
override fun getDimensionPixelSize(id: Int): Int {
- throw IllegalStateException("Don't use for tests")
+ return 0
}
}
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/communal/data/repository/FakeCommunalWidgetRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalWidgetRepository.kt
index 1a8c583..30132f7 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalWidgetRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalWidgetRepository.kt
@@ -1,5 +1,6 @@
package com.android.systemui.communal.data.repository
+import com.android.systemui.communal.data.model.CommunalWidgetMetadata
import com.android.systemui.communal.shared.CommunalAppWidgetInfo
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
@@ -8,6 +9,7 @@
class FakeCommunalWidgetRepository : CommunalWidgetRepository {
private val _stopwatchAppWidgetInfo = MutableStateFlow<CommunalAppWidgetInfo?>(null)
override val stopwatchAppWidgetInfo: Flow<CommunalAppWidgetInfo?> = _stopwatchAppWidgetInfo
+ override var communalWidgetAllowlist: List<CommunalWidgetMetadata> = emptyList()
fun setStopwatchAppWidgetInfo(appWidgetInfo: CommunalAppWidgetInfo) {
_stopwatchAppWidgetInfo.value = appWidgetInfo
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/keyguard/data/repository/FakeBiometricSettingsRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeBiometricSettingsRepository.kt
index e91e955..85261123 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeBiometricSettingsRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeBiometricSettingsRepository.kt
@@ -34,7 +34,7 @@
get() = _isFingerprintAuthCurrentlyAllowed
private val _isFaceAuthEnrolledAndEnabled = MutableStateFlow(false)
- override val isFaceAuthEnrolledAndEnabled: Flow<Boolean>
+ override val isFaceAuthEnrolledAndEnabled: StateFlow<Boolean>
get() = _isFaceAuthEnrolledAndEnabled
private val _isFaceAuthCurrentlyAllowed = MutableStateFlow(false)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/log/core/FakeLogBuffer.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/log/core/FakeLogBuffer.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/log/core/FakeLogBuffer.kt
rename to packages/SystemUI/tests/utils/src/com/android/systemui/log/core/FakeLogBuffer.kt
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/footer/FooterActionsTestUtils.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/footer/FooterActionsTestUtils.kt
index bf77b1a..911eafa 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/footer/FooterActionsTestUtils.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/footer/FooterActionsTestUtils.kt
@@ -53,7 +53,7 @@
import com.android.systemui.user.data.repository.UserSwitcherRepositoryImpl
import com.android.systemui.user.domain.interactor.UserInteractor
import com.android.systemui.util.mockito.mock
-import com.android.systemui.util.settings.FakeSettings
+import com.android.systemui.util.settings.FakeGlobalSettings
import com.android.systemui.util.settings.GlobalSettings
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.test.StandardTestDispatcher
@@ -69,8 +69,8 @@
private val scheduler: TestCoroutineScheduler,
) {
/** Enable or disable the user switcher in the settings. */
- fun setUserSwitcherEnabled(settings: GlobalSettings, enabled: Boolean, userId: Int) {
- settings.putBoolForUser(Settings.Global.USER_SWITCHER_ENABLED, enabled, userId)
+ fun setUserSwitcherEnabled(settings: GlobalSettings, enabled: Boolean) {
+ settings.putBool(Settings.Global.USER_SWITCHER_ENABLED, enabled)
// The settings listener is processing messages on the bgHandler (usually backed by a
// testableLooper in tests), so let's make sure we process the callback before continuing.
@@ -152,7 +152,7 @@
userTracker: UserTracker = FakeUserTracker(),
userSwitcherController: UserSwitcherController = mock(),
userInfoController: UserInfoController = FakeUserInfoController(),
- settings: GlobalSettings = FakeSettings(),
+ settings: GlobalSettings = FakeGlobalSettings(),
): UserSwitcherRepository {
return UserSwitcherRepositoryImpl(
context,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeGlobalSettings.java b/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeGlobalSettings.java
new file mode 100644
index 0000000..db5eaff
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeGlobalSettings.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util.settings;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ContentResolver;
+import android.database.ContentObserver;
+import android.net.Uri;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class FakeGlobalSettings implements GlobalSettings {
+ private final Map<String, String> mValues = new HashMap<>();
+ private final Map<String, List<ContentObserver>> mContentObserversAllUsers = new HashMap<>();
+
+ public static final Uri CONTENT_URI = Uri.parse("content://settings/fake_global");
+
+ public FakeGlobalSettings() {
+ }
+
+ @Override
+ public ContentResolver getContentResolver() {
+ return null;
+ }
+
+ @Override
+ public void registerContentObserver(Uri uri, boolean notifyDescendants,
+ ContentObserver settingsObserver) {
+ List<ContentObserver> observers;
+ mContentObserversAllUsers.putIfAbsent(uri.toString(), new ArrayList<>());
+ observers = mContentObserversAllUsers.get(uri.toString());
+ observers.add(settingsObserver);
+ }
+
+ @Override
+ public void unregisterContentObserver(ContentObserver settingsObserver) {
+ for (Map.Entry<String, List<ContentObserver>> entry :
+ mContentObserversAllUsers.entrySet()) {
+ entry.getValue().remove(settingsObserver);
+ }
+ }
+
+ @Override
+ public Uri getUriFor(String name) {
+ return Uri.withAppendedPath(CONTENT_URI, name);
+ }
+
+ @Override
+ public String getString(String name) {
+ return mValues.get(getUriFor(name).toString());
+ }
+
+ @Override
+ public boolean putString(String name, String value) {
+ return putString(name, value, null, false);
+ }
+
+ @Override
+ public boolean putString(@NonNull String name, @Nullable String value, @Nullable String tag,
+ boolean makeDefault) {
+ String key = getUriFor(name).toString();
+ mValues.put(key, value);
+
+ Uri uri = getUriFor(name);
+ for (ContentObserver observer :
+ mContentObserversAllUsers.getOrDefault(uri.toString(), new ArrayList<>())) {
+ observer.dispatchChange(false, List.of(uri), 0);
+ }
+ return true;
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeSettings.java b/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeSettings.java
index 4b97316..a491886 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeSettings.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeSettings.java
@@ -23,6 +23,8 @@
import android.os.UserHandle;
import android.util.Pair;
+import androidx.annotation.NonNull;
+
import com.android.systemui.settings.UserTracker;
import java.util.ArrayList;
@@ -30,7 +32,7 @@
import java.util.List;
import java.util.Map;
-public class FakeSettings implements SecureSettings, GlobalSettings, SystemSettings {
+public class FakeSettings implements SecureSettings, SystemSettings {
private final Map<SettingsKey, String> mValues = new HashMap<>();
private final Map<SettingsKey, List<ContentObserver>> mContentObservers =
new HashMap<>();
@@ -64,7 +66,7 @@
}
@Override
- public void registerContentObserverForUser(Uri uri, boolean notifyDescendents,
+ public void registerContentObserverForUser(Uri uri, boolean notifyDescendants,
ContentObserver settingsObserver, int userHandle) {
List<ContentObserver> observers;
if (userHandle == UserHandle.USER_ALL) {
@@ -147,7 +149,7 @@
}
@Override
- public boolean putString(String name, String value, String tag, boolean makeDefault) {
+ public boolean putString(@NonNull String name, String value, String tag, boolean makeDefault) {
return putString(name, value);
}
diff --git a/proto/src/metrics_constants/metrics_constants.proto b/proto/src/metrics_constants/metrics_constants.proto
index ced97cc..7ac7859 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/proto/src/system_messages.proto b/proto/src/system_messages.proto
index 21d0979..b403a7f 100644
--- a/proto/src/system_messages.proto
+++ b/proto/src/system_messages.proto
@@ -404,5 +404,9 @@
// Notify the user that audio was lowered based on Calculated Sound Dose (CSD)
NOTE_CSD_LOWER_AUDIO = 1007;
+
+ // Notify the user about external display events related to screenshot.
+ // Package: com.android.systemui
+ NOTE_GLOBAL_SCREENSHOT_EXTERNAL_DISPLAY = 1008;
}
}
diff --git a/ravenwood/Android.bp b/ravenwood/Android.bp
new file mode 100644
index 0000000..91acc3d
--- /dev/null
+++ b/ravenwood/Android.bp
@@ -0,0 +1,33 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+filegroup {
+ name: "ravenwood-annotations",
+ srcs: [
+ "annotations-src/**/*.java",
+ ],
+ visibility: ["//visibility:public"],
+}
+
+// File that contains the standard command line arguments to hoststubgen.
+filegroup {
+ name: "ravenwood-standard-options",
+ srcs: [
+ "ravenwood-standard-options.txt",
+ ],
+ visibility: ["//visibility:public"],
+}
+
+java_library {
+ name: "ravenwood-annotations-lib",
+ srcs: [":ravenwood-annotations"],
+ sdk_version: "core_current",
+ host_supported: true,
+ visibility: ["//visibility:public"],
+}
diff --git a/ravenwood/OWNERS b/ravenwood/OWNERS
new file mode 100644
index 0000000..c06b3b9
--- /dev/null
+++ b/ravenwood/OWNERS
@@ -0,0 +1,3 @@
+jsharkey@google.com
+omakoto@google.com
+jaggies@google.com
diff --git a/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodClassLoadHook.java b/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodClassLoadHook.java
new file mode 100644
index 0000000..be7b923
--- /dev/null
+++ b/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodClassLoadHook.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.ravenwood.annotations;
+
+import static java.lang.annotation.ElementType.TYPE;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * THIS ANNOTATION IS EXPERIMENTAL. REACH OUT TO g/ravenwood BEFORE USING IT, OR YOU HAVE ANY
+ * QUESTIONS ABOUT IT.
+ *
+ * Add this with a fully-specified method name (e.g. {@code "com.package.Class.methodName"})
+ * of a callback to get a callback at the class load time.
+ *
+ * The method must be {@code public static} with a single argument that takes
+ * {@link Class}.
+ */
+@Target({TYPE})
+@Retention(RetentionPolicy.CLASS)
+public @interface RavenwoodClassLoadHook {
+ String value();
+}
diff --git a/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodKeep.java b/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodKeep.java
new file mode 100644
index 0000000..1644ffc
--- /dev/null
+++ b/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodKeep.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.ravenwood.annotations;
+
+import static java.lang.annotation.ElementType.CONSTRUCTOR;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.TYPE;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * THIS ANNOTATION IS EXPERIMENTAL. REACH OUT TO g/ravenwood BEFORE USING IT, OR YOU HAVE ANY
+ * QUESTIONS ABOUT IT.
+ *
+ * TODO: Javadoc
+ *
+ */
+@Target({TYPE, FIELD, METHOD, CONSTRUCTOR})
+@Retention(RetentionPolicy.CLASS)
+public @interface RavenwoodKeep {
+}
diff --git a/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodNativeSubstitutionClass.java b/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodNativeSubstitutionClass.java
new file mode 100644
index 0000000..eb883e2
--- /dev/null
+++ b/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodNativeSubstitutionClass.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.ravenwood.annotations;
+
+import static java.lang.annotation.ElementType.TYPE;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * THIS ANNOTATION IS EXPERIMENTAL. REACH OUT TO g/ravenwood BEFORE USING IT, OR YOU HAVE ANY
+ * QUESTIONS ABOUT IT.
+ *
+ * TODO: Javadoc
+ */
+@Target({TYPE})
+@Retention(RetentionPolicy.CLASS)
+public @interface RavenwoodNativeSubstitutionClass {
+ String value();
+}
diff --git a/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodRemove.java b/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodRemove.java
new file mode 100644
index 0000000..ffa1fa5
--- /dev/null
+++ b/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodRemove.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.ravenwood.annotations;
+
+import static java.lang.annotation.ElementType.CONSTRUCTOR;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.TYPE;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * THIS ANNOTATION IS EXPERIMENTAL. REACH OUT TO g/ravenwood BEFORE USING IT, OR YOU HAVE ANY
+ * QUESTIONS ABOUT IT.
+ *
+ * TODO: Javadoc
+ */
+@Target({TYPE, FIELD, METHOD, CONSTRUCTOR})
+@Retention(RetentionPolicy.CLASS)
+public @interface RavenwoodRemove {
+}
diff --git a/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodSubstitute.java b/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodSubstitute.java
new file mode 100644
index 0000000..6d747da
--- /dev/null
+++ b/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodSubstitute.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.ravenwood.annotations;
+
+import static java.lang.annotation.ElementType.METHOD;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * THIS ANNOTATION IS EXPERIMENTAL. REACH OUT TO g/ravenwood BEFORE USING IT, OR YOU HAVE ANY
+ * QUESTIONS ABOUT IT.
+ *
+ * TODO: Javadoc
+ */
+@Target({METHOD})
+@Retention(RetentionPolicy.CLASS)
+public @interface RavenwoodSubstitute {
+ // TODO We should add "_host" as default. We're not doing it yet, because extractign the default
+ // value with ASM doesn't seem trivial. (? not sure.)
+ String suffix();
+}
diff --git a/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodThrow.java b/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodThrow.java
new file mode 100644
index 0000000..a329d84
--- /dev/null
+++ b/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodThrow.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.ravenwood.annotations;
+
+import static java.lang.annotation.ElementType.CONSTRUCTOR;
+import static java.lang.annotation.ElementType.METHOD;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * THIS ANNOTATION IS EXPERIMENTAL. REACH OUT TO g/ravenwood BEFORE USING IT, OR YOU HAVE ANY
+ * QUESTIONS ABOUT IT.
+ *
+ * TODO: Javadoc
+ * TODO: Create "whole-class-throw"?
+ */
+@Target({METHOD, CONSTRUCTOR})
+@Retention(RetentionPolicy.CLASS)
+public @interface RavenwoodThrow {
+}
diff --git a/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodWholeClassKeep.java b/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodWholeClassKeep.java
new file mode 100644
index 0000000..ae6f42d
--- /dev/null
+++ b/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodWholeClassKeep.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.ravenwood.annotations;
+
+import static java.lang.annotation.ElementType.CONSTRUCTOR;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.TYPE;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * THIS ANNOTATION IS EXPERIMENTAL. REACH OUT TO g/ravenwood BEFORE USING IT, OR YOU HAVE ANY
+ * QUESTIONS ABOUT IT.
+ *
+ * TODO: Javadoc
+ * TODO: Create "whole-class-throw"?
+ */
+@Target({TYPE, FIELD, METHOD, CONSTRUCTOR})
+@Retention(RetentionPolicy.CLASS)
+public @interface RavenwoodWholeClassKeep {
+}
diff --git a/ravenwood/ravenwood-standard-options.txt b/ravenwood/ravenwood-standard-options.txt
new file mode 100644
index 0000000..6e1384f
--- /dev/null
+++ b/ravenwood/ravenwood-standard-options.txt
@@ -0,0 +1,37 @@
+# File containing standard options to HostStubGen for Ravenwood
+
+--debug
+
+# Keep all classes / methods / fields, but make the methods throw.
+--default-throw
+
+# Uncomment below lines to enable each feature.
+# --enable-non-stub-method-check
+
+#--default-method-call-hook
+# com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+#--default-class-load-hook
+# com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+
+# Standard annotations.
+# Note, each line is a single argument, so we need newlines after each `--xxx-annotation`.
+--keep-annotation
+ android.ravenwood.annotations.RavenwoodKeep
+
+--keep-class-annotation
+ android.ravenwood.annotations.RavenwoodWholeClassKeep
+
+--throw-annotation
+ android.ravenwood.annotations.RavenwoodThrow
+
+--remove-annotation
+ android.ravenwood.annotations.RavenwoodRemove
+
+--substitute-annotation
+ android.ravenwood.annotations.RavenwoodSubstitute
+
+--native-substitute-annotation
+ android.ravenwood.annotations.RavenwoodNativeSubstitutionClass
+
+--class-load-hook-annotation
+ android.ravenwood.annotations.RavenwoodClassLoadHook
diff --git a/services/accessibility/accessibility.aconfig b/services/accessibility/accessibility.aconfig
index 6fa9c08..f09cb19 100644
--- a/services/accessibility/accessibility.aconfig
+++ b/services/accessibility/accessibility.aconfig
@@ -40,4 +40,25 @@
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"
+}
+
+flag {
+ name: "reduce_touch_exploration_sensitivity"
+ namespace: "accessibility"
+ description: "Reduces touch exploration sensitivity by only sending a hover event when the ifnger has moved the amount of pixels defined by the system's touch slop."
+ bug: "303677860"
+}
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/gestures/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
index c418485..fc8d4f8 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
@@ -882,10 +882,22 @@
final int pointerIndex = event.findPointerIndex(pointerId);
switch (event.getPointerCount()) {
case 1:
- // Touch exploration.
+ // Touch exploration.
sendTouchExplorationGestureStartAndHoverEnterIfNeeded(policyFlags);
- mDispatcher.sendMotionEvent(
- event, ACTION_HOVER_MOVE, rawEvent, pointerIdBits, policyFlags);
+ if (Flags.reduceTouchExplorationSensitivity()
+ && mState.getLastInjectedHoverEvent() != null) {
+ final MotionEvent lastEvent = mState.getLastInjectedHoverEvent();
+ final float deltaX = lastEvent.getX() - rawEvent.getX();
+ final float deltaY = lastEvent.getY() - rawEvent.getY();
+ final double moveDelta = Math.hypot(deltaX, deltaY);
+ if (moveDelta > mTouchSlop) {
+ mDispatcher.sendMotionEvent(
+ event, ACTION_HOVER_MOVE, rawEvent, pointerIdBits, policyFlags);
+ }
+ } else {
+ mDispatcher.sendMotionEvent(
+ event, ACTION_HOVER_MOVE, rawEvent, pointerIdBits, policyFlags);
+ }
break;
case 2:
if (mGestureDetector.isMultiFingerGesturesEnabled()
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
index 30b9d0b..01064ac 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
@@ -873,7 +873,7 @@
transitionToDelegatingStateAndClear();
- } else if (mDetectTripleTap
+ } else if (mDetectSingleFingerTripleTap
// If activated, delay an ACTION_DOWN for mMultiTapMaxDelay
// to ensure reachability of
// STATE_PANNING_SCALING(triggerable with ACTION_POINTER_DOWN)
@@ -989,7 +989,7 @@
// Shortcut acts as the 2 initial taps
if (mShortcutTriggered) return tapCount() + 2 >= numTaps;
- final boolean multitapTriggered = mDetectTripleTap
+ final boolean multitapTriggered = mDetectSingleFingerTripleTap
&& tapCount() >= numTaps
&& isMultiTap(mPreLastDown, mLastDown)
&& isMultiTap(mPreLastUp, mLastUp);
@@ -1205,7 +1205,7 @@
* @return true if tap is out of distance slop
*/
boolean isTapOutOfDistanceSlop() {
- if (!mDetectTripleTap) return false;
+ if (!mDetectSingleFingerTripleTap) return false;
if (mPreLastDown == null || mLastDown == null) {
return false;
}
@@ -1282,7 +1282,7 @@
+ ", mMagnifiedInteractionState=" + mPanningScalingState
+ ", mViewportDraggingState=" + mViewportDraggingState
+ ", mSinglePanningState=" + mSinglePanningState
- + ", mDetectTripleTap=" + mDetectTripleTap
+ + ", mDetectSingleFingerTripleTap=" + mDetectSingleFingerTripleTap
+ ", mDetectShortcutTrigger=" + mDetectShortcutTrigger
+ ", mCurrentState=" + State.nameOf(mCurrentState)
+ ", mPreviousState=" + State.nameOf(mPreviousState)
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGestureHandler.java
index 2894693..8476a5e 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGestureHandler.java
@@ -57,11 +57,11 @@
protected final boolean mDetectShortcutTrigger;
/**
- * {@code true} if this detector should detect and respond to triple-tap
+ * {@code true} if this detector should detect and respond to single-finger triple-tap
* gestures for engaging and disengaging magnification,
* {@code false} if it should ignore such gestures
*/
- protected final boolean mDetectTripleTap;
+ protected final boolean mDetectSingleFingerTripleTap;
/** Callback interface to report that magnification is interactive with a user. */
public interface Callback {
@@ -85,12 +85,12 @@
private final AccessibilityTraceManager mTrace;
protected final Callback mCallback;
- protected MagnificationGestureHandler(int displayId, boolean detectTripleTap,
+ protected MagnificationGestureHandler(int displayId, boolean detectSingleFingerTripleTap,
boolean detectShortcutTrigger,
AccessibilityTraceManager trace,
@NonNull Callback callback) {
mDisplayId = displayId;
- mDetectTripleTap = detectTripleTap;
+ mDetectSingleFingerTripleTap = detectSingleFingerTripleTap;
mDetectShortcutTrigger = detectShortcutTrigger;
mTrace = trace;
mCallback = callback;
@@ -128,7 +128,7 @@
}
private boolean shouldDispatchTransformedEvent(MotionEvent event) {
- if ((!mDetectTripleTap && !mDetectShortcutTrigger) || !event.isFromSource(
+ if ((!mDetectSingleFingerTripleTap && !mDetectShortcutTrigger) || !event.isFromSource(
SOURCE_TOUCHSCREEN)) {
return true;
}
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java
index c58e9a6..2d9dcb9 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java
@@ -111,7 +111,7 @@
(event, rawEvent, policyFlags) -> dispatchTransformedEvent(event, rawEvent,
policyFlags));
mDelegatingState = new DelegatingState(mMotionEventDispatcherDelegate);
- mDetectingState = new DetectingState(context, mDetectTripleTap);
+ mDetectingState = new DetectingState(context);
mViewportDraggingState = new ViewportDraggingState();
mObservePanningScalingState = new PanningScalingGestureState(
new PanningScalingHandler(context, MAX_SCALE, MIN_SCALE, true,
@@ -448,22 +448,14 @@
private final MagnificationGesturesObserver mGesturesObserver;
- /**
- * {@code true} if this detector should detect and respond to triple-tap
- * gestures for engaging and disengaging magnification,
- * {@code false} if it should ignore such gestures
- */
- private final boolean mDetectTripleTap;
-
- DetectingState(@UiContext Context context, boolean detectTripleTap) {
- mDetectTripleTap = detectTripleTap;
- final MultiTap multiTap = new MultiTap(context, mDetectTripleTap ? 3 : 1,
- mDetectTripleTap
+ DetectingState(@UiContext Context context) {
+ final MultiTap multiTap = new MultiTap(context, mDetectSingleFingerTripleTap ? 3 : 1,
+ mDetectSingleFingerTripleTap
? MagnificationGestureMatcher.GESTURE_TRIPLE_TAP
: MagnificationGestureMatcher.GESTURE_SINGLE_TAP, null);
final MultiTapAndHold multiTapAndHold = new MultiTapAndHold(context,
- mDetectTripleTap ? 3 : 1,
- mDetectTripleTap
+ mDetectSingleFingerTripleTap ? 3 : 1,
+ mDetectSingleFingerTripleTap
? MagnificationGestureMatcher.GESTURE_TRIPLE_TAP_AND_HOLD
: MagnificationGestureMatcher.GESTURE_SINGLE_TAP_AND_HOLD, null);
mGesturesObserver = new MagnificationGesturesObserver(this,
@@ -488,7 +480,7 @@
@Override
public boolean shouldStopDetection(MotionEvent motionEvent) {
return !mWindowMagnificationMgr.isWindowMagnifierEnabled(mDisplayId)
- && !mDetectTripleTap;
+ && !mDetectSingleFingerTripleTap;
}
@Override
diff --git a/services/autofill/bugfixes.aconfig b/services/autofill/bugfixes.aconfig
index 123b65c..b37bbd6 100644
--- a/services/autofill/bugfixes.aconfig
+++ b/services/autofill/bugfixes.aconfig
@@ -8,6 +8,13 @@
}
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"
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index c7b53c5..e4f1d3a 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 = "";
@@ -356,6 +363,7 @@
case AutofillFeatureFlags.DEVICE_CONFIG_AUTOFILL_PCC_FEATURE_PROVIDER_HINTS:
case AutofillFeatureFlags.DEVICE_CONFIG_PREFER_PROVIDER_OVER_PCC:
case AutofillFeatureFlags.DEVICE_CONFIG_PCC_USE_FALLBACK:
+ case Flags.FLAG_AUTOFILL_CREDMAN_INTEGRATION:
setDeviceConfigProperties();
break;
case AutofillFeatureFlags.DEVICE_CONFIG_AUTOFILL_COMPAT_MODE_ALLOWED_PACKAGES:
@@ -701,12 +709,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 +977,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 +1025,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 +2126,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 265ed46..07e9c50 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -16,7 +16,6 @@
package com.android.server.autofill;
-import static android.Manifest.permission.PROVIDE_OWN_AUTOFILL_SUGGESTIONS;
import static android.service.autofill.AutofillFieldClassificationService.EXTRA_SCORES;
import static android.service.autofill.AutofillService.EXTRA_FILL_RESPONSE;
import static android.service.autofill.Dataset.PICK_REASON_NO_PCC;
@@ -44,7 +43,6 @@
import static android.view.autofill.AutofillManager.ACTION_VIEW_EXITED;
import static android.view.autofill.AutofillManager.COMMIT_REASON_SESSION_DESTROYED;
import static android.view.autofill.AutofillManager.COMMIT_REASON_UNKNOWN;
-import static android.view.autofill.AutofillManager.FLAG_ENABLED_CLIENT_SUGGESTIONS;
import static android.view.autofill.AutofillManager.FLAG_SMART_SUGGESTION_SYSTEM;
import static android.view.autofill.AutofillManager.getSmartSuggestionModeToString;
@@ -110,8 +108,6 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.IntentSender;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
import android.content.pm.ServiceInfo;
import android.graphics.Bitmap;
import android.graphics.Rect;
@@ -481,9 +477,6 @@
*/
private final PccAssistDataReceiverImpl mPccAssistReceiver = new PccAssistDataReceiverImpl();
- @Nullable
- private ClientSuggestionsSession mClientSuggestionsSession;
-
private final ClassificationState mClassificationState = new ClassificationState();
// TODO(b/216576510): Share one BroadcastReceiver between all Sessions instead of creating a
@@ -625,9 +618,6 @@
/** Whether the current {@link FillResponse} is expired. */
private boolean mExpiredResponse;
- /** Whether the client is using {@link android.view.autofill.AutofillRequestCallback}. */
- private boolean mClientSuggestionsEnabled;
-
/** Whether the fill dialog UI is disabled. */
private boolean mFillDialogDisabled;
@@ -673,19 +663,13 @@
}
mWaitForInlineRequest = inlineSuggestionsRequest != null;
mPendingInlineSuggestionsRequest = inlineSuggestionsRequest;
- maybeRequestFillFromServiceLocked();
+ maybeRequestFillLocked();
viewState.resetState(ViewState.STATE_PENDING_CREATE_INLINE_REQUEST);
}
}
- void newAutofillRequestLocked(@Nullable InlineSuggestionsRequest inlineRequest) {
- mPendingFillRequest = null;
- mWaitForInlineRequest = inlineRequest != null;
- mPendingInlineSuggestionsRequest = inlineRequest;
- }
-
@GuardedBy("mLock")
- void maybeRequestFillFromServiceLocked() {
+ void maybeRequestFillLocked() {
if (mPendingFillRequest == null) {
return;
}
@@ -696,15 +680,13 @@
return;
}
- if (mPendingInlineSuggestionsRequest.isServiceSupported()) {
- mPendingFillRequest = new FillRequest(mPendingFillRequest.getId(),
- mPendingFillRequest.getFillContexts(),
- mPendingFillRequest.getHints(),
- mPendingFillRequest.getClientState(),
- mPendingFillRequest.getFlags(),
- mPendingInlineSuggestionsRequest,
- mPendingFillRequest.getDelayedFillIntentSender());
- }
+ mPendingFillRequest = new FillRequest(mPendingFillRequest.getId(),
+ mPendingFillRequest.getFillContexts(),
+ mPendingFillRequest.getHints(),
+ mPendingFillRequest.getClientState(),
+ mPendingFillRequest.getFlags(),
+ mPendingInlineSuggestionsRequest,
+ mPendingFillRequest.getDelayedFillIntentSender());
}
mLastFillRequest = mPendingFillRequest;
@@ -826,7 +808,7 @@
: mDelayedFillPendingIntent.getIntentSender());
mPendingFillRequest = request;
- maybeRequestFillFromServiceLocked();
+ maybeRequestFillLocked();
}
if (mActivityToken != null) {
@@ -1152,39 +1134,30 @@
}
/**
- * Cancels the last request sent to the {@link #mRemoteFillService} or the
- * {@link #mClientSuggestionsSession}.
+ * Cancels the last request sent to the {@link #mRemoteFillService}.
*/
@GuardedBy("mLock")
private void cancelCurrentRequestLocked() {
- if (mRemoteFillService == null && mClientSuggestionsSession == null) {
- wtf(null, "cancelCurrentRequestLocked() called without a remote service or a "
- + "client suggestions session. mForAugmentedAutofillOnly: %s",
- mSessionFlags.mAugmentedAutofillOnly);
+ if (mRemoteFillService == null) {
+ wtf(null, "cancelCurrentRequestLocked() called without a remote service. "
+ + "mForAugmentedAutofillOnly: %s", mSessionFlags.mAugmentedAutofillOnly);
return;
}
+ final int canceledRequest = mRemoteFillService.cancelCurrentRequest();
- if (mRemoteFillService != null) {
- final int canceledRequest = mRemoteFillService.cancelCurrentRequest();
+ // Remove the FillContext as there will never be a response for the service
+ if (canceledRequest != INVALID_REQUEST_ID && mContexts != null) {
+ final int numContexts = mContexts.size();
- // Remove the FillContext as there will never be a response for the service
- if (canceledRequest != INVALID_REQUEST_ID && mContexts != null) {
- final int numContexts = mContexts.size();
-
- // It is most likely the last context, hence search backwards
- for (int i = numContexts - 1; i >= 0; i--) {
- if (mContexts.get(i).getRequestId() == canceledRequest) {
- if (sDebug) Slog.d(TAG, "cancelCurrentRequest(): id = " + canceledRequest);
- mContexts.remove(i);
- break;
- }
+ // It is most likely the last context, hence search backwards
+ for (int i = numContexts - 1; i >= 0; i--) {
+ if (mContexts.get(i).getRequestId() == canceledRequest) {
+ if (sDebug) Slog.d(TAG, "cancelCurrentRequest(): id = " + canceledRequest);
+ mContexts.remove(i);
+ break;
}
}
}
-
- if (mClientSuggestionsSession != null) {
- mClientSuggestionsSession.cancelCurrentRequest();
- }
}
private boolean isViewFocusedLocked(int flags) {
@@ -1280,30 +1253,16 @@
requestAssistStructureForPccLocked(flags | FLAG_PCC_DETECTION);
}
- // Only ask IME to create inline suggestions request when
- // 1. Autofill provider supports it or client enabled client suggestions.
- // 2. The render service is available.
- // 3. The view is focused. (The view may not be focused if the autofill is triggered
- // manually.)
+ // Only ask IME to create inline suggestions request if Autofill provider supports it and
+ // the render service is available except the autofill is triggered manually and the view
+ // is also not focused.
final RemoteInlineSuggestionRenderService remoteRenderService =
mService.getRemoteInlineSuggestionRenderServiceLocked();
- if ((mSessionFlags.mInlineSupportedByService || mSessionFlags.mClientSuggestionsEnabled)
- && remoteRenderService != null
- && (isViewFocusedLocked(flags) || (isRequestSupportFillDialog(flags)))) {
- final Consumer<InlineSuggestionsRequest> inlineSuggestionsRequestConsumer;
- if (mSessionFlags.mClientSuggestionsEnabled) {
- final int finalRequestId = requestId;
- inlineSuggestionsRequestConsumer = (inlineSuggestionsRequest) -> {
- // Using client suggestions
- synchronized (mLock) {
- onClientFillRequestLocked(finalRequestId, inlineSuggestionsRequest);
- }
- viewState.resetState(ViewState.STATE_PENDING_CREATE_INLINE_REQUEST);
- };
- } else {
- inlineSuggestionsRequestConsumer = mAssistReceiver.newAutofillRequestLocked(
- viewState, /* isInlineRequest= */ true);
- }
+ if (mSessionFlags.mInlineSupportedByService && remoteRenderService != null
+ && (isViewFocusedLocked(flags) || isRequestSupportFillDialog(flags))) {
+ Consumer<InlineSuggestionsRequest> inlineSuggestionsRequestConsumer =
+ mAssistReceiver.newAutofillRequestLocked(viewState,
+ /* isInlineRequest= */ true);
if (inlineSuggestionsRequestConsumer != null) {
final int requestIdCopy = requestId;
final AutofillId focusedId = mCurrentViewId;
@@ -1323,18 +1282,10 @@
inlineSuggestionRendorInfoCallback);
viewState.setState(ViewState.STATE_PENDING_CREATE_INLINE_REQUEST);
}
- } else if (mSessionFlags.mClientSuggestionsEnabled) {
- // Request client suggestions for the dropdown mode
- onClientFillRequestLocked(requestId, null);
} else {
mAssistReceiver.newAutofillRequestLocked(viewState, /* isInlineRequest= */ false);
}
- if (mSessionFlags.mClientSuggestionsEnabled) {
- // Using client suggestions, unnecessary request AssistStructure
- return;
- }
-
// Now request the assist structure data.
requestAssistStructureLocked(requestId, flags);
}
@@ -1443,11 +1394,6 @@
mSessionFlags = new SessionFlags();
mSessionFlags.mAugmentedAutofillOnly = forAugmentedAutofillOnly;
mSessionFlags.mInlineSupportedByService = mService.isInlineSuggestionsEnabledLocked();
- if (mContext.checkCallingPermission(PROVIDE_OWN_AUTOFILL_SUGGESTIONS)
- == PackageManager.PERMISSION_GRANTED) {
- mSessionFlags.mClientSuggestionsEnabled =
- (mFlags & FLAG_ENABLED_CLIENT_SUGGESTIONS) != 0;
- }
setClientLocked(client);
}
@@ -1599,15 +1545,14 @@
if (requestLog != null) {
requestLog.addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUM_DATASETS, -1);
}
- processNullResponseOrFallbackLocked(requestId, requestFlags);
+ processNullResponseLocked(requestId, requestFlags);
return;
}
// TODO: Check if this is required. We can still present datasets to the user even if
// traditional field classification is disabled.
fieldClassificationIds = response.getFieldClassificationIds();
- if (!mSessionFlags.mClientSuggestionsEnabled && fieldClassificationIds != null
- && !mService.isFieldClassificationEnabledLocked()) {
+ if (fieldClassificationIds != null && !mService.isFieldClassificationEnabledLocked()) {
Slog.w(TAG, "Ignoring " + response + " because field detection is disabled");
processNullResponseLocked(requestId, requestFlags);
return;
@@ -1741,9 +1686,7 @@
|| (ArrayUtils.isEmpty(saveInfo.getOptionalIds())
&& ArrayUtils.isEmpty(saveInfo.getRequiredIds())
&& ((saveInfo.getFlags() & SaveInfo.FLAG_DELAY_SAVE) == 0)))
- && (ArrayUtils.isEmpty(response.getFieldClassificationIds())
- || (!mSessionFlags.mClientSuggestionsEnabled
- && !mService.isFieldClassificationEnabledLocked())));
+ && (ArrayUtils.isEmpty(response.getFieldClassificationIds())));
}
}
@@ -2190,40 +2133,6 @@
fieldFilters.add(dataset.getFilter(index));
}
- @GuardedBy("mLock")
- private void processNullResponseOrFallbackLocked(int requestId, int flags) {
- if (!mSessionFlags.mClientSuggestionsEnabled) {
- processNullResponseLocked(requestId, flags);
- return;
- }
-
- // fallback to the default platform password manager
- mSessionFlags.mClientSuggestionsEnabled = false;
- mLastFillDialogTriggerIds = null;
- // Log the existing FillResponse event.
- mFillResponseEventLogger.logAndEndEvent();
-
- final InlineSuggestionsRequest inlineRequest =
- (mLastInlineSuggestionsRequest != null
- && mLastInlineSuggestionsRequest.first == requestId)
- ? mLastInlineSuggestionsRequest.second : null;
-
- // Start a new FillRequest logger for client suggestion fallback.
- mFillRequestEventLogger.startLogForNewRequest();
- mRequestCount++;
- mFillRequestEventLogger.maybeSetAppPackageUid(uid);
- mFillRequestEventLogger.maybeSetFlags(
- flags & ~FLAG_ENABLED_CLIENT_SUGGESTIONS);
- mFillRequestEventLogger.maybeSetRequestTriggerReason(
- TRIGGER_REASON_NORMAL_TRIGGER);
- mFillRequestEventLogger.maybeSetIsClientSuggestionFallback(true);
-
- mAssistReceiver.newAutofillRequestLocked(inlineRequest);
- requestAssistStructureLocked(requestId,
- flags & ~FLAG_ENABLED_CLIENT_SUGGESTIONS);
- return;
- }
-
// FillServiceCallbacks
@Override
@SuppressWarnings("GuardedBy")
@@ -4520,22 +4429,13 @@
filterText = value.getTextValue().toString();
}
- final CharSequence targetLabel;
- final Drawable targetIcon;
- synchronized (mLock) {
- if (mSessionFlags.mClientSuggestionsEnabled) {
- final ApplicationInfo appInfo = ClientSuggestionsSession.getAppInfo(mComponentName,
- mService.getUserId());
- targetLabel = ClientSuggestionsSession.getAppLabelLocked(
- mService.getMaster().getContext(), appInfo);
- targetIcon = ClientSuggestionsSession.getAppIconLocked(
- mService.getMaster().getContext(), appInfo);
- } else {
- targetLabel = mService.getServiceLabelLocked();
- targetIcon = mService.getServiceIconLocked();
- }
+ final CharSequence serviceLabel;
+ final Drawable serviceIcon;
+ synchronized (this.mService.mLock) {
+ serviceLabel = mService.getServiceLabelLocked();
+ serviceIcon = mService.getServiceIconLocked();
}
- if (targetLabel == null || targetIcon == null) {
+ if (serviceLabel == null || serviceIcon == null) {
wtf(null, "onFillReady(): no service label or icon");
return;
}
@@ -4596,7 +4496,7 @@
getUiForShowing().showFillUi(filledId, response, filterText,
mService.getServicePackageName(), mComponentName,
- targetLabel, targetIcon, this, mContext, id, mCompatMode,
+ serviceLabel, serviceIcon, this, mContext, id, mCompatMode,
mService.getMaster().getMaxInputLengthForAutofill());
synchronized (mLock) {
@@ -4799,17 +4699,6 @@
return false;
}
- final InlineSuggestionsRequest request = inlineSuggestionsRequest.get();
- if (mSessionFlags.mClientSuggestionsEnabled && !request.isClientSupported()
- || !mSessionFlags.mClientSuggestionsEnabled && !request.isServiceSupported()) {
- if (sDebug) {
- Slog.d(TAG, "Inline suggestions not supported for "
- + (mSessionFlags.mClientSuggestionsEnabled ? "client" : "service")
- + ". Falling back to dropdown.");
- }
- return false;
- }
-
final RemoteInlineSuggestionRenderService remoteRenderService =
mService.getRemoteInlineSuggestionRenderServiceLocked();
if (remoteRenderService == null) {
@@ -4824,7 +4713,7 @@
}
final InlineFillUi.InlineFillUiInfo inlineFillUiInfo =
- new InlineFillUi.InlineFillUiInfo(request, focusedId,
+ new InlineFillUi.InlineFillUiInfo(inlineSuggestionsRequest.get(), focusedId,
filterText, remoteRenderService, userId, id);
InlineFillUi inlineFillUi = InlineFillUi.forAutofill(inlineFillUiInfo, response,
new InlineFillUi.InlineSuggestionUiCallback() {
@@ -5641,26 +5530,6 @@
}
}
- @GuardedBy("mLock")
- private void onClientFillRequestLocked(int requestId,
- InlineSuggestionsRequest inlineSuggestionsRequest) {
- if (mClientSuggestionsSession == null) {
- mClientSuggestionsSession = new ClientSuggestionsSession(id, mClient, mHandler,
- mComponentName, this);
- }
-
- if (mContexts == null) {
- mContexts = new ArrayList<>(1);
- }
- mContexts.add(new FillContext(requestId, new AssistStructure(), mCurrentViewId));
-
- if (inlineSuggestionsRequest != null && !inlineSuggestionsRequest.isClientSupported()) {
- inlineSuggestionsRequest = null;
- }
-
- mClientSuggestionsSession.onFillRequest(requestId, inlineSuggestionsRequest, mFlags);
- }
-
/**
* The result of checking whether to show the save dialog, when session can be saved.
*
@@ -6142,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..4298c07 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
@@ -632,6 +632,10 @@
public void setDevicePolicy(@VirtualDeviceParams.DynamicPolicyType int policyType,
@VirtualDeviceParams.DevicePolicy int devicePolicy) {
super.setDevicePolicy_enforcePermission();
+ if (!Flags.dynamicPolicy()) {
+ return;
+ }
+
switch (policyType) {
case POLICY_TYPE_RECENTS:
synchronized (mVirtualDeviceLock) {
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
index f594170..65975e4 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;
@@ -1326,6 +1437,10 @@
if (!mDevCfgEnableContentProtectionReceiver) {
return false;
}
+ if (mDevCfgContentProtectionRequiredGroups.isEmpty()
+ && mDevCfgContentProtectionOptionalGroups.isEmpty()) {
+ return false;
+ }
}
return mContentProtectionConsentManager.isConsentGranted(userId)
&& mContentProtectionBlocklistManager.isAllowed(packageName);
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 93dca2f..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();
}
/**
@@ -560,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;
}
@@ -604,7 +621,6 @@
int key = currentPinKeys.valueAt(i);
pinApp(key, userHandle, true /* force */);
}
- pinAnonRegion();
}
/**
@@ -689,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;
@@ -700,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;
@@ -729,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;
@@ -744,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 962f38f..beea063 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -214,9 +214,12 @@
// external storage service.
public static final int FAILED_MOUNT_RESET_TIMEOUT_SECONDS = 10;
- /** Extended timeout for the system server watchdog. */
+ /** 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<>();
@@ -2338,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,
@@ -2463,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);
@@ -2483,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);
@@ -2499,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);
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index 5fb889a..1650a96 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -5309,7 +5309,7 @@
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "performing bindService to " + authenticatorInfo.componentName);
}
- long flags = Context.BIND_FILTER_OUT_QUARANTINED_COMPONENTS | Context.BIND_AUTO_CREATE;
+ long flags = Context.BIND_AUTO_CREATE;
if (mAuthenticatorCache.getBindInstantServiceAllowed(mAccounts.userId)) {
flags |= Context.BIND_ALLOW_INSTANT;
}
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 553b085..5f1a7e7 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());
}
}
@@ -3662,8 +3678,8 @@
|| (flags & Context.BIND_EXTERNAL_SERVICE_LONG) != 0;
final boolean allowInstant = (flags & Context.BIND_ALLOW_INSTANT) != 0;
final boolean inSharedIsolatedProcess = (flags & Context.BIND_SHARED_ISOLATED_PROCESS) != 0;
- final boolean filterOutQuarantined =
- (flags & Context.BIND_FILTER_OUT_QUARANTINED_COMPONENTS) != 0;
+ final boolean matchQuarantined =
+ (flags & Context.BIND_MATCH_QUARANTINED_COMPONENTS) != 0;
ProcessRecord attributedApp = null;
if (sdkSandboxClientAppUid > 0) {
@@ -3673,7 +3689,7 @@
isSdkSandboxService, sdkSandboxClientAppUid, sdkSandboxClientAppPackage,
resolvedType, callingPackage, callingPid, callingUid, userId, true, callerFg,
isBindExternal, allowInstant, null /* fgsDelegateOptions */,
- inSharedIsolatedProcess, filterOutQuarantined);
+ inSharedIsolatedProcess, matchQuarantined);
if (res == null) {
return 0;
}
@@ -4186,7 +4202,7 @@
sdkSandboxClientAppUid, sdkSandboxClientAppPackage, resolvedType, callingPackage,
callingPid, callingUid, userId, createIfNeeded, callingFromFg, isBindExternal,
allowInstant, fgsDelegateOptions, inSharedIsolatedProcess,
- false /* filterOutQuarantined */);
+ false /* matchQuarantined */);
}
private ServiceLookupResult retrieveServiceLocked(Intent service,
@@ -4195,7 +4211,7 @@
String callingPackage, int callingPid, int callingUid, int userId,
boolean createIfNeeded, boolean callingFromFg, boolean isBindExternal,
boolean allowInstant, ForegroundServiceDelegationOptions fgsDelegateOptions,
- boolean inSharedIsolatedProcess, boolean filterOutQuarantined) {
+ boolean inSharedIsolatedProcess, boolean matchQuarantined) {
if (isSdkSandboxService && instanceName == null) {
throw new IllegalArgumentException("No instanceName provided for sdk sandbox process");
}
@@ -4317,8 +4333,8 @@
if (allowInstant) {
flags |= PackageManager.MATCH_INSTANT;
}
- if (filterOutQuarantined) {
- flags |= PackageManager.FILTER_OUT_QUARANTINED_COMPONENTS;
+ if (matchQuarantined) {
+ flags |= PackageManager.MATCH_QUARANTINED_COMPONENTS;
}
// TODO: come back and remove this assumption to triage all services
ResolveInfo rInfo = mAm.getPackageManagerInternal().resolveService(service,
@@ -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..31817f1 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -58,7 +58,6 @@
import static android.app.ProcessMemoryState.HOSTING_COMPONENT_TYPE_PERSISTENT;
import static android.app.ProcessMemoryState.HOSTING_COMPONENT_TYPE_SYSTEM;
import static android.content.pm.ApplicationInfo.HIDDEN_API_ENFORCEMENT_DEFAULT;
-import static android.content.pm.PackageManager.FILTER_OUT_QUARANTINED_COMPONENTS;
import static android.content.pm.PackageManager.GET_SHARED_LIBRARY_FILES;
import static android.content.pm.PackageManager.MATCH_ALL;
import static android.content.pm.PackageManager.MATCH_ANY_USER;
@@ -377,6 +376,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 +562,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 +1531,11 @@
*/
int mBootPhase;
+ /**
+ * The time stamp that all apps have received BOOT_COMPLETED.
+ */
+ volatile long mBootCompletedTimestamp;
+
@GuardedBy("this")
boolean mDeterministicUidIdle = false;
@@ -1630,7 +1635,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 +1989,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 +4162,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 +4759,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 +4797,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 +4877,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 +5015,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 +5168,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();
@@ -14248,8 +14294,7 @@
private List<ResolveInfo> collectReceiverComponents(Intent intent, String resolvedType,
int callingUid, int[] users, int[] broadcastAllowList) {
// TODO: come back and remove this assumption to triage all broadcasts
- long pmFlags = STOCK_PM_FLAGS | MATCH_DEBUG_TRIAGED_MISSING
- | FILTER_OUT_QUARANTINED_COMPONENTS;
+ long pmFlags = STOCK_PM_FLAGS | MATCH_DEBUG_TRIAGED_MISSING;
List<ResolveInfo> receivers = null;
HashSet<ComponentName> singleUserReceivers = null;
diff --git a/services/core/java/com/android/server/am/AnrTimer.java b/services/core/java/com/android/server/am/AnrTimer.java
index 9ba49ce..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;
@@ -150,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) {
@@ -159,7 +159,7 @@
this.tag = tag;
this.stack = stack;
this.arg = arg;
- this.date = new Date().toString();
+ this.timestamp = SystemClock.elapsedRealtime();
}
}
@@ -347,20 +347,23 @@
* main Looper.
*/
@NonNull
- Handler getHandler(@NonNull Handler.Callback callback) {
+ Handler newHandler(@NonNull Handler.Callback callback) {
Looper looper = mReferenceHandler.getLooper();
if (looper == null) looper = Looper.getMainLooper();
return new Handler(looper, callback);
- };
+ }
- /** Return a CpuTracker. */
+ /**
+ * Return a CpuTracker. The default behavior is to create a new CpuTracker but this changes
+ * for unit tests.
+ **/
@NonNull
- CpuTracker getTracker() {
+ CpuTracker newTracker() {
return new CpuTracker();
}
/** Return true if the feature is enabled. */
- boolean getFeatureEnabled() {
+ boolean isFeatureEnabled() {
return anrTimerServiceEnabled();
}
}
@@ -401,8 +404,8 @@
/** 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. */
@@ -513,7 +516,26 @@
private final FeatureSwitch mFeature;
/**
- * The common constructor. A null injector results in a normal, production timer.
+ * 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,
@@ -522,7 +544,7 @@
mWhat = what;
mLabel = label;
mExtend = extend;
- boolean enabled = injector.getFeatureEnabled();
+ boolean enabled = injector.isFeatureEnabled();
if (!enabled) {
mFeature = new FeatureDisabled();
mTimerService = null;
@@ -538,14 +560,25 @@
}
/**
- * 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, 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);
@@ -555,6 +588,8 @@
* 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();
@@ -642,7 +677,7 @@
}
/**
- * 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));
@@ -654,9 +689,13 @@
*/
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();
}
@@ -666,6 +705,7 @@
*/
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);
@@ -673,22 +713,26 @@
}
/** 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;
}
@@ -703,16 +747,17 @@
/**
* 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) {
- // 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) {
+ restartedLocked(old.status, arg);
discard(arg);
} else {
cancel(arg);
@@ -735,6 +780,7 @@
/**
* Cancel a timer. Return false if the timer was not found.
*/
+ @Override
boolean cancel(@NonNull V arg) {
synchronized (mLock) {
Timer timer = removeLocked(arg);
@@ -755,6 +801,7 @@
* 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);
@@ -775,6 +822,7 @@
* 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);
@@ -791,40 +839,58 @@
}
/** The feature is enabled. */
+ @Override
boolean enabled() {
return true;
}
}
/**
- * Start a timer associated with arg. If a timer already exists with the same arg, then that
- * timer is canceled and a new timer is created. This returns false if the timer cannot be
- * created.
+ * 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 a running timer and remove it from any list. This returns true if the timer was
- * found and false otherwise. It is not an error to cancel a non-existent timer. It is also
- * not an error to cancel an expired timer.
+ * 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) {
return mFeature.cancel(arg);
}
/**
- * Accept an expired timer. This returns false if the timer was not found or if the timer was
- * not expired.
+ * 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) {
return mFeature.accept(arg);
}
/**
- * Discard an expired timer. This returns false if the timer was not found or if the timer was
- * not expired.
+ * 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) {
return mFeature.discard(arg);
@@ -913,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/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 2249607..0ab81a5 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -91,6 +91,7 @@
import android.telephony.NetworkRegistrationInfo;
import android.telephony.SignalStrength;
import android.telephony.TelephonyManager;
+import android.util.AtomicFile;
import android.util.IndentingPrintWriter;
import android.util.Slog;
import android.util.StatsEvent;
@@ -99,8 +100,10 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.IBatteryStats;
import com.android.internal.os.BinderCallsStats;
+import com.android.internal.os.Clock;
import com.android.internal.os.CpuScalingPolicies;
import com.android.internal.os.CpuScalingPolicyReader;
+import com.android.internal.os.MonotonicClock;
import com.android.internal.os.PowerProfile;
import com.android.internal.os.RailStats;
import com.android.internal.os.RpmStats;
@@ -114,16 +117,21 @@
import com.android.server.net.BaseNetworkObserver;
import com.android.server.pm.UserManagerInternal;
import com.android.server.power.optimization.Flags;
+import com.android.server.power.stats.AggregatedPowerStatsConfig;
import com.android.server.power.stats.BatteryExternalStatsWorker;
import com.android.server.power.stats.BatteryStatsImpl;
import com.android.server.power.stats.BatteryUsageStatsProvider;
-import com.android.server.power.stats.BatteryUsageStatsStore;
+import com.android.server.power.stats.PowerStatsAggregator;
+import com.android.server.power.stats.PowerStatsScheduler;
+import com.android.server.power.stats.PowerStatsStore;
import com.android.server.power.stats.SystemServerCpuThreadReader.SystemServiceCpuThreadTimes;
import com.android.server.power.stats.wakeups.CpuWakeupStats;
import java.io.File;
import java.io.FileDescriptor;
+import java.io.FileOutputStream;
import java.io.IOException;
+import java.io.InputStream;
import java.io.PrintWriter;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
@@ -136,6 +144,7 @@
import java.util.List;
import java.util.Map;
import java.util.Objects;
+import java.util.Properties;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
@@ -153,20 +162,24 @@
static final String TAG = "BatteryStatsService";
static final String TRACE_TRACK_WAKEUP_REASON = "wakeup_reason";
static final boolean DBG = false;
- private static final boolean BATTERY_USAGE_STORE_ENABLED = true;
private static IBatteryStats sService;
private final PowerProfile mPowerProfile;
private final CpuScalingPolicies mCpuScalingPolicies;
+ private final MonotonicClock mMonotonicClock;
private final BatteryStatsImpl.BatteryStatsConfig mBatteryStatsConfig;
final BatteryStatsImpl mStats;
final CpuWakeupStats mCpuWakeupStats;
- private final BatteryUsageStatsStore mBatteryUsageStatsStore;
+ private final PowerStatsStore mPowerStatsStore;
+ private final PowerStatsAggregator mPowerStatsAggregator;
+ private final PowerStatsScheduler mPowerStatsScheduler;
private final BatteryStatsImpl.UserInfoProvider mUserManagerUserInfoProvider;
private final Context mContext;
private final BatteryExternalStatsWorker mWorker;
private final BatteryUsageStatsProvider mBatteryUsageStatsProvider;
+ private final AtomicFile mConfigFile;
+
private volatile boolean mMonitorEnabled = true;
private native void getRailEnergyPowerStats(RailStats railStats);
@@ -376,6 +389,7 @@
mHandlerThread.start();
mHandler = new Handler(mHandlerThread.getLooper());
+ mMonotonicClock = new MonotonicClock(new File(systemDir, "monotonic_clock.xml"));
mPowerProfile = new PowerProfile(context);
mCpuScalingPolicies = new CpuScalingPolicyReader().read();
@@ -391,23 +405,43 @@
.setResetOnUnplugAfterSignificantCharge(resetOnUnplugAfterSignificantCharge)
.setPowerStatsThrottlePeriodCpu(powerStatsThrottlePeriodCpu)
.build();
- mStats = new BatteryStatsImpl(mBatteryStatsConfig, systemDir, handler, this,
- this, mUserManagerUserInfoProvider, mPowerProfile, mCpuScalingPolicies);
+ mStats = new BatteryStatsImpl(mBatteryStatsConfig, Clock.SYSTEM_CLOCK, mMonotonicClock,
+ systemDir, handler, this, this, mUserManagerUserInfoProvider, mPowerProfile,
+ mCpuScalingPolicies);
mWorker = new BatteryExternalStatsWorker(context, mStats);
mStats.setExternalStatsSyncLocked(mWorker);
mStats.setRadioScanningTimeoutLocked(mContext.getResources().getInteger(
com.android.internal.R.integer.config_radioScanningTimeout) * 1000L);
mStats.startTrackingSystemServerCpuTime();
- if (BATTERY_USAGE_STORE_ENABLED) {
- mBatteryUsageStatsStore =
- new BatteryUsageStatsStore(context, mStats, systemDir, mHandler);
- } else {
- mBatteryUsageStatsStore = null;
- }
+ AggregatedPowerStatsConfig aggregatedPowerStatsConfig = getAggregatedPowerStatsConfig();
+ mPowerStatsStore = new PowerStatsStore(systemDir, mHandler, aggregatedPowerStatsConfig);
mBatteryUsageStatsProvider = new BatteryUsageStatsProvider(context, mStats,
- mBatteryUsageStatsStore);
+ mPowerStatsStore);
+ mPowerStatsAggregator = new PowerStatsAggregator(aggregatedPowerStatsConfig,
+ mStats.getHistory());
+ final long aggregatedPowerStatsSpanDuration = context.getResources().getInteger(
+ com.android.internal.R.integer.config_aggregatedPowerStatsSpanDuration);
+ final long powerStatsAggregationPeriod = context.getResources().getInteger(
+ com.android.internal.R.integer.config_powerStatsAggregationPeriod);
+ mPowerStatsScheduler = new PowerStatsScheduler(context, mPowerStatsAggregator,
+ aggregatedPowerStatsSpanDuration, powerStatsAggregationPeriod, mPowerStatsStore,
+ Clock.SYSTEM_CLOCK, mMonotonicClock, mHandler, mStats, mBatteryUsageStatsProvider);
mCpuWakeupStats = new CpuWakeupStats(context, R.xml.irq_device_map, mHandler);
+ mConfigFile = new AtomicFile(new File(systemDir, "battery_usage_stats_config"));
+ }
+
+ private AggregatedPowerStatsConfig getAggregatedPowerStatsConfig() {
+ AggregatedPowerStatsConfig config = new AggregatedPowerStatsConfig();
+ config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_CPU)
+ .trackDeviceStates(
+ AggregatedPowerStatsConfig.STATE_POWER,
+ AggregatedPowerStatsConfig.STATE_SCREEN)
+ .trackUidStates(
+ AggregatedPowerStatsConfig.STATE_POWER,
+ AggregatedPowerStatsConfig.STATE_SCREEN,
+ AggregatedPowerStatsConfig.STATE_PROCESS_STATE);
+ return config;
}
/**
@@ -466,9 +500,7 @@
*/
public void onSystemReady() {
mStats.onSystemReady();
- if (BATTERY_USAGE_STORE_ENABLED) {
- mBatteryUsageStatsStore.onSystemReady();
- }
+ mPowerStatsScheduler.start(Flags.streamlinedBatteryStats());
}
private final class LocalService extends BatteryStatsInternal {
@@ -639,6 +671,9 @@
// Shutdown the thread we made.
mWorker.shutdown();
+
+ // To insure continuity, write the monotonic timeshift after writing the last history event
+ mMonotonicClock.write();
}
public static IBatteryStats getService() {
@@ -892,12 +927,8 @@
bus = getBatteryUsageStats(List.of(queryPowerProfile)).get(0);
break;
case FrameworkStatsLog.BATTERY_USAGE_STATS_BEFORE_RESET:
- if (!BATTERY_USAGE_STORE_ENABLED) {
- return StatsManager.PULL_SKIP;
- }
-
- final long sessionStart = mBatteryUsageStatsStore
- .getLastBatteryUsageStatsBeforeResetAtomPullTimestamp();
+ final long sessionStart =
+ getLastBatteryUsageStatsBeforeResetAtomPullTimestamp();
final long sessionEnd;
synchronized (mStats) {
sessionEnd = mStats.getStartClockTime();
@@ -910,8 +941,7 @@
.aggregateSnapshots(sessionStart, sessionEnd)
.build();
bus = getBatteryUsageStats(List.of(queryBeforeReset)).get(0);
- mBatteryUsageStatsStore
- .setLastBatteryUsageStatsBeforeResetAtomPullTimestamp(sessionEnd);
+ setLastBatteryUsageStatsBeforeResetAtomPullTimestamp(sessionEnd);
break;
default:
throw new UnsupportedOperationException("Unknown tagId=" + atomTag);
@@ -2641,7 +2671,15 @@
}
private void dumpAggregatedStats(PrintWriter pw) {
- mStats.dumpAggregatedStats(pw, /* startTime */ 0, /* endTime */0);
+ mPowerStatsScheduler.aggregateAndDumpPowerStats(pw);
+ }
+
+ private void dumpPowerStatsStore(PrintWriter pw) {
+ mPowerStatsStore.dump(new IndentingPrintWriter(pw, " "));
+ }
+
+ private void dumpPowerStatsStoreTableOfContents(PrintWriter pw) {
+ mPowerStatsStore.dumpTableOfContents(new IndentingPrintWriter(pw, " "));
}
private void dumpMeasuredEnergyStats(PrintWriter pw) {
@@ -2789,7 +2827,7 @@
synchronized (mStats) {
mStats.resetAllStatsAndHistoryLocked(
BatteryStatsImpl.RESET_REASON_ADB_COMMAND);
- mBatteryUsageStatsStore.removeAllSnapshots();
+ mPowerStatsStore.reset();
pw.println("Battery stats and history reset.");
noOutput = true;
}
@@ -2891,6 +2929,12 @@
} else if ("--aggregated".equals(arg)) {
dumpAggregatedStats(pw);
return;
+ } else if ("--store".equals(arg)) {
+ dumpPowerStatsStore(pw);
+ return;
+ } else if ("--store-toc".equals(arg)) {
+ dumpPowerStatsStoreTableOfContents(pw);
+ return;
} else if ("-a".equals(arg)) {
flags |= BatteryStats.DUMP_VERBOSE;
} else if (arg.length() > 0 && arg.charAt(0) == '-'){
@@ -2951,7 +2995,7 @@
in.unmarshall(raw, 0, raw.length);
in.setDataPosition(0);
BatteryStatsImpl checkinStats = new BatteryStatsImpl(
- mBatteryStatsConfig,
+ mBatteryStatsConfig, Clock.SYSTEM_CLOCK, mMonotonicClock,
null, mStats.mHandler, null, null,
mUserManagerUserInfoProvider, mPowerProfile,
mCpuScalingPolicies);
@@ -2993,7 +3037,7 @@
in.unmarshall(raw, 0, raw.length);
in.setDataPosition(0);
BatteryStatsImpl checkinStats = new BatteryStatsImpl(
- mBatteryStatsConfig,
+ mBatteryStatsConfig, Clock.SYSTEM_CLOCK, mMonotonicClock,
null, mStats.mHandler, null, null,
mUserManagerUserInfoProvider, mPowerProfile,
mCpuScalingPolicies);
@@ -3360,6 +3404,52 @@
}
}
+ private static final String BATTERY_USAGE_STATS_BEFORE_RESET_TIMESTAMP_PROPERTY =
+ "BATTERY_USAGE_STATS_BEFORE_RESET_TIMESTAMP";
+
+ /**
+ * Saves the supplied timestamp of the BATTERY_USAGE_STATS_BEFORE_RESET statsd atom pull
+ * in persistent file.
+ */
+ public void setLastBatteryUsageStatsBeforeResetAtomPullTimestamp(long timestamp) {
+ synchronized (mConfigFile) {
+ Properties props = new Properties();
+ try (InputStream in = mConfigFile.openRead()) {
+ props.load(in);
+ } catch (IOException e) {
+ Slog.e(TAG, "Cannot load config file " + mConfigFile, e);
+ }
+ props.put(BATTERY_USAGE_STATS_BEFORE_RESET_TIMESTAMP_PROPERTY,
+ String.valueOf(timestamp));
+ FileOutputStream out = null;
+ try {
+ out = mConfigFile.startWrite();
+ props.store(out, "Statsd atom pull timestamps");
+ mConfigFile.finishWrite(out);
+ } catch (IOException e) {
+ mConfigFile.failWrite(out);
+ Slog.e(TAG, "Cannot save config file " + mConfigFile, e);
+ }
+ }
+ }
+
+ /**
+ * Retrieves the previously saved timestamp of the last BATTERY_USAGE_STATS_BEFORE_RESET
+ * statsd atom pull.
+ */
+ public long getLastBatteryUsageStatsBeforeResetAtomPullTimestamp() {
+ synchronized (mConfigFile) {
+ Properties props = new Properties();
+ try (InputStream in = mConfigFile.openRead()) {
+ props.load(in);
+ } catch (IOException e) {
+ Slog.e(TAG, "Cannot load config file " + mConfigFile, e);
+ }
+ return Long.parseLong(
+ props.getProperty(BATTERY_USAGE_STATS_BEFORE_RESET_TIMESTAMP_PROPERTY, "0"));
+ }
+ }
+
/**
* Sets battery AC charger to enabled/disabled, and freezes the battery state.
*/
diff --git a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
index a428907..d19eae5 100644
--- a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
+++ b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
@@ -258,7 +258,8 @@
private static final int MSG_PROCESS_FREEZABLE_CHANGED = 6;
private static final int MSG_UID_STATE_CHANGED = 7;
- // Required when Flags.anrTimerServiceEnabled is false.
+ // 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() {
@@ -274,7 +275,8 @@
updateRunningList();
return true;
}
- // Required when Flags.anrTimerServiceEnabled is false.
+ // 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);
@@ -1169,7 +1171,8 @@
r.resultTo = null;
}
- // Required when Flags.anrTimerServiceEnabled is false.
+ // 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()) {
@@ -1181,7 +1184,8 @@
}
}
- // Required when Flags.anrTimerServiceEnabled is false.
+ // 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()) {
@@ -1189,7 +1193,8 @@
}
}
- // Required when Flags.anrTimerServiceEnabled is false.
+ // 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) {
diff --git a/services/core/java/com/android/server/am/CoreSettingsObserver.java b/services/core/java/com/android/server/am/CoreSettingsObserver.java
index 4b622f5..903cb7b 100644
--- a/services/core/java/com/android/server/am/CoreSettingsObserver.java
+++ b/services/core/java/com/android/server/am/CoreSettingsObserver.java
@@ -175,13 +175,15 @@
TextFlags.ENABLE_NEW_CONTEXT_MENU_DEFAULT));
// Register all text aconfig flags.
- for (String flag : TextFlags.TEXT_ACONFIGS_FLAGS) {
+ for (int i = 0; i < TextFlags.TEXT_ACONFIGS_FLAGS.length; i++) {
+ final String flag = TextFlags.TEXT_ACONFIGS_FLAGS[i];
+ final boolean defaultValue = TextFlags.TEXT_ACONFIG_DEFAULT_VALUE[i];
sDeviceConfigEntries.add(new DeviceConfigEntry<Boolean>(
TextFlags.NAMESPACE,
flag,
TextFlags.getKeyForFlag(flag),
boolean.class,
- false)); // All aconfig flags are false by default.
+ defaultValue));
}
// add other device configs here...
}
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 4572766..e0e6cad 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -1439,7 +1439,7 @@
}
public static long computeNextPssTime(int procState, ProcStateMemTracker tracker, boolean test,
- boolean sleeping, long now) {
+ boolean sleeping, long now, long earliest) {
boolean first;
float scalingFactor;
final int memState = sProcStateToProcMem[procState];
@@ -1470,7 +1470,7 @@
if (delay > PSS_MAX_INTERVAL) {
delay = PSS_MAX_INTERVAL;
}
- return now + delay;
+ return Math.max(now + delay, earliest);
}
long getMemLevel(int adjustment) {
diff --git a/services/core/java/com/android/server/am/ProcessProfileRecord.java b/services/core/java/com/android/server/am/ProcessProfileRecord.java
index db74f1a..940c58b 100644
--- a/services/core/java/com/android/server/am/ProcessProfileRecord.java
+++ b/services/core/java/com/android/server/am/ProcessProfileRecord.java
@@ -142,6 +142,11 @@
final AtomicLong mCurCpuTime = new AtomicLong(0);
/**
+ * How long the process has spent on waiting in the runqueue since fork.
+ */
+ final AtomicLong mLastCpuDelayTime = new AtomicLong(0);
+
+ /**
* Last selected memory trimming level.
*/
@CompositeRWLock({"mService", "mProcLock"})
@@ -570,7 +575,11 @@
@GuardedBy("mProfilerLock")
long computeNextPssTime(int procState, boolean test, boolean sleeping, long now) {
- return ProcessList.computeNextPssTime(procState, mProcStateMemTracker, test, sleeping, now);
+ return ProcessList.computeNextPssTime(procState, mProcStateMemTracker, test, sleeping, now,
+ // Cap the Pss time to make sure no Pss is collected during the very few
+ // minutes after the system is boot, given the system is already busy.
+ Math.max(mService.mBootCompletedTimestamp, mService.mLastIdleTime)
+ + mService.mConstants.FULL_PSS_MIN_INTERVAL);
}
private static void commitNextPssTime(ProcStateMemTracker tracker) {
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index 3771c05..f02b8c7 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -511,8 +511,8 @@
pw.print(prefix); pw.print("pid="); pw.println(mPid);
pw.print(prefix); pw.print("lastActivityTime=");
TimeUtils.formatDuration(mLastActivityTime, nowUptime, pw);
- pw.print(prefix); pw.print("startUptimeTime=");
- TimeUtils.formatDuration(mStartElapsedTime, nowUptime, pw);
+ pw.print(prefix); pw.print("startUpTime=");
+ TimeUtils.formatDuration(mStartUptime, nowUptime, pw);
pw.print(prefix); pw.print("startElapsedTime=");
TimeUtils.formatDuration(mStartElapsedTime, nowElapsedTime, pw);
pw.println();
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index 1ba1f55..16e3fdf2 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -163,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;
@@ -261,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) {
@@ -332,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/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 1a12fcd..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 e42b664..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
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/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index 8736a53..ac7d9c1 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -221,9 +221,8 @@
/** Flags used when connecting to a sync adapter service */
private static final Context.BindServiceFlags SYNC_ADAPTER_CONNECTION_FLAGS =
- Context.BindServiceFlags.of(
- Context.BIND_FILTER_OUT_QUARANTINED_COMPONENTS | Context.BIND_AUTO_CREATE
- | Context.BIND_NOT_FOREGROUND | Context.BIND_ALLOW_OOM_MANAGEMENT);
+ Context.BindServiceFlags.of(Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND
+ | Context.BIND_ALLOW_OOM_MANAGEMENT);
/** Singleton instance. */
@GuardedBy("SyncManager.class")
diff --git a/services/core/java/com/android/server/display/DeviceStateToLayoutMap.java b/services/core/java/com/android/server/display/DeviceStateToLayoutMap.java
index 472c1f5..1ac3a12 100644
--- a/services/core/java/com/android/server/display/DeviceStateToLayoutMap.java
+++ b/services/core/java/com/android/server/display/DeviceStateToLayoutMap.java
@@ -135,7 +135,8 @@
leadDisplayAddress,
d.getBrightnessThrottlingMapId(),
d.getRefreshRateZoneId(),
- d.getRefreshRateThermalThrottlingMapId());
+ d.getRefreshRateThermalThrottlingMapId(),
+ d.getPowerThrottlingMapId());
}
layout.postProcessLocked();
}
diff --git a/services/core/java/com/android/server/display/DisplayDevice.java b/services/core/java/com/android/server/display/DisplayDevice.java
index 098cb87..9f4f787 100644
--- a/services/core/java/com/android/server/display/DisplayDevice.java
+++ b/services/core/java/com/android/server/display/DisplayDevice.java
@@ -23,7 +23,7 @@
import android.content.Context;
import android.graphics.Point;
import android.graphics.Rect;
-import android.hardware.display.DisplayManagerInternal;
+import android.hardware.display.DisplayManagerInternal.DisplayOffloadSession;
import android.hardware.display.DisplayViewport;
import android.os.IBinder;
import android.util.Slog;
@@ -201,20 +201,6 @@
* @param state The new display state.
* @param brightnessState The new display brightnessState.
* @param sdrBrightnessState The new display brightnessState for SDR layers.
- * @return A runnable containing work to be deferred until after we have
- * exited the critical section, or null if none.
- */
- public Runnable requestDisplayStateLocked(int state, float brightnessState,
- float sdrBrightnessState) {
- return requestDisplayStateLocked(state, brightnessState, sdrBrightnessState, null);
- }
-
- /**
- * Sets the display state, if supported.
- *
- * @param state The new display state.
- * @param brightnessState The new display brightnessState.
- * @param sdrBrightnessState The new display brightnessState for SDR layers.
* @param displayOffloadSession {@link DisplayOffloadSession} associated with current device.
* @return A runnable containing work to be deferred until after we have exited the critical
* section, or null if none.
@@ -223,7 +209,7 @@
int state,
float brightnessState,
float sdrBrightnessState,
- @Nullable DisplayManagerInternal.DisplayOffloadSession displayOffloadSession) {
+ @Nullable DisplayOffloadSession displayOffloadSession) {
return null;
}
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index d372f30..5b87eea 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;
@@ -493,6 +494,7 @@
// If we would like to keep a particular eye on a package, we can set the package name.
private final boolean mExtraDisplayEventLogging;
+ private final String mExtraDisplayLoggingPackageName;
private final BroadcastReceiver mIdleModeReceiver = new BroadcastReceiver() {
@Override
@@ -522,6 +524,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 +559,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);
@@ -580,8 +585,8 @@
mOverlayProperties = SurfaceControl.getOverlaySupport();
mSystemReady = false;
mConfigParameterProvider = new DeviceConfigParameterProvider(DeviceConfigInterface.REAL);
- final String name = DisplayProperties.debug_vri_package().orElse(null);
- mExtraDisplayEventLogging = !TextUtils.isEmpty(name);
+ mExtraDisplayLoggingPackageName = DisplayProperties.debug_vri_package().orElse(null);
+ mExtraDisplayEventLogging = !TextUtils.isEmpty(mExtraDisplayLoggingPackageName);
}
public void setupSchedulerPolicies() {
@@ -650,6 +655,7 @@
}
mDisplayModeDirector.onBootCompleted();
mLogicalDisplayMapper.onBootCompleted();
+ mDisplayNotificationManager.onBootCompleted();
}
}
@@ -752,7 +758,8 @@
mContext.registerReceiver(mIdleModeReceiver, filter);
- mSmallAreaDetectionController = SmallAreaDetectionController.create(mContext);
+ mSmallAreaDetectionController = (mFlags.isSmallAreaDetectionEnabled())
+ ? SmallAreaDetectionController.create(mContext) : null;
}
@VisibleForTesting
@@ -784,6 +791,10 @@
}
}
+ DisplayNotificationManager getDisplayNotificationManager() {
+ return mDisplayNotificationManager;
+ }
+
private void loadStableDisplayValuesLocked() {
final Point size = mPersistentDataStore.getStableDisplaySize();
if (size.x > 0 && size.y > 0) {
@@ -1776,7 +1787,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
@@ -2924,12 +2936,17 @@
// Only send updates outside of DisplayManagerService for enabled displays
if (display.isEnabledLocked()) {
sendDisplayEventLocked(display, event);
+ } else if (mExtraDisplayEventLogging) {
+ Slog.i(TAG, "Not Sending Display Event; display is not enabled: " + display);
}
}
private void sendDisplayEventLocked(@NonNull LogicalDisplay display, @DisplayEvent int event) {
int displayId = display.getDisplayIdLocked();
Message msg = mHandler.obtainMessage(MSG_DELIVER_DISPLAY_EVENT, displayId, event);
+ if (mExtraDisplayEventLogging) {
+ Slog.i(TAG, "Deliver Display Event on Handler: " + event);
+ }
mHandler.sendMessage(msg);
}
@@ -2995,6 +3012,10 @@
// For cached apps, save the pending event until it becomes non-cached
synchronized (mPendingCallbackSelfLocked) {
PendingCallback pendingCallback = mPendingCallbackSelfLocked.get(uid);
+ if (extraLogging(callbackRecord.mPackageName)) {
+ Slog.i(TAG,
+ "Uid is cached: " + uid + ", pendingCallback: " + pendingCallback);
+ }
if (pendingCallback == null) {
mPendingCallbackSelfLocked.put(uid,
new PendingCallback(callbackRecord, displayId, event));
@@ -3009,6 +3030,10 @@
mTempCallbacks.clear();
}
+ private boolean extraLogging(String packageName) {
+ return mExtraDisplayEventLogging && mExtraDisplayLoggingPackageName.equals(packageName);
+ }
+
// Runs on Handler thread.
// Delivers display group event notifications to callbacks.
private void deliverDisplayGroupEvent(int groupId, int event) {
@@ -3191,9 +3216,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() {
@@ -3451,6 +3477,7 @@
public final int mUid;
private final IDisplayManagerCallback mCallback;
private @EventsMask AtomicLong mEventsMask;
+ private final String mPackageName;
public boolean mWifiDisplayScanRequested;
@@ -3460,6 +3487,9 @@
mUid = uid;
mCallback = callback;
mEventsMask = new AtomicLong(eventsMask);
+
+ String[] packageNames = mContext.getPackageManager().getPackagesForUid(uid);
+ mPackageName = packageNames == null ? null : packageNames[0];
}
public void updateEventsMask(@EventsMask long eventsMask) {
@@ -3468,7 +3498,8 @@
@Override
public void binderDied() {
- if (DEBUG) {
+ if (DEBUG || mExtraDisplayEventLogging && mExtraDisplayLoggingPackageName.equals(
+ mPackageName)) {
Slog.d(TAG, "Display listener for pid " + mPid + " died.");
}
onCallbackDied(this);
@@ -3479,6 +3510,11 @@
*/
public boolean notifyDisplayEventAsync(int displayId, @DisplayEvent int event) {
if (!shouldSendEvent(event)) {
+ if (mExtraDisplayEventLogging && mExtraDisplayLoggingPackageName.equals(
+ mPackageName)) {
+ Slog.i(TAG,
+ "Not sending displayEvent: " + event + " due to mask:" + mEventsMask);
+ }
return true;
}
diff --git a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
index 9b022d8..d97c8e7 100644
--- a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
+++ b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
@@ -30,6 +30,8 @@
class DisplayManagerShellCommand extends ShellCommand {
private static final String TAG = "DisplayManagerShellCommand";
+ private static final String NOTIFICATION_TYPES =
+ "on-hotplug-error, on-link-training-failure, on-cable-dp-incapable";
private final DisplayManagerService mService;
private final DisplayManagerFlags mFlags;
@@ -46,6 +48,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 +108,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 +182,39 @@
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;
+ case "on-link-training-failure":
+ mService.getDisplayNotificationManager().onDisplayPortLinkTrainingFailure();
+ break;
+ case "on-cable-dp-incapable":
+ mService.getDisplayNotificationManager().onCableNotCapableDisplayPort();
+ 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/DisplayPowerController2.java b/services/core/java/com/android/server/display/DisplayPowerController2.java
index 1652871..7d9c018 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController2.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController2.java
@@ -506,7 +506,6 @@
mTag = "DisplayPowerController2[" + mDisplayId + "]";
mThermalBrightnessThrottlingDataId =
logicalDisplay.getDisplayInfoLocked().thermalBrightnessThrottlingDataId;
-
mDisplayDevice = mLogicalDisplay.getPrimaryDisplayDeviceLocked();
mUniqueDisplayId = logicalDisplay.getPrimaryDisplayDeviceLocked().getUniqueId();
mDisplayStatsId = mUniqueDisplayId.hashCode();
@@ -566,8 +565,8 @@
modeChangeCallback::run, new BrightnessClamperController.DisplayDeviceData(
mUniqueDisplayId,
mThermalBrightnessThrottlingDataId,
- mDisplayDeviceConfig
- ), mContext);
+ logicalDisplay.getPowerThrottlingDataIdLocked(),
+ mDisplayDeviceConfig), mContext, flags);
// Seed the cached brightness
saveBrightnessInfo(getScreenBrightnessSetting());
mAutomaticBrightnessStrategy =
@@ -821,10 +820,8 @@
.getDisplayDeviceInfoLocked().type == Display.TYPE_INTERNAL;
final String thermalBrightnessThrottlingDataId =
mLogicalDisplay.getDisplayInfoLocked().thermalBrightnessThrottlingDataId;
-
- mBrightnessClamperController.onDisplayChanged(
- new BrightnessClamperController.DisplayDeviceData(mUniqueDisplayId,
- mThermalBrightnessThrottlingDataId, config));
+ final String powerThrottlingDataId =
+ mLogicalDisplay.getPowerThrottlingDataIdLocked();
mHandler.postAtTime(() -> {
boolean changed = false;
@@ -858,6 +855,14 @@
}
mIsDisplayInternal = isDisplayInternal;
+ // using local variables here, when mBrightnessThrottler is removed,
+ // mThermalBrightnessThrottlingDataId could be removed as well
+ // changed = true will be not needed - clampers are maintaining their state and
+ // will call updatePowerState if needed.
+ mBrightnessClamperController.onDisplayChanged(
+ new BrightnessClamperController.DisplayDeviceData(uniqueId,
+ thermalBrightnessThrottlingDataId, powerThrottlingDataId, config));
+
if (changed) {
updatePowerState();
}
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/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java
index bd82b81..3d4209e 100644
--- a/services/core/java/com/android/server/display/LogicalDisplay.java
+++ b/services/core/java/com/android/server/display/LogicalDisplay.java
@@ -190,6 +190,11 @@
private SurfaceControl.RefreshRateRange mLayoutLimitedRefreshRate;
/**
+ * The ID of the power throttling data that should be used.
+ */
+ private String mPowerThrottlingDataId;
+
+ /**
* RefreshRateRange limitation for @Temperature.ThrottlingStatus
*/
@NonNull
@@ -205,6 +210,7 @@
mIsEnabled = true;
mIsInTransition = false;
mThermalBrightnessThrottlingDataId = DisplayDeviceConfig.DEFAULT_ID;
+ mPowerThrottlingDataId = DisplayDeviceConfig.DEFAULT_ID;
mBaseDisplayInfo.thermalBrightnessThrottlingDataId = mThermalBrightnessThrottlingDataId;
}
@@ -911,6 +917,25 @@
}
/**
+ * @param powerThrottlingDataId The ID of the brightness throttling data that this
+ * display should use.
+ */
+ public void setPowerThrottlingDataIdLocked(String powerThrottlingDataId) {
+ if (!Objects.equals(powerThrottlingDataId, mPowerThrottlingDataId)) {
+ mPowerThrottlingDataId = powerThrottlingDataId;
+ mDirty = true;
+ }
+ }
+
+ /**
+ * Returns powerThrottlingDataId which is the ID of the brightness
+ * throttling data that this display should use.
+ */
+ public String getPowerThrottlingDataIdLocked() {
+ return mPowerThrottlingDataId;
+ }
+
+ /**
* Sets the display of which this display is a follower, regarding brightness or other
* properties. If set to {@link Layout#NO_LEAD_DISPLAY}, this display does not follow any
* others, and has the potential to be a lead display to others.
@@ -976,6 +1001,7 @@
pw.println("mLeadDisplayId=" + mLeadDisplayId);
pw.println("mLayoutLimitedRefreshRate=" + mLayoutLimitedRefreshRate);
pw.println("mThermalRefreshRateThrottling=" + mThermalRefreshRateThrottling);
+ pw.println("mPowerThrottlingDataId=" + mPowerThrottlingDataId);
}
@Override
diff --git a/services/core/java/com/android/server/display/LogicalDisplayMapper.java b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
index b3b16ad..c55bc62 100644
--- a/services/core/java/com/android/server/display/LogicalDisplayMapper.java
+++ b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
@@ -1123,13 +1123,15 @@
displayLayout.getRefreshRateThermalThrottlingMapId()
)
);
-
setEnabledLocked(newDisplay, displayLayout.isEnabled());
newDisplay.setThermalBrightnessThrottlingDataIdLocked(
displayLayout.getThermalBrightnessThrottlingMapId() == null
? DisplayDeviceConfig.DEFAULT_ID
: displayLayout.getThermalBrightnessThrottlingMapId());
-
+ newDisplay.setPowerThrottlingDataIdLocked(
+ displayLayout.getPowerThrottlingMapId() == null
+ ? DisplayDeviceConfig.DEFAULT_ID
+ : displayLayout.getPowerThrottlingMapId());
newDisplay.setDisplayGroupNameLocked(displayLayout.getDisplayGroupName());
}
}
diff --git a/services/core/java/com/android/server/display/SmallAreaDetectionController.java b/services/core/java/com/android/server/display/SmallAreaDetectionController.java
index adaa539..bf384b0 100644
--- a/services/core/java/com/android/server/display/SmallAreaDetectionController.java
+++ b/services/core/java/com/android/server/display/SmallAreaDetectionController.java
@@ -20,6 +20,7 @@
import android.annotation.Nullable;
import android.content.Context;
import android.content.pm.PackageManagerInternal;
+import android.os.UserHandle;
import android.provider.DeviceConfig;
import android.provider.DeviceConfigInterface;
import android.util.ArrayMap;
@@ -30,15 +31,14 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.BackgroundThread;
import com.android.server.LocalServices;
-import com.android.server.pm.UserManagerInternal;
+import com.android.server.pm.pkg.PackageStateInternal;
import java.io.PrintWriter;
-import java.util.Arrays;
import java.util.Map;
final class SmallAreaDetectionController {
- private static native void nativeUpdateSmallAreaDetection(int[] uids, float[] thresholds);
- private static native void nativeSetSmallAreaDetectionThreshold(int uid, float threshold);
+ private static native void nativeUpdateSmallAreaDetection(int[] appIds, float[] thresholds);
+ private static native void nativeSetSmallAreaDetectionThreshold(int appId, float threshold);
// TODO(b/281720315): Move this to DeviceConfig once server side ready.
private static final String KEY_SMALL_AREA_DETECTION_ALLOWLIST =
@@ -47,12 +47,8 @@
private final Object mLock = new Object();
private final Context mContext;
private final PackageManagerInternal mPackageManager;
- private final UserManagerInternal mUserManager;
@GuardedBy("mLock")
private final Map<String, Float> mAllowPkgMap = new ArrayMap<>();
- // TODO(b/298722189): Update allowlist when user changes
- @GuardedBy("mLock")
- private int[] mUserIds;
static SmallAreaDetectionController create(@NonNull Context context) {
final SmallAreaDetectionController controller =
@@ -67,7 +63,6 @@
SmallAreaDetectionController(Context context, DeviceConfigInterface deviceConfig) {
mContext = context;
mPackageManager = LocalServices.getService(PackageManagerInternal.class);
- mUserManager = LocalServices.getService(UserManagerInternal.class);
deviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
BackgroundThread.getExecutor(),
new SmallAreaDetectionController.OnPropertiesChangedListener());
@@ -76,6 +71,7 @@
@VisibleForTesting
void updateAllowlist(@Nullable String property) {
+ final Map<String, Float> allowPkgMap = new ArrayMap<>();
synchronized (mLock) {
mAllowPkgMap.clear();
if (property != null) {
@@ -86,8 +82,11 @@
.getStringArray(R.array.config_smallAreaDetectionAllowlist);
for (String defaultMapString : defaultMapStrings) putToAllowlist(defaultMapString);
}
- updateSmallAreaDetection();
+
+ if (mAllowPkgMap.isEmpty()) return;
+ allowPkgMap.putAll(mAllowPkgMap);
}
+ updateSmallAreaDetection(allowPkgMap);
}
@GuardedBy("mLock")
@@ -105,43 +104,32 @@
}
}
- @GuardedBy("mLock")
- private void updateUidListForAllUsers(SparseArray<Float> list, String pkg, float threshold) {
- for (int i = 0; i < mUserIds.length; i++) {
- final int userId = mUserIds[i];
- final int uid = mPackageManager.getPackageUid(pkg, 0, userId);
- if (uid > 0) list.put(uid, threshold);
- }
- }
-
- @GuardedBy("mLock")
- private void updateSmallAreaDetection() {
- if (mAllowPkgMap.isEmpty()) return;
-
- mUserIds = mUserManager.getUserIds();
-
- final SparseArray<Float> uidThresholdList = new SparseArray<>();
- for (String pkg : mAllowPkgMap.keySet()) {
- final float threshold = mAllowPkgMap.get(pkg);
- updateUidListForAllUsers(uidThresholdList, pkg, threshold);
+ private void updateSmallAreaDetection(Map<String, Float> allowPkgMap) {
+ final SparseArray<Float> appIdThresholdList = new SparseArray(allowPkgMap.size());
+ for (String pkg : allowPkgMap.keySet()) {
+ final float threshold = allowPkgMap.get(pkg);
+ final PackageStateInternal stage = mPackageManager.getPackageStateInternal(pkg);
+ if (stage != null) {
+ appIdThresholdList.put(stage.getAppId(), threshold);
+ }
}
- final int[] uids = new int[uidThresholdList.size()];
- final float[] thresholds = new float[uidThresholdList.size()];
- for (int i = 0; i < uidThresholdList.size(); i++) {
- uids[i] = uidThresholdList.keyAt(i);
- thresholds[i] = uidThresholdList.valueAt(i);
+ final int[] appIds = new int[appIdThresholdList.size()];
+ final float[] thresholds = new float[appIdThresholdList.size()];
+ for (int i = 0; i < appIdThresholdList.size(); i++) {
+ appIds[i] = appIdThresholdList.keyAt(i);
+ thresholds[i] = appIdThresholdList.valueAt(i);
}
- updateSmallAreaDetection(uids, thresholds);
+ updateSmallAreaDetection(appIds, thresholds);
}
@VisibleForTesting
- void updateSmallAreaDetection(int[] uids, float[] thresholds) {
- nativeUpdateSmallAreaDetection(uids, thresholds);
+ void updateSmallAreaDetection(int[] appIds, float[] thresholds) {
+ nativeUpdateSmallAreaDetection(appIds, thresholds);
}
- void setSmallAreaDetectionThreshold(int uid, float threshold) {
- nativeSetSmallAreaDetectionThreshold(uid, threshold);
+ void setSmallAreaDetectionThreshold(int appId, float threshold) {
+ nativeSetSmallAreaDetectionThreshold(appId, threshold);
}
void dump(PrintWriter pw) {
@@ -151,7 +139,6 @@
for (String pkg : mAllowPkgMap.keySet()) {
pw.println(" " + pkg + " threshold = " + mAllowPkgMap.get(pkg));
}
- pw.println(" mUserIds=" + Arrays.toString(mUserIds));
}
}
@@ -167,11 +154,15 @@
private final class PackageReceiver implements PackageManagerInternal.PackageListObserver {
@Override
public void onPackageAdded(@NonNull String packageName, int uid) {
+ float threshold = 0.0f;
synchronized (mLock) {
if (mAllowPkgMap.containsKey(packageName)) {
- setSmallAreaDetectionThreshold(uid, mAllowPkgMap.get(packageName));
+ threshold = mAllowPkgMap.get(packageName);
}
}
+ if (threshold > 0.0f) {
+ setSmallAreaDetectionThreshold(UserHandle.getAppId(uid), threshold);
+ }
}
}
}
diff --git a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
index d910e16..b002587 100644
--- a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
@@ -43,6 +43,7 @@
import android.annotation.Nullable;
import android.content.Context;
import android.graphics.Point;
+import android.hardware.display.DisplayManagerInternal.DisplayOffloadSession;
import android.hardware.display.IVirtualDisplayCallback;
import android.hardware.display.VirtualDisplayConfig;
import android.media.projection.IMediaProjection;
@@ -395,7 +396,7 @@
@Override
public Runnable requestDisplayStateLocked(int state, float brightnessState,
- float sdrBrightnessState) {
+ float sdrBrightnessState, DisplayOffloadSession displayOffloadSession) {
if (state != mDisplayState) {
mDisplayState = state;
if (state == Display.STATE_OFF) {
diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamper.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamper.java
index 54a280f..68f72d3 100644
--- a/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamper.java
+++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamper.java
@@ -52,7 +52,8 @@
abstract void stop();
- enum Type {
- THERMAL
+ protected enum Type {
+ THERMAL,
+ POWER
}
}
diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java
index 14637af..787f786 100644
--- a/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java
+++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java
@@ -34,9 +34,12 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.display.DisplayBrightnessState;
import com.android.server.display.DisplayDeviceConfig;
+import com.android.server.display.DisplayDeviceConfig.PowerThrottlingConfigData;
+import com.android.server.display.DisplayDeviceConfig.PowerThrottlingData;
import com.android.server.display.DisplayDeviceConfig.ThermalBrightnessThrottlingData;
import com.android.server.display.brightness.BrightnessReason;
import com.android.server.display.feature.DeviceConfigParameterProvider;
+import com.android.server.display.feature.DisplayManagerFlags;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -48,11 +51,9 @@
*/
public class BrightnessClamperController {
private static final String TAG = "BrightnessClamperController";
-
private final DeviceConfigParameterProvider mDeviceConfigParameterProvider;
private final Handler mHandler;
private final ClamperChangeListener mClamperChangeListenerExternal;
-
private final Executor mExecutor;
private final List<BrightnessClamper<? super DisplayDeviceData>> mClampers;
@@ -64,13 +65,15 @@
private boolean mClamperApplied = false;
public BrightnessClamperController(Handler handler,
- ClamperChangeListener clamperChangeListener, DisplayDeviceData data, Context context) {
- this(new Injector(), handler, clamperChangeListener, data, context);
+ ClamperChangeListener clamperChangeListener, DisplayDeviceData data, Context context,
+ DisplayManagerFlags flags) {
+ this(new Injector(), handler, clamperChangeListener, data, context, flags);
}
@VisibleForTesting
BrightnessClamperController(Injector injector, Handler handler,
- ClamperChangeListener clamperChangeListener, DisplayDeviceData data, Context context) {
+ ClamperChangeListener clamperChangeListener, DisplayDeviceData data, Context context,
+ DisplayManagerFlags flags) {
mDeviceConfigParameterProvider = injector.getDeviceConfigParameterProvider();
mHandler = handler;
mClamperChangeListenerExternal = clamperChangeListener;
@@ -84,7 +87,7 @@
}
};
- mClampers = injector.getClampers(handler, clamperChangeListenerInternal, data);
+ mClampers = injector.getClampers(handler, clamperChangeListenerInternal, data, flags);
mModifiers = injector.getModifiers(context);
mOnPropertiesChangedListener =
properties -> mClampers.forEach(BrightnessClamper::onDeviceConfigChanged);
@@ -144,6 +147,8 @@
return BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE;
} else if (mClamperType == Type.THERMAL) {
return BrightnessInfo.BRIGHTNESS_MAX_REASON_THERMAL;
+ } else if (mClamperType == Type.POWER) {
+ return BrightnessInfo.BRIGHTNESS_MAX_REASON_POWER_IC;
} else {
Slog.wtf(TAG, "BrightnessMaxReason not mapped for type=" + mClamperType);
return BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE;
@@ -193,6 +198,7 @@
mClamperType = clamperType;
mClamperChangeListenerExternal.onChanged();
}
+
}
private void start() {
@@ -219,10 +225,15 @@
}
List<BrightnessClamper<? super DisplayDeviceData>> getClampers(Handler handler,
- ClamperChangeListener clamperChangeListener, DisplayDeviceData data) {
+ ClamperChangeListener clamperChangeListener, DisplayDeviceData data,
+ DisplayManagerFlags flags) {
List<BrightnessClamper<? super DisplayDeviceData>> clampers = new ArrayList<>();
clampers.add(
new BrightnessThermalClamper(handler, clamperChangeListener, data));
+ if (flags.isPowerThrottlingClamperEnabled()) {
+ clampers.add(new BrightnessPowerClamper(handler, clamperChangeListener,
+ data));
+ }
return clampers;
}
@@ -235,21 +246,26 @@
}
/**
- * Data for clampers
+ * Config Data for clampers
*/
- public static class DisplayDeviceData implements BrightnessThermalClamper.ThermalData {
+ public static class DisplayDeviceData implements BrightnessThermalClamper.ThermalData,
+ BrightnessPowerClamper.PowerData {
@NonNull
private final String mUniqueDisplayId;
@NonNull
private final String mThermalThrottlingDataId;
+ @NonNull
+ private final String mPowerThrottlingDataId;
private final DisplayDeviceConfig mDisplayDeviceConfig;
public DisplayDeviceData(@NonNull String uniqueDisplayId,
@NonNull String thermalThrottlingDataId,
+ @NonNull String powerThrottlingDataId,
@NonNull DisplayDeviceConfig displayDeviceConfig) {
mUniqueDisplayId = uniqueDisplayId;
mThermalThrottlingDataId = thermalThrottlingDataId;
+ mPowerThrottlingDataId = powerThrottlingDataId;
mDisplayDeviceConfig = displayDeviceConfig;
}
@@ -272,5 +288,24 @@
return mDisplayDeviceConfig.getThermalBrightnessThrottlingDataMapByThrottlingId().get(
mThermalThrottlingDataId);
}
+
+ @NonNull
+ @Override
+ public String getPowerThrottlingDataId() {
+ return mPowerThrottlingDataId;
+ }
+
+ @Nullable
+ @Override
+ public PowerThrottlingData getPowerThrottlingData() {
+ return mDisplayDeviceConfig.getPowerThrottlingDataMapByThrottlingId().get(
+ mPowerThrottlingDataId);
+ }
+
+ @Nullable
+ @Override
+ public PowerThrottlingConfigData getPowerThrottlingConfigData() {
+ return mDisplayDeviceConfig.getPowerThrottlingConfigData();
+ }
}
}
diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessPowerClamper.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessPowerClamper.java
new file mode 100644
index 0000000..339b589
--- /dev/null
+++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessPowerClamper.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.display.brightness.clamper;
+
+import static com.android.server.display.DisplayDeviceConfig.DEFAULT_ID;
+import static com.android.server.display.brightness.clamper.BrightnessClamperController.ClamperChangeListener;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Handler;
+import android.os.PowerManager;
+import android.os.Temperature;
+import android.provider.DeviceConfigInterface;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.display.DisplayDeviceConfig.PowerThrottlingConfigData;
+import com.android.server.display.DisplayDeviceConfig.PowerThrottlingData;
+import com.android.server.display.DisplayDeviceConfig.PowerThrottlingData.ThrottlingLevel;
+import com.android.server.display.feature.DeviceConfigParameterProvider;
+import com.android.server.display.utils.DeviceConfigParsingUtils;
+
+import java.io.PrintWriter;
+import java.util.List;
+import java.util.Map;
+import java.util.function.BiFunction;
+import java.util.function.Function;
+
+
+class BrightnessPowerClamper extends
+ BrightnessClamper<BrightnessPowerClamper.PowerData> {
+
+ private static final String TAG = "BrightnessPowerClamper";
+ @NonNull
+ private final Injector mInjector;
+ @NonNull
+ private final DeviceConfigParameterProvider mConfigParameterProvider;
+ @NonNull
+ private final Handler mHandler;
+ @NonNull
+ private final ClamperChangeListener mChangeListener;
+ @Nullable
+ private PmicMonitor mPmicMonitor;
+ // data from DeviceConfig, for all displays, for all dataSets
+ // mapOf(uniqueDisplayId to mapOf(dataSetId to PowerThrottlingData))
+ @NonNull
+ private Map<String, Map<String, PowerThrottlingData>>
+ mPowerThrottlingDataOverride = Map.of();
+ // data from DisplayDeviceConfig, for particular display+dataSet
+ @Nullable
+ private PowerThrottlingData mPowerThrottlingDataFromDDC = null;
+ // Active data, if mPowerThrottlingDataOverride contains data for mUniqueDisplayId,
+ // mDataId, then use it, otherwise mPowerThrottlingDataFromDDC.
+ @Nullable
+ private PowerThrottlingData mPowerThrottlingDataActive = null;
+ @Nullable
+ private PowerThrottlingConfigData mPowerThrottlingConfigData = null;
+
+ private @Temperature.ThrottlingStatus int mCurrentThermalLevel = Temperature.THROTTLING_NONE;
+ private float mCurrentAvgPowerConsumed = 0;
+ @Nullable
+ private String mUniqueDisplayId = null;
+ @Nullable
+ private String mDataId = null;
+
+ private final BiFunction<String, String, ThrottlingLevel> mDataPointMapper = (key, value) -> {
+ try {
+ int status = DeviceConfigParsingUtils.parseThermalStatus(key);
+ float powerQuota = Float.parseFloat(value);
+ return new ThrottlingLevel(status, powerQuota);
+ } catch (IllegalArgumentException iae) {
+ return null;
+ }
+ };
+
+ private final Function<List<ThrottlingLevel>, PowerThrottlingData>
+ mDataSetMapper = PowerThrottlingData::create;
+
+
+ BrightnessPowerClamper(Handler handler, ClamperChangeListener listener,
+ PowerData powerData) {
+ this(new Injector(), handler, listener, powerData);
+ }
+
+ @VisibleForTesting
+ BrightnessPowerClamper(Injector injector, Handler handler, ClamperChangeListener listener,
+ PowerData powerData) {
+ mInjector = injector;
+ mConfigParameterProvider = injector.getDeviceConfigParameterProvider();
+ mHandler = handler;
+ mChangeListener = listener;
+
+ mHandler.post(() -> {
+ setDisplayData(powerData);
+ loadOverrideData();
+ start();
+ });
+
+ }
+
+ @Override
+ @NonNull
+ BrightnessClamper.Type getType() {
+ return Type.POWER;
+ }
+
+ @Override
+ void onDeviceConfigChanged() {
+ mHandler.post(() -> {
+ loadOverrideData();
+ recalculateActiveData();
+ });
+ }
+
+ @Override
+ void onDisplayChanged(PowerData data) {
+ mHandler.post(() -> {
+ setDisplayData(data);
+ recalculateActiveData();
+ });
+ }
+
+ @Override
+ void stop() {
+ if (mPmicMonitor != null) {
+ mPmicMonitor.shutdown();
+ }
+ }
+
+ /**
+ * Dumps the state of BrightnessPowerClamper.
+ */
+ public void dump(PrintWriter pw) {
+ pw.println("BrightnessPowerClamper:");
+ pw.println(" mCurrentAvgPowerConsumed=" + mCurrentAvgPowerConsumed);
+ pw.println(" mUniqueDisplayId=" + mUniqueDisplayId);
+ pw.println(" mCurrentThermalLevel=" + mCurrentThermalLevel);
+ pw.println(" mPowerThrottlingDataFromDDC=" + (mPowerThrottlingDataFromDDC == null ? "null"
+ : mPowerThrottlingDataFromDDC.toString()));
+ super.dump(pw);
+ }
+
+ private void recalculateActiveData() {
+ if (mUniqueDisplayId == null || mDataId == null) {
+ return;
+ }
+ mPowerThrottlingDataActive = mPowerThrottlingDataOverride
+ .getOrDefault(mUniqueDisplayId, Map.of()).getOrDefault(mDataId,
+ mPowerThrottlingDataFromDDC);
+ if (mPowerThrottlingDataActive != null) {
+ if (mPmicMonitor != null) {
+ mPmicMonitor.stop();
+ mPmicMonitor.start();
+ }
+ } else {
+ if (mPmicMonitor != null) {
+ mPmicMonitor.stop();
+ }
+ }
+ recalculateBrightnessCap();
+ }
+
+ private void loadOverrideData() {
+ String throttlingDataOverride = mConfigParameterProvider.getPowerThrottlingData();
+ mPowerThrottlingDataOverride = DeviceConfigParsingUtils.parseDeviceConfigMap(
+ throttlingDataOverride, mDataPointMapper, mDataSetMapper);
+ }
+
+ private void setDisplayData(@NonNull PowerData data) {
+ mUniqueDisplayId = data.getUniqueDisplayId();
+ mDataId = data.getPowerThrottlingDataId();
+ mPowerThrottlingDataFromDDC = data.getPowerThrottlingData();
+ if (mPowerThrottlingDataFromDDC == null && !DEFAULT_ID.equals(mDataId)) {
+ Slog.wtf(TAG,
+ "Power throttling data is missing for powerThrottlingDataId=" + mDataId);
+ }
+
+ mPowerThrottlingConfigData = data.getPowerThrottlingConfigData();
+ if (mPowerThrottlingConfigData == null) {
+ Slog.d(TAG,
+ "Power throttling data is missing for configuration data.");
+ }
+ }
+
+ private void recalculateBrightnessCap() {
+ boolean isActive = false;
+ float targetBrightnessCap = PowerManager.BRIGHTNESS_MAX;
+ float powerQuota = getPowerQuotaForThermalStatus(mCurrentThermalLevel);
+ if (mPowerThrottlingDataActive == null) {
+ return;
+ }
+ if (powerQuota > 0 && mCurrentAvgPowerConsumed > powerQuota) {
+ isActive = true;
+ // calculate new brightness Cap.
+ // Brightness has a linear relation to power-consumed.
+ targetBrightnessCap =
+ (powerQuota / mCurrentAvgPowerConsumed) * PowerManager.BRIGHTNESS_MAX;
+ // Cap to lowest allowed brightness on device.
+ targetBrightnessCap = Math.max(targetBrightnessCap,
+ mPowerThrottlingConfigData.brightnessLowestCapAllowed);
+ }
+
+ if (mBrightnessCap != targetBrightnessCap || mIsActive != isActive) {
+ mIsActive = isActive;
+ mBrightnessCap = targetBrightnessCap;
+ mChangeListener.onChanged();
+ }
+ }
+
+ private float getPowerQuotaForThermalStatus(@Temperature.ThrottlingStatus int thermalStatus) {
+ float powerQuota = 0f;
+ if (mPowerThrottlingDataActive != null) {
+ // Throttling levels are sorted by increasing severity
+ for (ThrottlingLevel level : mPowerThrottlingDataActive.throttlingLevels) {
+ if (level.thermalStatus <= thermalStatus) {
+ powerQuota = level.powerQuotaMilliWatts;
+ } else {
+ // Throttling levels that are greater than the current status are irrelevant
+ break;
+ }
+ }
+ }
+ return powerQuota;
+ }
+
+ private void recalculatePowerQuotaChange(float avgPowerConsumed, int thermalStatus) {
+ mHandler.post(() -> {
+ mCurrentThermalLevel = thermalStatus;
+ mCurrentAvgPowerConsumed = avgPowerConsumed;
+ recalculateBrightnessCap();
+ });
+ }
+
+ private void start() {
+ if (mPowerThrottlingConfigData == null) {
+ return;
+ }
+ PowerChangeListener listener = (powerConsumed, thermalStatus) -> {
+ recalculatePowerQuotaChange(powerConsumed, thermalStatus);
+ };
+ mPmicMonitor =
+ mInjector.getPmicMonitor(listener, mPowerThrottlingConfigData.pollingWindowMillis);
+ mPmicMonitor.start();
+ }
+
+ public interface PowerData {
+ @NonNull
+ String getUniqueDisplayId();
+
+ @NonNull
+ String getPowerThrottlingDataId();
+
+ @Nullable
+ PowerThrottlingData getPowerThrottlingData();
+
+ @Nullable
+ PowerThrottlingConfigData getPowerThrottlingConfigData();
+ }
+
+ /**
+ * Power change listener
+ */
+ @FunctionalInterface
+ public interface PowerChangeListener {
+ /**
+ * Notifies that power state changed from power controller.
+ */
+ void onChanged(float avgPowerConsumed, @Temperature.ThrottlingStatus int thermalStatus);
+ }
+
+ @VisibleForTesting
+ static class Injector {
+ PmicMonitor getPmicMonitor(PowerChangeListener listener, int pollingTime) {
+ return new PmicMonitor(listener, pollingTime);
+ }
+
+ DeviceConfigParameterProvider getDeviceConfigParameterProvider() {
+ return new DeviceConfigParameterProvider(DeviceConfigInterface.REAL);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/display/brightness/clamper/PmicMonitor.java b/services/core/java/com/android/server/display/brightness/clamper/PmicMonitor.java
new file mode 100644
index 0000000..26784f23
--- /dev/null
+++ b/services/core/java/com/android/server/display/brightness/clamper/PmicMonitor.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.brightness.clamper;
+
+import static com.android.server.display.brightness.clamper.BrightnessPowerClamper.PowerChangeListener;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.hardware.power.stats.EnergyConsumer;
+import android.hardware.power.stats.EnergyConsumerResult;
+import android.hardware.power.stats.EnergyConsumerType;
+import android.os.IThermalService;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.Temperature;
+import android.power.PowerStatsInternal;
+import android.util.IntArray;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.LocalServices;
+
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Monitors the display consumed power and helps make informed decision,
+ * regarding overconsumption.
+ */
+public class PmicMonitor {
+ private static final String TAG = "PmicMonitor";
+
+ // The executor to periodically monitor the display power.
+ private final ScheduledExecutorService mExecutor;
+ @NonNull
+ private final PowerChangeListener mPowerChangeListener;
+ private final long mPowerMonitorPeriodConfigSecs;
+ private final PowerStatsInternal mPowerStatsInternal;
+ @VisibleForTesting final IThermalService mThermalService;
+ private ScheduledFuture<?> mPmicMonitorFuture;
+ private float mLastEnergyConsumed = 0;
+ private float mCurrentAvgPower = 0;
+ private Temperature mCurrentTemperature;
+ private long mCurrentTimestampMillis = 0;
+
+ PmicMonitor(PowerChangeListener listener, int powerMonitorPeriodConfigSecs) {
+ mPowerChangeListener = listener;
+ mPowerStatsInternal = LocalServices.getService(PowerStatsInternal.class);
+ mThermalService = IThermalService.Stub.asInterface(
+ ServiceManager.getService(Context.THERMAL_SERVICE));
+ // start a periodic worker thread.
+ mExecutor = Executors.newSingleThreadScheduledExecutor();
+ mPowerMonitorPeriodConfigSecs = (long) powerMonitorPeriodConfigSecs;
+ }
+
+ @Nullable
+ private Temperature getDisplayTemperature() {
+ Temperature retTemperature = null;
+ try {
+ Temperature[] temperatures;
+ // TODO b/279114539 Try DISPLAY first and then fallback to SKIN.
+ temperatures = mThermalService.getCurrentTemperaturesWithType(
+ Temperature.TYPE_SKIN);
+ if (temperatures.length > 1) {
+ Slog.w(TAG, "Multiple skin temperatures not allowed!");
+ }
+ if (temperatures.length > 0) {
+ retTemperature = temperatures[0];
+ }
+ } catch (RemoteException e) {
+ Slog.w(TAG, "getDisplayTemperature failed" + e);
+ }
+ return retTemperature;
+ }
+
+ private void capturePeriodicDisplayPower() {
+ final EnergyConsumer[] energyConsumers = mPowerStatsInternal.getEnergyConsumerInfo();
+ if (energyConsumers == null || energyConsumers.length == 0) {
+ return;
+ }
+ final IntArray energyConsumerIds = new IntArray();
+ for (int i = 0; i < energyConsumers.length; i++) {
+ if (energyConsumers[i].type == EnergyConsumerType.DISPLAY) {
+ energyConsumerIds.add(energyConsumers[i].id);
+ }
+ }
+
+ if (energyConsumerIds.size() == 0) {
+ Slog.w(TAG, "DISPLAY energyConsumerIds size is null");
+ return;
+ }
+ CompletableFuture<EnergyConsumerResult[]> futureECRs =
+ mPowerStatsInternal.getEnergyConsumedAsync(energyConsumerIds.toArray());
+ if (futureECRs == null) {
+ Slog.w(TAG, "Energy consumers results are null");
+ return;
+ }
+
+ EnergyConsumerResult[] displayResults;
+ try {
+ displayResults = futureECRs.get();
+ } catch (InterruptedException e) {
+ Slog.w(TAG, "timeout or interrupt reading getEnergyConsumedAsync failed", e);
+ displayResults = null;
+ } catch (ExecutionException e) {
+ Slog.wtf(TAG, "exception reading getEnergyConsumedAsync: ", e);
+ displayResults = null;
+ }
+
+ if (displayResults == null || displayResults.length == 0) {
+ Slog.w(TAG, "displayResults are null");
+ return;
+ }
+ // Support for only 1 display rail.
+ float energyConsumed = (displayResults[0].energyUWs - mLastEnergyConsumed);
+ float timeIntervalSeconds =
+ (displayResults[0].timestampMs - mCurrentTimestampMillis) / 1000.f;
+ // energy consumed is received in microwatts-seconds.
+ float currentPower = energyConsumed / timeIntervalSeconds;
+ // convert power received in microwatts to milliwatts.
+ currentPower = currentPower / 1000.f;
+
+ // capture thermal state.
+ Temperature displayTemperature = getDisplayTemperature();
+ mCurrentAvgPower = currentPower;
+ mCurrentTemperature = displayTemperature;
+ mLastEnergyConsumed = displayResults[0].energyUWs;
+ mCurrentTimestampMillis = displayResults[0].timestampMs;
+ if (mCurrentTemperature != null) {
+ mPowerChangeListener.onChanged(mCurrentAvgPower, mCurrentTemperature.getStatus());
+ }
+ }
+
+ /**
+ * Start polling the power IC.
+ */
+ public void start() {
+ if (mPowerStatsInternal == null) {
+ Slog.w(TAG, "Power stats service not found for monitoring.");
+ return;
+ }
+ if (mThermalService == null) {
+ Slog.w(TAG, "Thermal service not found.");
+ return;
+ }
+ if (mPmicMonitorFuture == null) {
+ mPmicMonitorFuture = mExecutor.scheduleAtFixedRate(
+ this::capturePeriodicDisplayPower,
+ mPowerMonitorPeriodConfigSecs,
+ mPowerMonitorPeriodConfigSecs,
+ TimeUnit.SECONDS);
+ } else {
+ Slog.e(TAG, "already scheduled, stop() called before start.");
+ }
+ }
+
+ /**
+ * Stop polling to power IC.
+ */
+ public void stop() {
+ if (mPmicMonitorFuture != null) {
+ mPmicMonitorFuture.cancel(true);
+ mPmicMonitorFuture = null;
+ }
+ }
+
+ /**
+ * Shutdown power IC service and worker thread.
+ */
+ public void shutdown() {
+ mExecutor.shutdownNow();
+ }
+}
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/DeviceConfigParameterProvider.java b/services/core/java/com/android/server/display/feature/DeviceConfigParameterProvider.java
index 23ffe59..465584c 100644
--- a/services/core/java/com/android/server/display/feature/DeviceConfigParameterProvider.java
+++ b/services/core/java/com/android/server/display/feature/DeviceConfigParameterProvider.java
@@ -77,6 +77,12 @@
// Test parameters
// usage e.g.: adb shell device_config put display_manager refresh_rate_in_hbm_sunlight 90
+ // allows to customize power throttling data
+ public String getPowerThrottlingData() {
+ return mDeviceConfig.getString(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
+ DisplayManager.DeviceConfig.KEY_POWER_THROTTLING_DATA, null);
+ }
+
// allows to customize brightness throttling data
public String getBrightnessThrottlingData() {
return mDeviceConfig.getString(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
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 f7b9869..d953e8e 100644
--- a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
+++ b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
@@ -47,6 +47,10 @@
Flags.FLAG_ENABLE_ADAPTIVE_TONE_IMPROVEMENTS_1,
Flags::enableAdaptiveToneImprovements1);
+ private final FlagState mAdaptiveToneImprovements2 = new FlagState(
+ Flags.FLAG_ENABLE_ADAPTIVE_TONE_IMPROVEMENTS_2,
+ Flags::enableAdaptiveToneImprovements2);
+
private final FlagState mDisplayOffloadFlagState = new FlagState(
Flags.FLAG_ENABLE_DISPLAY_OFFLOAD,
Flags::enableDisplayOffload);
@@ -55,24 +59,43 @@
Flags.FLAG_ENABLE_MODE_LIMIT_FOR_EXTERNAL_DISPLAY,
Flags::enableModeLimitForExternalDisplay);
+ 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);
+ private final FlagState mPowerThrottlingClamperFlagState = new FlagState(
+ Flags.FLAG_ENABLE_POWER_THROTTLING_CLAMPER,
+ Flags::enablePowerThrottlingClamper);
+
+ private final FlagState mSmallAreaDetectionFlagState = new FlagState(
+ Flags.FLAG_ENABLE_SMALL_AREA_DETECTION,
+ Flags::enableSmallAreaDetection);
+
/** Returns whether connected display management is enabled or not. */
public boolean isConnectedDisplayManagementEnabled() {
return mConnectedDisplayManagementFlagState.isEnabled();
}
- /** Returns whether hdr clamper is enabled on not*/
+ /** Returns whether NBM Controller is enabled or not. */
public boolean isNbmControllerEnabled() {
return mNbmControllerFlagState.isEnabled();
}
+ /** Returns whether hdr clamper is enabled on not. */
public boolean isHdrClamperEnabled() {
return mHdrClamperFlagState.isEnabled();
}
+ /** Returns whether power throttling clamper is enabled on not. */
+ public boolean isPowerThrottlingClamperEnabled() {
+ return mPowerThrottlingClamperFlagState.isEnabled();
+ }
+
+
/**
* Returns whether adaptive tone improvements are enabled
*/
@@ -80,6 +103,13 @@
return mAdaptiveToneImprovements1.isEnabled();
}
+ /**
+ * Returns whether adaptive tone improvements are enabled
+ */
+ public boolean isAdaptiveTone2Enabled() {
+ return mAdaptiveToneImprovements2.isEnabled();
+ }
+
/** Returns whether resolution range voting feature is enabled or not. */
public boolean isDisplayResolutionRangeVotingEnabled() {
return isExternalDisplayLimitModeEnabled();
@@ -112,10 +142,19 @@
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();
}
+ public boolean isSmallAreaDetectionEnabled() {
+ return mSmallAreaDetectionFlagState.isEnabled();
+ }
+
private static class FlagState {
private final String mName;
@@ -129,7 +168,6 @@
mFlagFunction = flagFunction;
}
- // TODO(b/297159910): Simplify using READ-ONLY flags when available.
private boolean isEnabled() {
if (mEnabledSet) {
if (DEBUG) {
@@ -146,14 +184,7 @@
}
private boolean flagOrSystemProperty(Supplier<Boolean> flagFunction, String flagName) {
- boolean flagValue = false;
- try {
- flagValue = flagFunction.get();
- } catch (Throwable ex) {
- if (DEBUG) {
- Slog.i(TAG, "Flags not ready yet. Return false for " + flagName, ex);
- }
- }
+ boolean flagValue = flagFunction.get();
// TODO(b/299462337) Remove when the infrastructure is ready.
if (Build.IS_ENG || Build.IS_USERDEBUG) {
return SystemProperties.getBoolean("persist.sys." + flagName + "-override",
diff --git a/services/core/java/com/android/server/display/feature/display_flags.aconfig b/services/core/java/com/android/server/display/feature/display_flags.aconfig
index c24b3ca..9141814 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
@@ -27,6 +27,14 @@
}
flag {
+ name: "enable_power_throttling_clamper"
+ namespace: "display_manager"
+ description: "Feature flag for Power Throttling Clamper"
+ bug: "294777007"
+ is_fixed_read_only: true
+}
+
+flag {
name: "enable_adaptive_tone_improvements_1"
namespace: "display_manager"
description: "Feature flag for Adaptive Tone Improvements"
@@ -35,6 +43,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"
@@ -74,9 +90,26 @@
}
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
}
+
+flag {
+ name: "enable_small_area_detection"
+ namespace: "display_manager"
+ description: "Feature flag for SmallAreaDetection"
+ bug: "298722189"
+ is_fixed_read_only: true
+}
+
diff --git a/services/core/java/com/android/server/display/layout/Layout.java b/services/core/java/com/android/server/display/layout/Layout.java
index d9ec3de..40cb3303 100644
--- a/services/core/java/com/android/server/display/layout/Layout.java
+++ b/services/core/java/com/android/server/display/layout/Layout.java
@@ -80,7 +80,8 @@
createDisplayLocked(address, /* isDefault= */ true, /* isEnabled= */ true,
DEFAULT_DISPLAY_GROUP_NAME, idProducer, POSITION_UNKNOWN,
/* leadDisplayAddress= */ null, /* brightnessThrottlingMapId= */ null,
- /* refreshRateZoneId= */ null, /* refreshRateThermalThrottlingMapId= */ null);
+ /* refreshRateZoneId= */ null, /* refreshRateThermalThrottlingMapId= */ null,
+ /* powerThrottlingMapId= */ null);
}
/**
@@ -97,6 +98,7 @@
* @param refreshRateZoneId Layout limited refresh rate zone name.
* @param refreshRateThermalThrottlingMapId Name of which refresh rate throttling
* policy should be used.
+ * @param powerThrottlingMapId Name of which power throttling policy should be used.
*
* @exception IllegalArgumentException When a default display owns a display group other than
* DEFAULT_DISPLAY_GROUP.
@@ -106,7 +108,8 @@
String displayGroupName, DisplayIdProducer idProducer, int position,
@Nullable DisplayAddress leadDisplayAddress, String brightnessThrottlingMapId,
@Nullable String refreshRateZoneId,
- @Nullable String refreshRateThermalThrottlingMapId) {
+ @Nullable String refreshRateThermalThrottlingMapId,
+ @Nullable String powerThrottlingMapId) {
if (contains(address)) {
Slog.w(TAG, "Attempting to add second definition for display-device: " + address);
return;
@@ -139,7 +142,7 @@
final Display display = new Display(address, logicalDisplayId, isEnabled, displayGroupName,
brightnessThrottlingMapId, position, leadDisplayAddress, refreshRateZoneId,
- refreshRateThermalThrottlingMapId);
+ refreshRateThermalThrottlingMapId, powerThrottlingMapId);
mDisplays.add(display);
}
@@ -311,6 +314,9 @@
@Nullable
private final String mThermalRefreshRateThrottlingMapId;
+ @Nullable
+ private final String mPowerThrottlingMapId;
+
// The ID of the lead display that this display will follow in a layout. -1 means no lead.
// This is determined using {@code mLeadDisplayAddress}.
private int mLeadDisplayId;
@@ -318,7 +324,8 @@
private Display(@NonNull DisplayAddress address, int logicalDisplayId, boolean isEnabled,
@NonNull String displayGroupName, String brightnessThrottlingMapId, int position,
@Nullable DisplayAddress leadDisplayAddress, @Nullable String refreshRateZoneId,
- @Nullable String refreshRateThermalThrottlingMapId) {
+ @Nullable String refreshRateThermalThrottlingMapId,
+ @Nullable String powerThrottlingMapId) {
mAddress = address;
mLogicalDisplayId = logicalDisplayId;
mIsEnabled = isEnabled;
@@ -328,6 +335,7 @@
mLeadDisplayAddress = leadDisplayAddress;
mRefreshRateZoneId = refreshRateZoneId;
mThermalRefreshRateThrottlingMapId = refreshRateThermalThrottlingMapId;
+ mPowerThrottlingMapId = powerThrottlingMapId;
mLeadDisplayId = NO_LEAD_DISPLAY;
}
@@ -344,6 +352,7 @@
+ ", mLeadDisplayId: " + mLeadDisplayId
+ ", mLeadDisplayAddress: " + mLeadDisplayAddress
+ ", mThermalRefreshRateThrottlingMapId: " + mThermalRefreshRateThrottlingMapId
+ + ", mPowerThrottlingMapId: " + mPowerThrottlingMapId
+ "}";
}
@@ -366,7 +375,9 @@
&& this.mLeadDisplayId == otherDisplay.mLeadDisplayId
&& Objects.equals(mLeadDisplayAddress, otherDisplay.mLeadDisplayAddress)
&& Objects.equals(mThermalRefreshRateThrottlingMapId,
- otherDisplay.mThermalRefreshRateThrottlingMapId);
+ otherDisplay.mThermalRefreshRateThrottlingMapId)
+ && Objects.equals(mPowerThrottlingMapId,
+ otherDisplay.mPowerThrottlingMapId);
}
@Override
@@ -382,6 +393,7 @@
result = 31 * result + mLeadDisplayId;
result = 31 * result + Objects.hashCode(mLeadDisplayAddress);
result = 31 * result + Objects.hashCode(mThermalRefreshRateThrottlingMapId);
+ result = 31 * result + Objects.hashCode(mPowerThrottlingMapId);
return result;
}
@@ -441,6 +453,15 @@
return mThermalRefreshRateThrottlingMapId;
}
+ /**
+ * Gets the id of the power throttling map that should be used.
+ * @return The ID of the power throttling map that this display should use,
+ * null if unspecified, will fall back to default.
+ */
+ public String getPowerThrottlingMapId() {
+ return mPowerThrottlingMapId;
+ }
+
private void setLeadDisplayId(int id) {
mLeadDisplayId = id;
}
diff --git a/services/core/java/com/android/server/display/notifications/ConnectedDisplayUsbErrorsDetector.java b/services/core/java/com/android/server/display/notifications/ConnectedDisplayUsbErrorsDetector.java
new file mode 100644
index 0000000..f683e81
--- /dev/null
+++ b/services/core/java/com/android/server/display/notifications/ConnectedDisplayUsbErrorsDetector.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.hardware.usb.DisplayPortAltModeInfo.DISPLAYPORT_ALT_MODE_STATUS_CAPABLE_DISABLED;
+import static android.hardware.usb.DisplayPortAltModeInfo.DISPLAYPORT_ALT_MODE_STATUS_NOT_CAPABLE;
+import static android.hardware.usb.DisplayPortAltModeInfo.LINK_TRAINING_STATUS_FAILURE;
+
+import android.annotation.NonNull;
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.hardware.usb.DisplayPortAltModeInfo;
+import android.hardware.usb.UsbManager;
+import android.hardware.usb.UsbManager.DisplayPortAltModeInfoListener;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.display.feature.DisplayManagerFlags;
+
+/**
+ * Detects usb issues related to an external display connected.
+ */
+public class ConnectedDisplayUsbErrorsDetector implements DisplayPortAltModeInfoListener {
+ private static final String TAG = "ConnectedDisplayUsbErrorsDetector";
+
+ /**
+ * Dependency injection for {@link ConnectedDisplayUsbErrorsDetector}.
+ */
+ public interface Injector {
+
+ /**
+ * @return {@link UsbManager} service.
+ */
+ UsbManager getUsbManager();
+ }
+
+ /**
+ * USB errors listener
+ */
+ public interface Listener {
+
+ /**
+ * Link training failure callback.
+ */
+ void onDisplayPortLinkTrainingFailure();
+
+ /**
+ * DisplayPort capable device plugged-in, but cable is not supporting DisplayPort.
+ */
+ void onCableNotCapableDisplayPort();
+ }
+
+ private Listener mListener;
+ private final Injector mInjector;
+ private final Context mContext;
+ private final boolean mIsConnectedDisplayErrorHandlingEnabled;
+
+ ConnectedDisplayUsbErrorsDetector(@NonNull final DisplayManagerFlags flags,
+ @NonNull final Context context) {
+ this(flags, context, () -> context.getSystemService(UsbManager.class));
+ }
+
+ @VisibleForTesting
+ ConnectedDisplayUsbErrorsDetector(@NonNull final DisplayManagerFlags flags,
+ @NonNull final Context context, @NonNull final Injector injector) {
+ mContext = context;
+ mInjector = injector;
+ mIsConnectedDisplayErrorHandlingEnabled =
+ flags.isConnectedDisplayErrorHandlingEnabled();
+ }
+
+ /** Register listener for usb error events. */
+ @SuppressLint("AndroidFrameworkRequiresPermission")
+ void registerListener(final Listener listener) {
+ if (!mIsConnectedDisplayErrorHandlingEnabled) {
+ return;
+ }
+
+ final var usbManager = mInjector.getUsbManager();
+ if (usbManager == null) {
+ Slog.e(TAG, "UsbManager is null");
+ return;
+ }
+
+ mListener = listener;
+
+ try {
+ usbManager.registerDisplayPortAltModeInfoListener(mContext.getMainExecutor(), this);
+ } catch (IllegalStateException e) {
+ Slog.e(TAG, "Failed to register listener", e);
+ }
+ }
+
+ /**
+ * Callback upon changes in {@link DisplayPortAltModeInfo}.
+ * @param portId String describing the {@link android.hardware.usb.UsbPort} that was changed.
+ * @param info New {@link DisplayPortAltModeInfo} for the corresponding portId.
+ */
+ @Override
+ public void onDisplayPortAltModeInfoChanged(@NonNull String portId,
+ @NonNull DisplayPortAltModeInfo info) {
+ if (mListener == null) {
+ return;
+ }
+
+ if (DISPLAYPORT_ALT_MODE_STATUS_CAPABLE_DISABLED == info.getPartnerSinkStatus()
+ && DISPLAYPORT_ALT_MODE_STATUS_NOT_CAPABLE == info.getCableStatus()
+ ) {
+ mListener.onCableNotCapableDisplayPort();
+ return;
+ }
+
+ if (LINK_TRAINING_STATUS_FAILURE == info.getLinkTrainingStatus()) {
+ mListener.onDisplayPortLinkTrainingFailure();
+ return;
+ }
+ }
+}
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..5cdef38
--- /dev/null
+++ b/services/core/java/com/android/server/display/notifications/DisplayNotificationManager.java
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 implements ConnectedDisplayUsbErrorsDetector.Listener {
+ /** Dependency injection interface for {@link DisplayNotificationManager} */
+ public interface Injector {
+ /** Get {@link NotificationManager} service or null if not available. */
+ @Nullable
+ NotificationManager getNotificationManager();
+
+ /** Get {@link ConnectedDisplayUsbErrorsDetector} or null if not available. */
+ @Nullable
+ ConnectedDisplayUsbErrorsDetector getUsbErrorsDetector();
+ }
+
+ 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;
+ private ConnectedDisplayUsbErrorsDetector mConnectedDisplayUsbErrorsDetector;
+
+ public DisplayNotificationManager(final DisplayManagerFlags flags, final Context context) {
+ this(flags, context, new Injector() {
+ @Nullable
+ @Override
+ public NotificationManager getNotificationManager() {
+ return context.getSystemService(NotificationManager.class);
+ }
+
+ @Nullable
+ @Override
+ public ConnectedDisplayUsbErrorsDetector getUsbErrorsDetector() {
+ return new ConnectedDisplayUsbErrorsDetector(flags, context);
+ }
+ });
+ }
+
+ @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;
+ }
+
+ mConnectedDisplayUsbErrorsDetector = mInjector.getUsbErrorsDetector();
+ if (mConnectedDisplayUsbErrorsDetector != null) {
+ mConnectedDisplayUsbErrorsDetector.registerListener(this);
+ }
+ }
+
+ /**
+ * Display error notification upon DisplayPort link training failure.
+ */
+ @Override
+ public void onDisplayPortLinkTrainingFailure() {
+ if (!mConnectedDisplayErrorHandlingEnabled) {
+ Slog.d(TAG, "onDisplayPortLinkTrainingFailure:"
+ + " mConnectedDisplayErrorHandlingEnabled is false");
+ return;
+ }
+
+ sendErrorNotification(createErrorNotification(
+ R.string.connected_display_unavailable_notification_title,
+ R.string.connected_display_unavailable_notification_content));
+ }
+
+ /**
+ * Display error notification upon cable not capable of DisplayPort connected to a device
+ * capable of DisplayPort.
+ */
+ @Override
+ public void onCableNotCapableDisplayPort() {
+ if (!mConnectedDisplayErrorHandlingEnabled) {
+ Slog.d(TAG, "onCableNotCapableDisplayPort:"
+ + " mConnectedDisplayErrorHandlingEnabled is false");
+ return;
+ }
+
+ sendErrorNotification(createErrorNotification(
+ R.string.connected_display_cable_dont_support_displays_notification_title,
+ R.string.connected_display_cable_dont_support_displays_notification_content));
+ }
+
+ /**
+ * 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/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 429db5e..c28a68b 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -4951,6 +4951,11 @@
AudioDeviceAttributes attributes = new AudioDeviceAttributes(
AudioDeviceAttributes.ROLE_OUTPUT, AudioDeviceInfo.TYPE_HDMI_EARC, "", "",
new ArrayList<AudioProfile>(), audioDescriptors);
+ // Set SAM to ON whenever CEC is disabled. Failure to do so may result in the absence
+ // of sound when CEC is disabled and eARC is enabled due to SAM being in the off state.
+ if (!isCecControlEnabled()) {
+ setSystemAudioActivated(true);
+ }
getAudioManager().setWiredDeviceConnectionState(attributes, enabled ? 1 : 0);
}
diff --git a/services/core/java/com/android/server/input/KeyboardLayoutManager.java b/services/core/java/com/android/server/input/KeyboardLayoutManager.java
index bad6bf0..8580b96 100644
--- a/services/core/java/com/android/server/input/KeyboardLayoutManager.java
+++ b/services/core/java/com/android/server/input/KeyboardLayoutManager.java
@@ -1246,11 +1246,17 @@
isFirstConfiguration);
for (int i = 0; i < imeInfoList.size(); i++) {
KeyboardLayoutInfo layoutInfo = layoutInfoList.get(i);
- boolean noLayoutFound = layoutInfo == null || layoutInfo.mDescriptor == null;
- configurationEventBuilder.addLayoutSelection(imeInfoList.get(i).mImeSubtype,
- noLayoutFound ? null : getKeyboardLayout(layoutInfo.mDescriptor),
- noLayoutFound ? LAYOUT_SELECTION_CRITERIA_DEFAULT
- : layoutInfo.mSelectionCriteria);
+ String layoutName = null;
+ int layoutSelectionCriteria = LAYOUT_SELECTION_CRITERIA_DEFAULT;
+ if (layoutInfo != null && layoutInfo.mDescriptor != null) {
+ layoutSelectionCriteria = layoutInfo.mSelectionCriteria;
+ KeyboardLayoutDescriptor d = KeyboardLayoutDescriptor.parse(layoutInfo.mDescriptor);
+ if (d != null) {
+ layoutName = d.keyboardLayoutName;
+ }
+ }
+ configurationEventBuilder.addLayoutSelection(imeInfoList.get(i).mImeSubtype, layoutName,
+ layoutSelectionCriteria);
}
KeyboardMetricsCollector.logKeyboardConfiguredAtom(configurationEventBuilder.build());
}
diff --git a/services/core/java/com/android/server/input/KeyboardMetricsCollector.java b/services/core/java/com/android/server/input/KeyboardMetricsCollector.java
index 08e5977..2dd2a16 100644
--- a/services/core/java/com/android/server/input/KeyboardMetricsCollector.java
+++ b/services/core/java/com/android/server/input/KeyboardMetricsCollector.java
@@ -491,7 +491,7 @@
private final InputDevice mInputDevice;
private boolean mIsFirstConfiguration;
private final List<InputMethodSubtype> mImeSubtypeList = new ArrayList<>();
- private final List<KeyboardLayout> mSelectedLayoutList = new ArrayList<>();
+ private final List<String> mSelectedLayoutList = new ArrayList<>();
private final List<Integer> mLayoutSelectionCriteriaList = new ArrayList<>();
public Builder(@NonNull InputDevice inputDevice) {
@@ -511,7 +511,7 @@
* Adds keyboard layout configuration info for a particular IME subtype language
*/
public Builder addLayoutSelection(@NonNull InputMethodSubtype imeSubtype,
- @Nullable KeyboardLayout selectedLayout,
+ @Nullable String selectedLayout,
@LayoutSelectionCriteria int layoutSelectionCriteria) {
Objects.requireNonNull(imeSubtype, "IME subtype provided should not be null");
if (!isValidSelectionCriteria(layoutSelectionCriteria)) {
@@ -533,7 +533,6 @@
}
List<LayoutConfiguration> configurationList = new ArrayList<>();
for (int i = 0; i < size; i++) {
- KeyboardLayout selectedLayout = mSelectedLayoutList.get(i);
@LayoutSelectionCriteria int layoutSelectionCriteria =
mLayoutSelectionCriteriaList.get(i);
InputMethodSubtype imeSubtype = mImeSubtypeList.get(i);
@@ -552,9 +551,9 @@
imeSubtype.getPhysicalKeyboardHintLayoutType());
// Sanitize null values
- String keyboardLayoutName =
- selectedLayout == null ? DEFAULT_LAYOUT_NAME
- : selectedLayout.getLabel();
+ String keyboardLayoutName = mSelectedLayoutList.get(i) == null
+ ? DEFAULT_LAYOUT_NAME
+ : mSelectedLayoutList.get(i);
configurationList.add(
new LayoutConfiguration(keyboardLayoutType, keyboardLanguageTag,
diff --git a/services/core/java/com/android/server/inputmethod/ImeTrackerService.java b/services/core/java/com/android/server/inputmethod/ImeTrackerService.java
index b090334..27e1b9a 100644
--- a/services/core/java/com/android/server/inputmethod/ImeTrackerService.java
+++ b/services/core/java/com/android/server/inputmethod/ImeTrackerService.java
@@ -60,84 +60,99 @@
private static final long TIMEOUT_MS = 10_000;
/** Handler for registering timeouts for live entries. */
+ @GuardedBy("mLock")
private final Handler mHandler;
/** Singleton instance of the History. */
- @GuardedBy("ImeTrackerService.this")
+ @GuardedBy("mLock")
private final History mHistory = new History();
+ private final Object mLock = new Object();
+
ImeTrackerService(@NonNull Looper looper) {
mHandler = new Handler(looper, null /* callback */, true /* async */);
}
@NonNull
@Override
- public synchronized ImeTracker.Token onRequestShow(@NonNull String tag, int uid,
+ public ImeTracker.Token onRequestShow(@NonNull String tag, int uid,
@ImeTracker.Origin int origin, @SoftInputShowHideReason int reason) {
final var binder = new Binder();
final var token = new ImeTracker.Token(binder, tag);
final var entry = new History.Entry(tag, uid, ImeTracker.TYPE_SHOW, ImeTracker.STATUS_RUN,
origin, reason);
- mHistory.addEntry(binder, entry);
+ synchronized (mLock) {
+ mHistory.addEntry(binder, entry);
- // Register a delayed task to handle the case where the new entry times out.
- mHandler.postDelayed(() -> {
- synchronized (ImeTrackerService.this) {
- mHistory.setFinished(token, ImeTracker.STATUS_TIMEOUT, ImeTracker.PHASE_NOT_SET);
- }
- }, TIMEOUT_MS);
-
+ // Register a delayed task to handle the case where the new entry times out.
+ mHandler.postDelayed(() -> {
+ synchronized (mLock) {
+ mHistory.setFinished(token, ImeTracker.STATUS_TIMEOUT,
+ ImeTracker.PHASE_NOT_SET);
+ }
+ }, TIMEOUT_MS);
+ }
return token;
}
@NonNull
@Override
- public synchronized ImeTracker.Token onRequestHide(@NonNull String tag, int uid,
+ public ImeTracker.Token onRequestHide(@NonNull String tag, int uid,
@ImeTracker.Origin int origin, @SoftInputShowHideReason int reason) {
final var binder = new Binder();
final var token = new ImeTracker.Token(binder, tag);
final var entry = new History.Entry(tag, uid, ImeTracker.TYPE_HIDE, ImeTracker.STATUS_RUN,
origin, reason);
- mHistory.addEntry(binder, entry);
+ synchronized (mLock) {
+ mHistory.addEntry(binder, entry);
- // Register a delayed task to handle the case where the new entry times out.
- mHandler.postDelayed(() -> {
- synchronized (ImeTrackerService.this) {
- mHistory.setFinished(token, ImeTracker.STATUS_TIMEOUT, ImeTracker.PHASE_NOT_SET);
- }
- }, TIMEOUT_MS);
-
+ // Register a delayed task to handle the case where the new entry times out.
+ mHandler.postDelayed(() -> {
+ synchronized (mLock) {
+ mHistory.setFinished(token, ImeTracker.STATUS_TIMEOUT,
+ ImeTracker.PHASE_NOT_SET);
+ }
+ }, TIMEOUT_MS);
+ }
return token;
}
@Override
- public synchronized void onProgress(@NonNull IBinder binder, @ImeTracker.Phase int phase) {
- final var entry = mHistory.getEntry(binder);
- if (entry == null) return;
+ public void onProgress(@NonNull IBinder binder, @ImeTracker.Phase int phase) {
+ synchronized (mLock) {
+ final var entry = mHistory.getEntry(binder);
+ if (entry == null) return;
- entry.mPhase = phase;
+ entry.mPhase = phase;
+ }
}
@Override
- public synchronized void onFailed(@NonNull ImeTracker.Token statsToken,
- @ImeTracker.Phase int phase) {
- mHistory.setFinished(statsToken, ImeTracker.STATUS_FAIL, phase);
+ public void onFailed(@NonNull ImeTracker.Token statsToken, @ImeTracker.Phase int phase) {
+ synchronized (mLock) {
+ mHistory.setFinished(statsToken, ImeTracker.STATUS_FAIL, phase);
+ }
}
@Override
- public synchronized void onCancelled(@NonNull ImeTracker.Token statsToken,
- @ImeTracker.Phase int phase) {
- mHistory.setFinished(statsToken, ImeTracker.STATUS_CANCEL, phase);
+ public void onCancelled(@NonNull ImeTracker.Token statsToken, @ImeTracker.Phase int phase) {
+ synchronized (mLock) {
+ mHistory.setFinished(statsToken, ImeTracker.STATUS_CANCEL, phase);
+ }
}
@Override
- public synchronized void onShown(@NonNull ImeTracker.Token statsToken) {
- mHistory.setFinished(statsToken, ImeTracker.STATUS_SUCCESS, ImeTracker.PHASE_NOT_SET);
+ public void onShown(@NonNull ImeTracker.Token statsToken) {
+ synchronized (mLock) {
+ mHistory.setFinished(statsToken, ImeTracker.STATUS_SUCCESS, ImeTracker.PHASE_NOT_SET);
+ }
}
@Override
- public synchronized void onHidden(@NonNull ImeTracker.Token statsToken) {
- mHistory.setFinished(statsToken, ImeTracker.STATUS_SUCCESS, ImeTracker.PHASE_NOT_SET);
+ public void onHidden(@NonNull ImeTracker.Token statsToken) {
+ synchronized (mLock) {
+ mHistory.setFinished(statsToken, ImeTracker.STATUS_SUCCESS, ImeTracker.PHASE_NOT_SET);
+ }
}
/**
@@ -146,25 +161,30 @@
* @param statsToken the token corresponding to the current IME request.
* @param requestWindowName the name of the window that created the IME request.
*/
- public synchronized void onImmsUpdate(@NonNull ImeTracker.Token statsToken,
+ public void onImmsUpdate(@NonNull ImeTracker.Token statsToken,
@NonNull String requestWindowName) {
- final var entry = mHistory.getEntry(statsToken.getBinder());
- if (entry == null) return;
+ synchronized (mLock) {
+ final var entry = mHistory.getEntry(statsToken.getBinder());
+ if (entry == null) return;
- entry.mRequestWindowName = requestWindowName;
+ entry.mRequestWindowName = requestWindowName;
+ }
}
/** Dumps the contents of the history. */
- public synchronized void dump(@NonNull PrintWriter pw, @NonNull String prefix) {
- mHistory.dump(pw, prefix);
+ public void dump(@NonNull PrintWriter pw, @NonNull String prefix) {
+ synchronized (mLock) {
+ mHistory.dump(pw, prefix);
+ }
}
@EnforcePermission(Manifest.permission.TEST_INPUT_METHOD)
@Override
- public synchronized boolean hasPendingImeVisibilityRequests() {
+ public boolean hasPendingImeVisibilityRequests() {
super.hasPendingImeVisibilityRequests_enforcePermission();
-
- return !mHistory.mLiveEntries.isEmpty();
+ synchronized (mLock) {
+ return !mHistory.mLiveEntries.isEmpty();
+ }
}
/**
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/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/AudioPoliciesBluetoothRouteController.java b/services/core/java/com/android/server/media/AudioPoliciesBluetoothRouteController.java
index eb997ba..8bc69c2 100644
--- a/services/core/java/com/android/server/media/AudioPoliciesBluetoothRouteController.java
+++ b/services/core/java/com/android/server/media/AudioPoliciesBluetoothRouteController.java
@@ -345,7 +345,7 @@
}
private void notifyBluetoothRoutesUpdated() {
- mListener.onBluetoothRoutesUpdated(getAllBluetoothRoutes());
+ mListener.onBluetoothRoutesUpdated();
}
private BluetoothRouteInfo createBluetoothRoute(BluetoothDevice device) {
diff --git a/services/core/java/com/android/server/media/AudioPoliciesDeviceRouteController.java b/services/core/java/com/android/server/media/AudioPoliciesDeviceRouteController.java
index 93f6ff3..33190ad 100644
--- a/services/core/java/com/android/server/media/AudioPoliciesDeviceRouteController.java
+++ b/services/core/java/com/android/server/media/AudioPoliciesDeviceRouteController.java
@@ -231,7 +231,7 @@
}
if (isDeviceRouteChanged) {
- mOnDeviceRouteChangedListener.onDeviceRouteChanged(deviceRoute);
+ mOnDeviceRouteChangedListener.onDeviceRouteChanged();
}
}
}
diff --git a/services/core/java/com/android/server/media/BluetoothRouteController.java b/services/core/java/com/android/server/media/BluetoothRouteController.java
index ddeeacc..2b01001 100644
--- a/services/core/java/com/android/server/media/BluetoothRouteController.java
+++ b/services/core/java/com/android/server/media/BluetoothRouteController.java
@@ -136,12 +136,8 @@
*/
interface BluetoothRoutesUpdatedListener {
- /**
- * Called when Bluetooth routes have changed.
- *
- * @param routes updated Bluetooth routes list.
- */
- void onBluetoothRoutesUpdated(@NonNull List<MediaRoute2Info> routes);
+ /** Called when Bluetooth routes have changed. */
+ void onBluetoothRoutesUpdated();
}
/**
diff --git a/services/core/java/com/android/server/media/DeviceRouteController.java b/services/core/java/com/android/server/media/DeviceRouteController.java
index e17f4a3..7876095 100644
--- a/services/core/java/com/android/server/media/DeviceRouteController.java
+++ b/services/core/java/com/android/server/media/DeviceRouteController.java
@@ -93,12 +93,8 @@
*/
interface OnDeviceRouteChangedListener {
- /**
- * Called when device route has changed.
- *
- * @param deviceRoute non-null device route.
- */
- void onDeviceRouteChanged(@NonNull MediaRoute2Info deviceRoute);
+ /** Called when device route has changed. */
+ void onDeviceRouteChanged();
}
}
diff --git a/services/core/java/com/android/server/media/LegacyBluetoothRouteController.java b/services/core/java/com/android/server/media/LegacyBluetoothRouteController.java
index 1980403..ba3cecf 100644
--- a/services/core/java/com/android/server/media/LegacyBluetoothRouteController.java
+++ b/services/core/java/com/android/server/media/LegacyBluetoothRouteController.java
@@ -280,7 +280,7 @@
private void notifyBluetoothRoutesUpdated() {
if (mListener != null) {
- mListener.onBluetoothRoutesUpdated(getAllBluetoothRoutes());
+ mListener.onBluetoothRoutesUpdated();
}
}
diff --git a/services/core/java/com/android/server/media/LegacyDeviceRouteController.java b/services/core/java/com/android/server/media/LegacyDeviceRouteController.java
index 971d11f..6ba40ae 100644
--- a/services/core/java/com/android/server/media/LegacyDeviceRouteController.java
+++ b/services/core/java/com/android/server/media/LegacyDeviceRouteController.java
@@ -165,8 +165,8 @@
}
}
- private void notifyDeviceRouteUpdate(@NonNull MediaRoute2Info deviceRoute) {
- mOnDeviceRouteChangedListener.onDeviceRouteChanged(deviceRoute);
+ private void notifyDeviceRouteUpdate() {
+ mOnDeviceRouteChangedListener.onDeviceRouteChanged();
}
private class AudioRoutesObserver extends IAudioRoutesObserver.Stub {
@@ -177,7 +177,7 @@
synchronized (LegacyDeviceRouteController.this) {
mDeviceRoute = deviceRoute;
}
- notifyDeviceRouteUpdate(deviceRoute);
+ notifyDeviceRouteUpdate();
}
}
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/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index 803ab28..0e8f907 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -19,6 +19,7 @@
import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
import static android.os.UserHandle.ALL;
import static android.os.UserHandle.CURRENT;
+
import static com.android.server.media.MediaKeyDispatcher.KEY_EVENT_LONG_PRESS;
import static com.android.server.media.MediaKeyDispatcher.isDoubleTapOverridden;
import static com.android.server.media.MediaKeyDispatcher.isLongPressOverridden;
@@ -1904,6 +1905,15 @@
keyEvent, AudioManager.USE_DEFAULT_STREAM_TYPE, false);
return;
}
+ if (Flags.fallbackToDefaultHandlingWhenMediaSessionHasFixedVolumeHandling()
+ && !record.canHandleVolumeKey()) {
+ Log.d(TAG, "Session with packageName=" + record.getPackageName()
+ + " doesn't support volume adjustment."
+ + " Fallbacks to the default handling.");
+ dispatchVolumeKeyEventLocked(packageName, opPackageName, pid, uid, true,
+ keyEvent, AudioManager.USE_DEFAULT_STREAM_TYPE, false);
+ return;
+ }
switch (keyEvent.getAction()) {
case KeyEvent.ACTION_DOWN: {
int direction = 0;
diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
index 6c9aa4b..67a1ccd 100644
--- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
@@ -109,21 +109,28 @@
mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
- mBluetoothRouteController = BluetoothRouteController.createInstance(context, (routes) -> {
- publishProviderState();
- if (updateSessionInfosIfNeeded()) {
- notifySessionInfoUpdated();
- }
- });
+ mBluetoothRouteController =
+ BluetoothRouteController.createInstance(
+ context,
+ () -> {
+ publishProviderState();
+ if (updateSessionInfosIfNeeded()) {
+ notifySessionInfoUpdated();
+ }
+ });
- mDeviceRouteController = DeviceRouteController.createInstance(context, (deviceRoute) -> {
- mHandler.post(() -> {
- publishProviderState();
- if (updateSessionInfosIfNeeded()) {
- notifySessionInfoUpdated();
- }
- });
- });
+ mDeviceRouteController =
+ DeviceRouteController.createInstance(
+ context,
+ () -> {
+ mHandler.post(
+ () -> {
+ publishProviderState();
+ if (updateSessionInfosIfNeeded()) {
+ notifySessionInfoUpdated();
+ }
+ });
+ });
mAudioManager.addOnDevicesForAttributesChangedListener(
AudioAttributesUtils.ATTRIBUTES_MEDIA, mContext.getMainExecutor(),
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/media/projection/MediaProjectionSessionIdGenerator.java b/services/core/java/com/android/server/media/projection/MediaProjectionSessionIdGenerator.java
new file mode 100644
index 0000000..ff70cb3
--- /dev/null
+++ b/services/core/java/com/android/server/media/projection/MediaProjectionSessionIdGenerator.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 android.content.Context;
+import android.content.SharedPreferences;
+import android.os.Environment;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.io.File;
+
+public class MediaProjectionSessionIdGenerator {
+
+ private static final String PREFERENCES_FILE_NAME = "media_projection_session_id";
+ private static final String SESSION_ID_PREF_KEY = "media_projection_session_id_key";
+ private static final int SESSION_ID_DEFAULT_VALUE = 0;
+
+ private static final Object sInstanceLock = new Object();
+
+ @GuardedBy("sInstanceLock")
+ private static MediaProjectionSessionIdGenerator sInstance;
+
+ private final Object mSessionIdLock = new Object();
+
+ @GuardedBy("mSessionIdLock")
+ private final SharedPreferences mSharedPreferences;
+
+ /** Creates or returns an existing instance of {@link MediaProjectionSessionIdGenerator}. */
+ public static MediaProjectionSessionIdGenerator getInstance(Context context) {
+ synchronized (sInstanceLock) {
+ if (sInstance == null) {
+ File preferencesFile =
+ new File(Environment.getDataSystemDirectory(), PREFERENCES_FILE_NAME);
+ SharedPreferences preferences =
+ context.getSharedPreferences(preferencesFile, Context.MODE_PRIVATE);
+ sInstance = new MediaProjectionSessionIdGenerator(preferences);
+ }
+ return sInstance;
+ }
+ }
+
+ @VisibleForTesting
+ public MediaProjectionSessionIdGenerator(SharedPreferences sharedPreferences) {
+ this.mSharedPreferences = sharedPreferences;
+ }
+
+ /** Returns the current session ID. This value is persisted across reboots. */
+ public int getCurrentSessionId() {
+ synchronized (mSessionIdLock) {
+ return getCurrentSessionIdInternal();
+ }
+ }
+
+ /**
+ * Creates and returns a new session ID. This value will be persisted as the new current session
+ * ID, and will be persisted across reboots.
+ */
+ public int createAndGetNewSessionId() {
+ synchronized (mSessionIdLock) {
+ int newSessionId = getCurrentSessionId() + 1;
+ setSessionIdInternal(newSessionId);
+ return newSessionId;
+ }
+ }
+
+ @GuardedBy("mSessionIdLock")
+ private void setSessionIdInternal(int value) {
+ mSharedPreferences.edit().putInt(SESSION_ID_PREF_KEY, value).apply();
+ }
+
+ @GuardedBy("mSessionIdLock")
+ private int getCurrentSessionIdInternal() {
+ return mSharedPreferences.getInt(SESSION_ID_PREF_KEY, SESSION_ID_DEFAULT_VALUE);
+ }
+}
diff --git a/services/core/java/com/android/server/notification/NotificationAttentionHelper.java b/services/core/java/com/android/server/notification/NotificationAttentionHelper.java
index 75a0cf5..6b7db2d 100644
--- a/services/core/java/com/android/server/notification/NotificationAttentionHelper.java
+++ b/services/core/java/com/android/server/notification/NotificationAttentionHelper.java
@@ -38,6 +38,7 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
import android.content.res.Resources;
import android.database.ContentObserver;
import android.media.AudioAttributes;
@@ -48,6 +49,7 @@
import android.os.RemoteException;
import android.os.SystemProperties;
import android.os.UserHandle;
+import android.os.UserManager;
import android.os.VibrationEffect;
import android.provider.Settings;
import android.telephony.PhoneStateListener;
@@ -61,18 +63,21 @@
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags;
+import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.server.EventLogTags;
import com.android.server.lights.LightsManager;
import com.android.server.lights.LogicalLight;
+import com.android.server.notification.Flags;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -87,15 +92,24 @@
static final boolean DEBUG_INTERRUPTIVENESS = SystemProperties.getBoolean(
"debug.notification.interruptiveness", false);
+ private static final float DEFAULT_VOLUME = 1.0f;
+ // TODO (b/291899544): remove for release
+ private static final String POLITE_STRATEGY1 = "rule1";
+ private static final String POLITE_STRATEGY2 = "rule2";
+ private static final int DEFAULT_NOTIFICATION_COOLDOWN_ENABLED = 1;
+ private static final int DEFAULT_NOTIFICATION_COOLDOWN_ENABLED_FOR_WORK = 0;
+ private static final int DEFAULT_NOTIFICATION_COOLDOWN_ALL = 1;
+ private static final int DEFAULT_NOTIFICATION_COOLDOWN_VIBRATE_UNLOCKED = 0;
+
private final Context mContext;
private final PackageManager mPackageManager;
private final TelephonyManager mTelephonyManager;
+ private final UserManager mUm;
private final NotificationManagerPrivate mNMP;
private final SystemUiSystemPropertiesFlags.FlagResolver mFlagResolver;
private AccessibilityManager mAccessibilityManager;
private KeyguardManager mKeyguardManager;
private AudioManager mAudioManager;
- private final LightsManager mLightsManager;
private final NotificationUsageStats mUsageStats;
private final ZenModeHelper mZenModeHelper;
@@ -126,17 +140,26 @@
private final float mInCallNotificationVolume;
private Binder mCallNotificationToken = null;
+ // Settings flags
+ private boolean mNotificationCooldownEnabled;
+ private boolean mNotificationCooldownForWorkEnabled;
+ private boolean mNotificationCooldownApplyToAll;
+ private boolean mNotificationCooldownVibrateUnlocked;
+
+ private boolean mEnablePoliteNotificationsFeature;
+ private final PolitenessStrategy mStrategy;
+ private int mCurrentWorkProfileId = UserHandle.USER_NULL;
public NotificationAttentionHelper(Context context, LightsManager lightsManager,
AccessibilityManager accessibilityManager, PackageManager packageManager,
- NotificationUsageStats usageStats,
+ UserManager userManager, NotificationUsageStats usageStats,
NotificationManagerPrivate notificationManagerPrivate,
ZenModeHelper zenModeHelper, SystemUiSystemPropertiesFlags.FlagResolver flagResolver) {
mContext = context;
mPackageManager = packageManager;
mTelephonyManager = context.getSystemService(TelephonyManager.class);
mAccessibilityManager = accessibilityManager;
- mLightsManager = lightsManager;
+ mUm = userManager;
mNMP = notificationManagerPrivate;
mUsageStats = usageStats;
mZenModeHelper = zenModeHelper;
@@ -144,8 +167,8 @@
mVibratorHelper = new VibratorHelper(context);
- mNotificationLight = mLightsManager.getLight(LightsManager.LIGHT_ID_NOTIFICATIONS);
- mAttentionLight = mLightsManager.getLight(LightsManager.LIGHT_ID_ATTENTION);
+ mNotificationLight = lightsManager.getLight(LightsManager.LIGHT_ID_NOTIFICATIONS);
+ mAttentionLight = lightsManager.getLight(LightsManager.LIGHT_ID_ATTENTION);
Resources resources = context.getResources();
mUseAttentionLight = resources.getBoolean(R.bool.config_useAttentionLight);
@@ -169,7 +192,39 @@
.build();
mInCallNotificationVolume = resources.getFloat(R.dimen.config_inCallNotificationVolume);
+ mEnablePoliteNotificationsFeature = Flags.politeNotifications();
+
+ if (mEnablePoliteNotificationsFeature) {
+ mStrategy = getPolitenessStrategy();
+ } else {
+ mStrategy = null;
+ }
+
mSettingsObserver = new SettingsObserver();
+ loadUserSettings();
+ }
+
+ private PolitenessStrategy getPolitenessStrategy() {
+ final String politenessStrategy = mFlagResolver.getStringValue(
+ NotificationFlags.NOTIF_COOLDOWN_RULE);
+
+ if (POLITE_STRATEGY2.equals(politenessStrategy)) {
+ return new Strategy2(mFlagResolver.getIntValue(NotificationFlags.NOTIF_COOLDOWN_T1),
+ mFlagResolver.getIntValue(NotificationFlags.NOTIF_COOLDOWN_T2),
+ mFlagResolver.getIntValue(NotificationFlags.NOTIF_VOLUME1),
+ mFlagResolver.getIntValue(NotificationFlags.NOTIF_VOLUME2));
+ } else {
+ if (!POLITE_STRATEGY1.equals(politenessStrategy)) {
+ Log.w(TAG, "Invalid cooldown strategy: " + politenessStrategy + ". Defaulting to "
+ + POLITE_STRATEGY1);
+ }
+
+ return new Strategy1(mFlagResolver.getIntValue(NotificationFlags.NOTIF_COOLDOWN_T1),
+ mFlagResolver.getIntValue(NotificationFlags.NOTIF_COOLDOWN_T2),
+ mFlagResolver.getIntValue(NotificationFlags.NOTIF_VOLUME1),
+ mFlagResolver.getIntValue(NotificationFlags.NOTIF_VOLUME2),
+ mFlagResolver.getIntValue(NotificationFlags.NOTIF_COOLDOWN_COUNTER_RESET));
+ }
}
public void onSystemReady() {
@@ -202,11 +257,59 @@
filter.addAction(Intent.ACTION_SCREEN_OFF);
filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
filter.addAction(Intent.ACTION_USER_PRESENT);
+ filter.addAction(Intent.ACTION_USER_ADDED);
+ filter.addAction(Intent.ACTION_USER_REMOVED);
+ filter.addAction(Intent.ACTION_USER_SWITCHED);
+ filter.addAction(Intent.ACTION_USER_UNLOCKED);
mContext.registerReceiverAsUser(mIntentReceiver, UserHandle.ALL, filter, null, null);
mContext.getContentResolver().registerContentObserver(
SettingsObserver.NOTIFICATION_LIGHT_PULSE_URI, false, mSettingsObserver,
UserHandle.USER_ALL);
+ if (mEnablePoliteNotificationsFeature) {
+ mContext.getContentResolver().registerContentObserver(
+ SettingsObserver.NOTIFICATION_COOLDOWN_ENABLED_URI, false, mSettingsObserver,
+ UserHandle.USER_ALL);
+ mContext.getContentResolver().registerContentObserver(
+ SettingsObserver.NOTIFICATION_COOLDOWN_ALL_URI, false, mSettingsObserver,
+ UserHandle.USER_ALL);
+ mContext.getContentResolver().registerContentObserver(
+ SettingsObserver.NOTIFICATION_COOLDOWN_VIBRATE_UNLOCKED_URI, false,
+ mSettingsObserver, UserHandle.USER_ALL);
+ }
+ }
+
+ private void loadUserSettings() {
+ if (mEnablePoliteNotificationsFeature) {
+ try {
+ mCurrentWorkProfileId = getManagedProfileId(ActivityManager.getCurrentUser());
+
+ mNotificationCooldownEnabled =
+ Settings.System.getIntForUser(mContext.getContentResolver(),
+ Settings.System.NOTIFICATION_COOLDOWN_ENABLED,
+ DEFAULT_NOTIFICATION_COOLDOWN_ENABLED, UserHandle.USER_CURRENT) != 0;
+ if (mCurrentWorkProfileId != UserHandle.USER_NULL) {
+ mNotificationCooldownForWorkEnabled = Settings.System.getIntForUser(
+ mContext.getContentResolver(),
+ Settings.System.NOTIFICATION_COOLDOWN_ENABLED,
+ DEFAULT_NOTIFICATION_COOLDOWN_ENABLED_FOR_WORK, mCurrentWorkProfileId)
+ != 0;
+ } else {
+ mNotificationCooldownForWorkEnabled = false;
+ }
+ mNotificationCooldownApplyToAll = Settings.System.getIntForUser(
+ mContext.getContentResolver(),
+ Settings.System.NOTIFICATION_COOLDOWN_ALL, DEFAULT_NOTIFICATION_COOLDOWN_ALL,
+ UserHandle.USER_CURRENT) != 0;
+ mNotificationCooldownVibrateUnlocked = Settings.System.getIntForUser(
+ mContext.getContentResolver(),
+ Settings.System.NOTIFICATION_COOLDOWN_VIBRATE_UNLOCKED,
+ DEFAULT_NOTIFICATION_COOLDOWN_VIBRATE_UNLOCKED,
+ UserHandle.USER_CURRENT) != 0;
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to read Settings: " + e);
+ }
+ }
}
@VisibleForTesting
@@ -229,6 +332,10 @@
Log.d(TAG, "buzzBeepBlinkLocked " + record);
}
+ if (isPoliteNotificationFeatureEnabled(record)) {
+ mStrategy.onNotificationPosted(record);
+ }
+
// Should this notification make noise, vibe, or use the LED?
final boolean aboveThreshold =
mIsAutomotive
@@ -269,6 +376,9 @@
vibration = mVibratorHelper.createFallbackVibration(insistent);
}
hasValidVibrate = vibration != null;
+ // Vibration-only if unlocked and Settings flag set
+ boolean vibrateOnly =
+ hasValidVibrate && mNotificationCooldownVibrateUnlocked && mUserPresent;
boolean hasAudibleAlert = hasValidSound || hasValidVibrate;
if (hasAudibleAlert && !shouldMuteNotificationLocked(record, signals)) {
if (!sentAccessibilityEvent) {
@@ -277,7 +387,7 @@
}
if (DEBUG) Slog.v(TAG, "Interrupting!");
boolean isInsistentUpdate = isInsistentUpdate(record);
- if (hasValidSound) {
+ if (hasValidSound && !vibrateOnly) {
if (isInsistentUpdate) {
// don't reset insistent sound, it's jarring
beep = true;
@@ -301,7 +411,7 @@
if (isInsistentUpdate) {
buzz = true;
} else {
- buzz = playVibration(record, vibration, hasValidSound);
+ buzz = playVibration(record, vibration, hasValidSound && !vibrateOnly);
if (buzz) {
mVibrateNotificationKey = key;
}
@@ -341,9 +451,7 @@
} else if (wasShowLights) {
updateLightsLocked();
}
- final int buzzBeepBlink =
- (buzz ? 1 : 0) | (beep ? 2 : 0) | (blink ? 4 : 0);
- if (buzzBeepBlink > 0) {
+ if (buzz || beep || blink) {
// Ignore summary updates because we don't display most of the information.
if (record.getSbn().isGroup() && record.getSbn().getNotification().isGroupSummary()) {
if (DEBUG_INTERRUPTIVENESS) {
@@ -362,15 +470,43 @@
+ record.getKey() + " is interruptive: alerted");
}
}
+ }
+ final int buzzBeepBlinkLoggingCode =
+ (buzz ? 1 : 0) | (beep ? 2 : 0) | (blink ? 4 : 0) | getPoliteBit(record);
+ if (buzzBeepBlinkLoggingCode > 0) {
MetricsLogger.action(record.getLogMaker()
.setCategory(MetricsEvent.NOTIFICATION_ALERT)
.setType(MetricsEvent.TYPE_OPEN)
- .setSubtype(buzzBeepBlink));
- EventLogTags.writeNotificationAlert(key, buzz ? 1 : 0, beep ? 1 : 0, blink ? 1 : 0);
+ .setSubtype(buzzBeepBlinkLoggingCode));
+ EventLogTags.writeNotificationAlert(key, buzz ? 1 : 0, beep ? 1 : 0, blink ? 1 : 0,
+ getPolitenessState(record));
}
record.setAudiblyAlerted(buzz || beep);
+ if (mEnablePoliteNotificationsFeature) {
+ // Update last alert time
+ if (buzz || beep) {
+ record.getChannel().setLastNotificationUpdateTimeMs(System.currentTimeMillis());
+ }
+ }
+ return buzzBeepBlinkLoggingCode;
+ }
- return buzzBeepBlink;
+ private int getPoliteBit(final NotificationRecord record) {
+ switch (getPolitenessState(record)) {
+ case PolitenessStrategy.POLITE_STATE_POLITE:
+ return MetricsProto.MetricsEvent.ALERT_POLITE;
+ case PolitenessStrategy.POLITE_STATE_MUTED:
+ return MetricsProto.MetricsEvent.ALERT_MUTED;
+ default:
+ return 0;
+ }
+ }
+
+ private int getPolitenessState(final NotificationRecord record) {
+ if (!isPoliteNotificationFeatureEnabled(record)) {
+ return PolitenessStrategy.POLITE_STATE_DEFAULT;
+ }
+ return mStrategy.getPolitenessState(record);
}
boolean isInsistentUpdate(final NotificationRecord record) {
@@ -468,7 +604,7 @@
+ record.getAudioAttributes());
}
player.playAsync(soundUri, record.getSbn().getUser(), looping,
- record.getAudioAttributes());
+ record.getAudioAttributes(), getSoundVolume(record));
return true;
}
} catch (RemoteException e) {
@@ -480,12 +616,56 @@
return false;
}
+ private boolean isPoliteNotificationFeatureEnabled(final NotificationRecord record) {
+ // Check feature flag
+ if (!mEnablePoliteNotificationsFeature) {
+ return false;
+ }
+
+ // The user can enable/disable notifications cooldown from the Settings app
+ if (!mNotificationCooldownEnabled) {
+ return false;
+ }
+
+ // The user can enable/disable notifications cooldown for work profile from the Settings app
+ if (isNotificationForWorkProfile(record) && !mNotificationCooldownForWorkEnabled) {
+ return false;
+ }
+
+ // The user can choose to apply cooldown for all apps/conversations only from the
+ // Settings app
+ if (!mNotificationCooldownApplyToAll && record.getChannel().getConversationId() == null) {
+ return false;
+ }
+
+ return true;
+ }
+
+ private float getSoundVolume(final NotificationRecord record) {
+ if (!isPoliteNotificationFeatureEnabled(record)) {
+ return DEFAULT_VOLUME;
+ }
+
+ return mStrategy.getSoundVolume(record);
+ }
+
+ private float getVibrationIntensity(final NotificationRecord record) {
+ if (!isPoliteNotificationFeatureEnabled(record)) {
+ return DEFAULT_VOLUME;
+ }
+
+ return mStrategy.getVibrationIntensity(record);
+ }
+
private boolean playVibration(final NotificationRecord record, final VibrationEffect effect,
boolean delayVibForSound) {
// Escalate privileges so we can use the vibrator even if the
// notifying app does not have the VIBRATE permission.
final long identity = Binder.clearCallingIdentity();
try {
+ final float scale = getVibrationIntensity(record);
+ final VibrationEffect scaledEffect = Float.compare(scale, DEFAULT_VOLUME) != 0
+ ? mVibratorHelper.scale(effect, scale) : effect;
if (delayVibForSound) {
new Thread(() -> {
// delay the vibration by the same amount as the notification sound
@@ -503,7 +683,7 @@
// so need to check that the notification is still valid for vibrate.
if (mNMP.getNotificationByKey(record.getKey()) != null) {
if (record.getKey().equals(mVibrateNotificationKey)) {
- vibrate(record, effect, true);
+ vibrate(record, scaledEffect, true);
} else {
if (DEBUG) {
Slog.v(TAG, "No vibration for notification "
@@ -517,7 +697,7 @@
}
}).start();
} else {
- vibrate(record, effect, false);
+ vibrate(record, scaledEffect, false);
}
return true;
} finally {
@@ -535,7 +715,7 @@
}
void playInCallNotification() {
- // TODO: Should we apply politeness to mInCallNotificationVolume ?
+ // TODO b/270456865: Should we apply politeness to mInCallNotificationVolume ?
final ContentResolver cr = mContext.getContentResolver();
if (mAudioManager.getRingerModeInternal() == AudioManager.RINGER_MODE_NORMAL
&& Settings.Secure.getIntForUser(cr,
@@ -760,6 +940,22 @@
|| signals.isCurrentProfile);
}
+ private boolean isNotificationForWorkProfile(final NotificationRecord record) {
+ return (record.getUser().getIdentifier() == mCurrentWorkProfileId
+ && mCurrentWorkProfileId != UserHandle.USER_NULL);
+ }
+
+ private int getManagedProfileId(int parentUserId) {
+ final List<UserInfo> profiles = mUm.getProfiles(parentUserId);
+ for (UserInfo profile : profiles) {
+ if (profile.isManagedProfile()
+ && profile.getUserHandle().getIdentifier() != parentUserId) {
+ return profile.getUserHandle().getIdentifier();
+ }
+ }
+ return UserHandle.USER_NULL;
+ }
+
void sendAccessibilityEvent(NotificationRecord record) {
if (!mAccessibilityManager.isEnabled()) {
return;
@@ -791,6 +987,16 @@
mAccessibilityManager.sendAccessibilityEvent(event);
}
+ /**
+ * Notify the attention helper of a user interaction with a notification
+ * @param record that was interacted with
+ */
+ public void onUserInteraction(final NotificationRecord record) {
+ if (isPoliteNotificationFeatureEnabled(record)) {
+ mStrategy.onUserInteraction(record);
+ }
+ }
+
public void dump(PrintWriter pw, String prefix, NotificationManagerService.DumpFilter filter) {
pw.println("\n Notification attention state:");
pw.print(prefix);
@@ -834,6 +1040,243 @@
}
}
+ abstract private static class PolitenessStrategy {
+ static final int POLITE_STATE_DEFAULT = 0;
+ static final int POLITE_STATE_POLITE = 1;
+ static final int POLITE_STATE_MUTED = 2;
+
+ @IntDef(prefix = { "POLITE_STATE_" }, value = {
+ POLITE_STATE_DEFAULT,
+ POLITE_STATE_POLITE,
+ POLITE_STATE_MUTED,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface PolitenessState {}
+
+ protected final Map<String, Integer> mVolumeStates;
+
+ // Cooldown timer for transitioning into polite state
+ protected final int mTimeoutPolite;
+ // Cooldown timer for transitioning into muted state
+ protected final int mTimeoutMuted;
+ // Volume for polite state
+ protected final float mVolumePolite;
+ // Volume for muted state
+ protected final float mVolumeMuted;
+
+ public PolitenessStrategy(int timeoutPolite, int timeoutMuted, int volumePolite,
+ int volumeMuted) {
+ mVolumeStates = new HashMap<>();
+
+ this.mTimeoutPolite = timeoutPolite;
+ this.mTimeoutMuted = timeoutMuted;
+ this.mVolumePolite = volumePolite / 100.0f;
+ this.mVolumeMuted = volumeMuted / 100.0f;
+ }
+
+ abstract void onNotificationPosted(NotificationRecord record);
+
+ String getChannelKey(final NotificationRecord record) {
+ // use conversationId if it's a conversation
+ String channelId = record.getChannel().getConversationId() != null
+ ? record.getChannel().getConversationId() : record.getChannel().getId();
+ return record.getSbn().getNormalizedUserId() + ":" + record.getSbn().getPackageName()
+ + ":" + channelId;
+ }
+
+ public float getSoundVolume(final NotificationRecord record) {
+ float volume = DEFAULT_VOLUME;
+ final String key = getChannelKey(record);
+ final @PolitenessState int volState = getPolitenessState(record);
+
+ switch (volState) {
+ case POLITE_STATE_DEFAULT:
+ volume = DEFAULT_VOLUME;
+ break;
+ case POLITE_STATE_POLITE:
+ volume = mVolumePolite;
+ break;
+ case POLITE_STATE_MUTED:
+ volume = mVolumeMuted;
+ break;
+ default:
+ Log.w(TAG, "getSoundVolume unexpected volume state: " + volState);
+ break;
+ }
+
+ if (DEBUG) {
+ Log.i(TAG,
+ "getSoundVolume state: " + volState + " vol: " + volume + " key: " + key);
+ }
+
+ return volume;
+ }
+
+ private float getVibrationIntensity(final NotificationRecord record) {
+ // TODO b/270456865: maybe use different scaling for vibration/sound ?
+ return getSoundVolume(record);
+ }
+
+ public void onUserInteraction(final NotificationRecord record) {
+ final String key = getChannelKey(record);
+ // reset to default state after user interaction
+ mVolumeStates.put(key, POLITE_STATE_DEFAULT);
+ record.getChannel().setLastNotificationUpdateTimeMs(0);
+ }
+
+ public final @PolitenessState int getPolitenessState(final NotificationRecord record) {
+ return mVolumeStates.getOrDefault(getChannelKey(record), POLITE_STATE_DEFAULT);
+ }
+ }
+
+ // TODO b/270456865: Only one of the two strategies will be released.
+ // The other one need to be removed
+ /**
+ * Polite notification strategy 1:
+ * - Transitions from default (loud) => polite (lower volume) state if a notification
+ * alerts the same channel before timeoutPolite.
+ * - Transitions from polite => muted state if a notification alerts the same channel
+ * before timeoutMuted OR transitions back to the default state if a notification alerts
+ * after timeoutPolite.
+ * - Transitions from muted => default state if the muted channel received more than maxPosted
+ * notifications OR transitions back to the polite state if a notification alerts
+ * after timeoutMuted.
+ * - Transitions back to the default state after a user interaction with a notification.
+ */
+ public static class Strategy1 extends PolitenessStrategy {
+ // Keep track of the number of notifications posted per channel
+ private final Map<String, Integer> mNumPosted;
+ // Reset to default state if number of posted notifications exceed this value when muted
+ private final int mMaxPostedForReset;
+
+ public Strategy1(int timeoutPolite, int timeoutMuted, int volumePolite, int volumeMuted,
+ int maxPosted) {
+ super(timeoutPolite, timeoutMuted, volumePolite, volumeMuted);
+
+ mNumPosted = new HashMap<>();
+ mMaxPostedForReset = maxPosted;
+
+ if (DEBUG) {
+ Log.i(TAG, "Strategy1: " + timeoutPolite + " " + timeoutMuted);
+ }
+ }
+
+ @Override
+ public void onNotificationPosted(final NotificationRecord record) {
+ long timeSinceLastNotif = System.currentTimeMillis()
+ - record.getChannel().getLastNotificationUpdateTimeMs();
+
+ final String key = getChannelKey(record);
+ @PolitenessState int volState = getPolitenessState(record);
+
+ int numPosted = mNumPosted.getOrDefault(key, 0) + 1;
+ mNumPosted.put(key, numPosted);
+
+ switch (volState) {
+ case POLITE_STATE_DEFAULT:
+ if (timeSinceLastNotif < mTimeoutPolite) {
+ volState = POLITE_STATE_POLITE;
+ }
+ break;
+ case POLITE_STATE_POLITE:
+ if (timeSinceLastNotif < mTimeoutMuted) {
+ volState = POLITE_STATE_MUTED;
+ } else if (timeSinceLastNotif > mTimeoutPolite) {
+ volState = POLITE_STATE_DEFAULT;
+ } else {
+ volState = POLITE_STATE_POLITE;
+ }
+ break;
+ case POLITE_STATE_MUTED:
+ if (timeSinceLastNotif > mTimeoutMuted) {
+ volState = POLITE_STATE_POLITE;
+ } else {
+ volState = POLITE_STATE_MUTED;
+ }
+ if (numPosted >= mMaxPostedForReset) {
+ volState = POLITE_STATE_DEFAULT;
+ mNumPosted.put(key, 0);
+ }
+ break;
+ default:
+ Log.w(TAG, "onNotificationPosted unexpected volume state: " + volState);
+ break;
+ }
+
+ if (DEBUG) {
+ Log.i(TAG, "onNotificationPosted time delta: " + timeSinceLastNotif + " vol state: "
+ + volState + " key: " + key + " numposted " + numPosted);
+ }
+
+ mVolumeStates.put(key, volState);
+ }
+
+ @Override
+ public void onUserInteraction(final NotificationRecord record) {
+ super.onUserInteraction(record);
+ mNumPosted.put(getChannelKey(record), 0);
+ }
+ }
+
+ /**
+ * Polite notification strategy 2:
+ * - Transitions from default (loud) => muted state if a notification
+ * alerts the same channel before timeoutPolite.
+ * - Transitions from polite => default state if a notification
+ * alerts the same channel before timeoutMuted.
+ * - Transitions from muted => default state if a notification alerts after timeoutMuted,
+ * otherwise transitions to the polite state.
+ * - Transitions back to the default state after a user interaction with a notification.
+ */
+ public static class Strategy2 extends PolitenessStrategy {
+ public Strategy2(int timeoutPolite, int timeoutMuted, int volumePolite, int volumeMuted) {
+ super(timeoutPolite, timeoutMuted, volumePolite, volumeMuted);
+
+ if (DEBUG) {
+ Log.i(TAG, "Strategy2: " + timeoutPolite + " " + timeoutMuted);
+ }
+ }
+
+ @Override
+ public void onNotificationPosted(final NotificationRecord record) {
+ long timeSinceLastNotif = System.currentTimeMillis()
+ - record.getChannel().getLastNotificationUpdateTimeMs();
+
+ final String key = getChannelKey(record);
+ @PolitenessState int volState = getPolitenessState(record);
+
+ switch (volState) {
+ case POLITE_STATE_DEFAULT:
+ if (timeSinceLastNotif < mTimeoutPolite) {
+ volState = POLITE_STATE_MUTED;
+ }
+ break;
+ case POLITE_STATE_POLITE:
+ if (timeSinceLastNotif > mTimeoutMuted) {
+ volState = POLITE_STATE_DEFAULT;
+ }
+ break;
+ case POLITE_STATE_MUTED:
+ if (timeSinceLastNotif > mTimeoutMuted) {
+ volState = POLITE_STATE_DEFAULT;
+ } else {
+ volState = POLITE_STATE_POLITE;
+ }
+ break;
+ default:
+ Log.w(TAG, "onNotificationPosted unexpected volume state: " + volState);
+ break;
+ }
+
+ if (DEBUG) {
+ Log.i(TAG, "onNotificationPosted time delta: " + timeSinceLastNotif + " vol state: "
+ + volState + " key: " + key);
+ }
+
+ mVolumeStates.put(key, volState);
+ }
+ }
+
//====================== Observers =============================
private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
@Override
@@ -859,6 +1302,11 @@
if (mNotificationLight != null) {
mNotificationLight.turnOff();
}
+ } else if (action.equals(Intent.ACTION_USER_ADDED)
+ || action.equals(Intent.ACTION_USER_REMOVED)
+ || action.equals(Intent.ACTION_USER_SWITCHED)
+ || action.equals(Intent.ACTION_USER_UNLOCKED)) {
+ loadUserSettings();
}
}
};
@@ -867,6 +1315,12 @@
private static final Uri NOTIFICATION_LIGHT_PULSE_URI = Settings.System.getUriFor(
Settings.System.NOTIFICATION_LIGHT_PULSE);
+ private static final Uri NOTIFICATION_COOLDOWN_ENABLED_URI = Settings.System.getUriFor(
+ Settings.System.NOTIFICATION_COOLDOWN_ENABLED);
+ private static final Uri NOTIFICATION_COOLDOWN_ALL_URI = Settings.System.getUriFor(
+ Settings.System.NOTIFICATION_COOLDOWN_ALL);
+ private static final Uri NOTIFICATION_COOLDOWN_VIBRATE_UNLOCKED_URI =
+ Settings.System.getUriFor(Settings.System.NOTIFICATION_COOLDOWN_VIBRATE_UNLOCKED);
public SettingsObserver() {
super(null);
}
@@ -884,11 +1338,45 @@
updateLightsLocked();
}
}
+ if (mEnablePoliteNotificationsFeature) {
+ if (NOTIFICATION_COOLDOWN_ENABLED_URI.equals(uri)) {
+ mNotificationCooldownEnabled = Settings.System.getIntForUser(
+ mContext.getContentResolver(),
+ Settings.System.NOTIFICATION_COOLDOWN_ENABLED,
+ DEFAULT_NOTIFICATION_COOLDOWN_ENABLED,
+ UserHandle.USER_CURRENT) != 0;
+
+ if (mCurrentWorkProfileId != UserHandle.USER_NULL) {
+ mNotificationCooldownForWorkEnabled = Settings.System.getIntForUser(
+ mContext.getContentResolver(),
+ Settings.System.NOTIFICATION_COOLDOWN_ENABLED,
+ DEFAULT_NOTIFICATION_COOLDOWN_ENABLED_FOR_WORK,
+ mCurrentWorkProfileId)
+ != 0;
+ } else {
+ mNotificationCooldownForWorkEnabled = false;
+ }
+ }
+ if (NOTIFICATION_COOLDOWN_ALL_URI.equals(uri)) {
+ mNotificationCooldownApplyToAll = Settings.System.getIntForUser(
+ mContext.getContentResolver(),
+ Settings.System.NOTIFICATION_COOLDOWN_ALL,
+ DEFAULT_NOTIFICATION_COOLDOWN_ALL, UserHandle.USER_CURRENT)
+ != 0;
+ }
+ if (NOTIFICATION_COOLDOWN_VIBRATE_UNLOCKED_URI.equals(uri)) {
+ mNotificationCooldownVibrateUnlocked = Settings.System.getIntForUser(
+ mContext.getContentResolver(),
+ Settings.System.NOTIFICATION_COOLDOWN_VIBRATE_UNLOCKED,
+ DEFAULT_NOTIFICATION_COOLDOWN_VIBRATE_UNLOCKED,
+ UserHandle.USER_CURRENT) != 0;
+ }
+ }
}
}
- //TODO: cleanup most (all?) of these
+ // TODO b/270456865: cleanup most (all?) of these
//======================= FOR TESTS =====================
@VisibleForTesting
void setIsAutomotive(boolean isAutomotive) {
@@ -931,6 +1419,11 @@
}
@VisibleForTesting
+ void setUserPresent(boolean userPresent) {
+ mUserPresent = userPresent;
+ }
+
+ @VisibleForTesting
void setLights(LogicalLight light) {
mNotificationLight = light;
mAttentionLight = light;
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 87c3067..837b761 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -2532,7 +2532,7 @@
if (mFlagResolver.isEnabled(NotificationFlags.ENABLE_ATTENTION_HELPER_REFACTOR)) {
mAttentionHelper = new NotificationAttentionHelper(getContext(), lightsManager,
- mAccessibilityManager, mPackageManagerClient, usageStats,
+ mAccessibilityManager, mPackageManagerClient, userManager, usageStats,
mNotificationManagerPrivate, mZenModeHelper, flagResolver);
}
@@ -3369,6 +3369,10 @@
mAppUsageStats.reportEvent(r.getSbn().getPackageName(),
getRealUserId(r.getSbn().getUserId()),
UsageEvents.Event.USER_INTERACTION);
+
+ if (Flags.politeNotifications()) {
+ mAttentionHelper.onUserInteraction(r);
+ }
}
private int getRealUserId(int userId) {
@@ -5730,13 +5734,18 @@
public void setNotificationListenerAccessGrantedForUser(ComponentName listener, int userId,
boolean granted, boolean userSet) {
Objects.requireNonNull(listener);
+ if (UserHandle.getCallingUserId() != userId) {
+ getContext().enforceCallingOrSelfPermission(
+ android.Manifest.permission.INTERACT_ACROSS_USERS,
+ "setNotificationListenerAccessGrantedForUser for user " + userId);
+ }
checkNotificationListenerAccess();
if (granted && listener.flattenToString().length()
> NotificationManager.MAX_SERVICE_COMPONENT_NAME_LENGTH) {
throw new IllegalArgumentException(
"Component name too long: " + listener.flattenToString());
}
- if (!userSet && isNotificationListenerAccessUserSet(listener)) {
+ if (!userSet && isNotificationListenerAccessUserSet(listener, userId)) {
// Don't override user's choice
return;
}
@@ -5762,9 +5771,8 @@
}
}
- private boolean isNotificationListenerAccessUserSet(ComponentName listener) {
- return mListeners.isPackageOrComponentUserSet(listener.flattenToString(),
- getCallingUserHandle().getIdentifier());
+ private boolean isNotificationListenerAccessUserSet(ComponentName listener, int userId) {
+ return mListeners.isPackageOrComponentUserSet(listener.flattenToString(), userId);
}
@Override
@@ -8612,7 +8620,7 @@
.setCategory(MetricsEvent.NOTIFICATION_ALERT)
.setType(MetricsEvent.TYPE_OPEN)
.setSubtype(buzzBeepBlink));
- EventLogTags.writeNotificationAlert(key, buzz ? 1 : 0, beep ? 1 : 0, blink ? 1 : 0);
+ EventLogTags.writeNotificationAlert(key, buzz ? 1 : 0, beep ? 1 : 0, blink ? 1 : 0, 0);
}
record.setAudiblyAlerted(buzz || beep);
return buzzBeepBlink;
@@ -8757,7 +8765,7 @@
if (DBG) Slog.v(TAG, "Playing sound " + soundUri
+ " with attributes " + record.getAudioAttributes());
player.playAsync(soundUri, record.getSbn().getUser(), looping,
- record.getAudioAttributes());
+ record.getAudioAttributes(), 1.0f);
return true;
}
} catch (RemoteException e) {
diff --git a/services/core/java/com/android/server/notification/VibratorHelper.java b/services/core/java/com/android/server/notification/VibratorHelper.java
index e5d07bc..7204d05 100644
--- a/services/core/java/com/android/server/notification/VibratorHelper.java
+++ b/services/core/java/com/android/server/notification/VibratorHelper.java
@@ -50,6 +50,7 @@
private final long[] mFallbackPattern;
@Nullable private final float[] mDefaultPwlePattern;
@Nullable private final float[] mFallbackPwlePattern;
+ private final int mDefaultVibrationAmplitude;
public VibratorHelper(Context context) {
mVibrator = context.getSystemService(Vibrator.class);
@@ -65,6 +66,8 @@
com.android.internal.R.array.config_defaultNotificationVibeWaveform);
mFallbackPwlePattern = getFloatArray(context.getResources(),
com.android.internal.R.array.config_notificationFallbackVibeWaveform);
+ mDefaultVibrationAmplitude = context.getResources().getInteger(
+ com.android.internal.R.integer.config_defaultVibrationAmplitude);
}
/**
@@ -136,6 +139,14 @@
}
/**
+ * Scale vibration effect, valid range is [0.0f, 1.0f]
+ * Resolves default amplitude value if not already set.
+ */
+ public VibrationEffect scale(VibrationEffect effect, float scale) {
+ return effect.resolve(mDefaultVibrationAmplitude).scale(scale);
+ }
+
+ /**
* Vibrate the device with given {@code effect}.
*
* <p>We need to vibrate as "android" so we can breakthrough DND.
diff --git a/services/core/java/com/android/server/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..30017be 100644
--- a/services/core/java/com/android/server/pm/ComputerEngine.java
+++ b/services/core/java/com/android/server/pm/ComputerEngine.java
@@ -505,6 +505,10 @@
int filterCallingUid, int userId, boolean resolveForStart,
boolean allowDynamicSplits) {
if (!mUserManager.exists(userId)) return Collections.emptyList();
+
+ // Allow to match activities of quarantined packages.
+ flags |= PackageManager.MATCH_QUARANTINED_COMPONENTS;
+
final String instantAppPkgName = getInstantAppPackageName(filterCallingUid);
enforceCrossUserPermission(Binder.getCallingUid(), userId,
false /* requireFullPermission */, false /* checkShell */,
@@ -647,11 +651,6 @@
flags = updateFlagsForResolve(flags, userId, callingUid, includeInstantApps,
false /* isImplicitImageCaptureIntentAndNotSetByDpc */);
- // Only if the query is coming from the system process,
- // it should be allowed to match quarantined components
- if (callingUid != Process.SYSTEM_UID) {
- flags |= PackageManager.FILTER_OUT_QUARANTINED_COMPONENTS;
- }
Intent originalIntent = null;
ComponentName comp = intent.getComponent();
if (comp == null) {
@@ -4047,9 +4046,6 @@
flags = updateFlagsForComponent(flags, userId);
enforceCrossUserPermission(callingUid, userId, false /* requireFullPermission */,
false /* checkShell */, "get provider info");
- if (callingUid != Process.SYSTEM_UID) {
- flags |= PackageManager.FILTER_OUT_QUARANTINED_COMPONENTS;
- }
ParsedProvider p = mComponentResolver.getProvider(component);
if (DEBUG_PACKAGE_INFO) Log.v(
TAG, "getProviderInfo " + component + ": " + p);
@@ -4679,9 +4675,6 @@
int callingUid) {
if (!mUserManager.exists(userId)) return null;
flags = updateFlagsForComponent(flags, userId);
- if (callingUid != Process.SYSTEM_UID) {
- flags |= PackageManager.FILTER_OUT_QUARANTINED_COMPONENTS;
- }
final ProviderInfo providerInfo = mComponentResolver.queryProvider(this, name, flags,
userId);
boolean checkedGrants = false;
@@ -4794,13 +4787,6 @@
false /* checkShell */, "queryContentProviders");
if (!mUserManager.exists(userId)) return ParceledListSlice.emptyList();
flags = updateFlagsForComponent(flags, userId);
-
- // Only if the service query is coming from the system process,
- // it should be allowed to match quarantined components
- if (callingUid != Process.SYSTEM_UID) {
- flags |= PackageManager.FILTER_OUT_QUARANTINED_COMPONENTS;
- }
-
ArrayList<ProviderInfo> finalList = null;
final List<ProviderInfo> matchList = mComponentResolver.queryProviders(this, processName,
metaDataKey, uid, flags, userId);
@@ -4938,7 +4924,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 +4943,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..2967818 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));
@@ -1267,10 +1269,9 @@
replace = true;
if (DEBUG_INSTALL) Slog.d(TAG, "Replace existing package: " + pkgName);
}
- final AndroidPackage oldPackage = mPm.mPackages.get(pkgName);
- if (replace && oldPackage != null) {
+ if (replace) {
// Prevent apps opting out from runtime permissions
- final int oldTargetSdk = oldPackage.getTargetSdkVersion();
+ final int oldTargetSdk = ps.getTargetSdkVersion();
final int newTargetSdk = parsedPackage.getTargetSdkVersion();
if (oldTargetSdk > Build.VERSION_CODES.LOLLIPOP_MR1
&& newTargetSdk <= Build.VERSION_CODES.LOLLIPOP_MR1) {
@@ -1282,10 +1283,10 @@
+ " target SDK " + oldTargetSdk + " does.");
}
// Prevent persistent apps from being updated
- if (oldPackage.isPersistent()
+ if (ps.isPersistent()
&& ((installFlags & PackageManager.INSTALL_STAGED) == 0)) {
throw new PrepareFailure(PackageManager.INSTALL_FAILED_INVALID_APK,
- "Package " + oldPackage.getPackageName() + " is a persistent app. "
+ "Package " + pkgName + " is a persistent app. "
+ "Persistent apps are not updateable.");
}
}
@@ -1666,7 +1667,7 @@
}
// don't allow a system upgrade unless the upgrade hash matches
- if (oldPackage != null && oldPackage.getRestrictUpdateHash() != null
+ if (oldPackageState.getRestrictUpdateHash() != null
&& oldPackageState.isSystem()) {
final byte[] digestBytes;
try {
@@ -1682,12 +1683,13 @@
throw new PrepareFailure(INSTALL_FAILED_INVALID_APK,
"Could not compute hash: " + pkgName11);
}
- if (!Arrays.equals(oldPackage.getRestrictUpdateHash(), digestBytes)) {
+ if (!Arrays.equals(oldPackageState.getRestrictUpdateHash(), digestBytes)) {
throw new PrepareFailure(INSTALL_FAILED_INVALID_APK,
"New package fails restrict-update check: " + pkgName11);
}
// retain upgrade restriction
- parsedPackage.setRestrictUpdateHash(oldPackage.getRestrictUpdateHash());
+ parsedPackage.setRestrictUpdateHash(
+ oldPackageState.getRestrictUpdateHash());
}
if (oldPackage != null) {
diff --git a/services/core/java/com/android/server/pm/InstallRequest.java b/services/core/java/com/android/server/pm/InstallRequest.java
index 2d19282..7d822b5 100644
--- a/services/core/java/com/android/server/pm/InstallRequest.java
+++ b/services/core/java/com/android/server/pm/InstallRequest.java
@@ -22,6 +22,8 @@
import static android.content.pm.PackageManager.INSTALL_SUCCEEDED;
import static android.os.Process.INVALID_UID;
+import static com.android.server.art.model.DexoptResult.DexContainerFileDexoptResult;
+import static com.android.server.art.model.DexoptResult.PackageDexoptResult;
import static com.android.server.pm.PackageManagerService.EMPTY_INT_ARRAY;
import static com.android.server.pm.PackageManagerService.SCAN_AS_INSTANT_APP;
import static com.android.server.pm.PackageManagerService.TAG;
@@ -32,6 +34,7 @@
import android.app.AppOpsManager;
import android.content.pm.ArchivedPackageParcel;
import android.content.pm.DataLoaderType;
+import android.content.pm.Flags;
import android.content.pm.IPackageInstallObserver2;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
@@ -56,6 +59,7 @@
import java.io.File;
import java.util.ArrayList;
+import java.util.LinkedHashSet;
import java.util.List;
final class InstallRequest {
@@ -147,6 +151,9 @@
@NonNull
private int[] mUpdateBroadcastInstantUserIds = EMPTY_INT_ARRAY;
+ @NonNull
+ private ArrayList<String> mWarnings = new ArrayList<>();
+
// New install
InstallRequest(InstallingSession params) {
mUserId = params.getUser().getIdentifier();
@@ -658,6 +665,11 @@
return mUpdateBroadcastInstantUserIds;
}
+ @NonNull
+ public ArrayList<String> getWarnings() {
+ return mWarnings;
+ }
+
public void setScanFlags(int scanFlags) {
mScanFlags = scanFlags;
}
@@ -855,6 +867,10 @@
}
}
+ public void addWarning(@NonNull String warning) {
+ mWarnings.add(warning);
+ }
+
public void onPrepareStarted() {
if (mPackageMetrics != null) {
mPackageMetrics.onStepStarted(PackageMetrics.STEP_PREPARE);
@@ -904,22 +920,37 @@
}
public void onDexoptFinished(DexoptResult dexoptResult) {
- if (mPackageMetrics == null) {
- return;
- }
- mDexoptStatus = dexoptResult.getFinalStatus();
- if (mDexoptStatus != DexoptResult.DEXOPT_PERFORMED) {
- return;
- }
- long durationMillis = 0;
- for (DexoptResult.PackageDexoptResult packageResult :
- dexoptResult.getPackageDexoptResults()) {
- for (DexoptResult.DexContainerFileDexoptResult fileResult :
- packageResult.getDexContainerFileDexoptResults()) {
- durationMillis += fileResult.getDex2oatWallTimeMillis();
+ // Only report external profile warnings when installing from adb. The goal is to warn app
+ // developers if they have provided bad external profiles, so it's not beneficial to report
+ // those warnings in the normal app install workflow.
+ if (isInstallFromAdb() && Flags.useArtServiceV2()) {
+ var externalProfileErrors = new LinkedHashSet<String>();
+ for (PackageDexoptResult packageResult : dexoptResult.getPackageDexoptResults()) {
+ for (DexContainerFileDexoptResult fileResult :
+ packageResult.getDexContainerFileDexoptResults()) {
+ externalProfileErrors.addAll(fileResult.getExternalProfileErrors());
+ }
+ }
+ if (!externalProfileErrors.isEmpty()) {
+ addWarning("Error occurred during dexopt when processing external profiles:\n "
+ + String.join("\n ", externalProfileErrors));
}
}
- mPackageMetrics.onStepFinished(PackageMetrics.STEP_DEXOPT, durationMillis);
+
+ // Report dexopt metrics.
+ if (mPackageMetrics != null) {
+ mDexoptStatus = dexoptResult.getFinalStatus();
+ if (mDexoptStatus == DexoptResult.DEXOPT_PERFORMED) {
+ long durationMillis = 0;
+ for (PackageDexoptResult packageResult : dexoptResult.getPackageDexoptResults()) {
+ for (DexContainerFileDexoptResult fileResult :
+ packageResult.getDexContainerFileDexoptResults()) {
+ durationMillis += fileResult.getDex2oatWallTimeMillis();
+ }
+ }
+ mPackageMetrics.onStepFinished(PackageMetrics.STEP_DEXOPT, durationMillis);
+ }
+ }
}
public void onInstallCompleted() {
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 11660a59..a161e8c 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -107,19 +107,22 @@
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.CollectionUtils;
import com.android.internal.util.Preconditions;
+import com.android.internal.util.SizedInputStream;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.pm.pkg.AndroidPackage;
import com.android.server.wm.ActivityTaskManagerInternal;
+import java.io.DataInputStream;
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.InputStream;
+import java.io.OutputStream;
import java.io.PrintWriter;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
-import java.nio.file.StandardCopyOption;
+import java.nio.file.StandardOpenOption;
import java.nio.file.attribute.PosixFilePermission;
import java.util.ArrayList;
import java.util.Arrays;
@@ -130,6 +133,8 @@
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
import java.util.function.BiConsumer;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
@@ -216,6 +221,7 @@
private final ShortcutChangeHandler mShortcutChangeHandler;
private final Handler mCallbackHandler;
+ private final ExecutorService mOnDumpExecutor = Executors.newSingleThreadExecutor();
private PackageInstallerService mPackageInstallerService;
@@ -1512,7 +1518,7 @@
forEachViewCaptureWindow((fileName, is) -> {
try {
zipOs.putNextEntry(new ZipEntry("FS" + fileName));
- is.transferTo(zipOs);
+ transferViewCaptureData(is, zipOs);
zipOs.closeEntry();
} catch (IOException e) {
getErrPrintWriter().write("Failed to output " + fileName
@@ -1553,8 +1559,9 @@
private void dumpViewCaptureDataToWmTrace(@NonNull String fileName,
@NonNull InputStream is) {
Path outPath = Paths.get(fileName);
- try {
- Files.copy(is, outPath, StandardCopyOption.REPLACE_EXISTING);
+ try (OutputStream os = Files.newOutputStream(outPath, StandardOpenOption.CREATE,
+ StandardOpenOption.TRUNCATE_EXISTING)) {
+ transferViewCaptureData(is, os);
Files.setPosixFilePermissions(outPath, WM_TRACE_FILE_PERMISSIONS);
} catch (IOException e) {
Log.d(TAG, "failed to write data to " + fileName + " in wmtrace dir", e);
@@ -1562,6 +1569,15 @@
}
/**
+ * Raw input stream reads hang on the final read when transferring data in via the pipe.
+ * The fix used below is to count and read the exact amount of bytes being sent.
+ */
+ private void transferViewCaptureData(InputStream is, OutputStream os) throws IOException {
+ DataInputStream dataInputStream = new DataInputStream(is);
+ new SizedInputStream(dataInputStream, dataInputStream.readInt()).transferTo(os);
+ }
+
+ /**
* IDumpCallback.onDump alerts the in-process ViewCapture instance to start sending data
* to LauncherAppsService via the pipe's input provided. This data (as well as an output
* file name) is provided to the consumer via an InputStream to output where it wants (for
@@ -1569,24 +1585,37 @@
*/
private void forEachViewCaptureWindow(
@NonNull BiConsumer<String, InputStream> outputtingConsumer) {
- for (int i = mDumpCallbacks.beginBroadcast() - 1; i >= 0; i--) {
- String packageName = (String) mDumpCallbacks.getBroadcastCookie(i);
- String fileName = WM_TRACE_DIR + packageName + "_" + i + VC_FILE_SUFFIX;
+ try {
+ // This multi-threading prevents ctrl-C command line command aborting from putting
+ // the mDumpCallbacks RemoteCallbackList in a bad Broadcast state. We need to wait
+ // for it to complete even though it is on a background thread.
+ mOnDumpExecutor.submit(() -> {
+ try {
+ for (int i = mDumpCallbacks.beginBroadcast() - 1; i >= 0; i--) {
+ String packageName = (String) mDumpCallbacks.getBroadcastCookie(i);
+ String fileName = WM_TRACE_DIR + packageName + "_" + i + VC_FILE_SUFFIX;
- try {
- // Order is important here. OnDump needs to be called before the BiConsumer
- // accepts & starts blocking on reading the input stream.
- ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createPipe();
- mDumpCallbacks.getBroadcastItem(i).onDump(pipe[1]);
+ try {
+ // Order is important here. OnDump needs to be called before the
+ // BiConsumer accepts & starts blocking on reading the input stream.
+ ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createPipe();
+ mDumpCallbacks.getBroadcastItem(i).onDump(pipe[1]);
- InputStream is = new ParcelFileDescriptor.AutoCloseInputStream(pipe[0]);
- outputtingConsumer.accept(fileName, is);
- is.close();
- } catch (Exception e) {
- Log.d(TAG, "failed to pipe view capture data", e);
- }
+ InputStream is = new ParcelFileDescriptor.AutoCloseInputStream(
+ pipe[0]);
+ outputtingConsumer.accept(fileName, is);
+ is.close();
+ } catch (Exception e) {
+ Log.d(TAG, "failed to pipe view capture data", e);
+ }
+ }
+ } finally {
+ mDumpCallbacks.finishBroadcast();
+ }
+ }).get();
+ } catch (InterruptedException | ExecutionException e) {
+ Log.e(TAG, "background work was interrupted", e);
}
- mDumpCallbacks.finishBroadcast();
}
@RequiresPermission(READ_FRAME_BUFFER)
diff --git a/services/core/java/com/android/server/pm/MovePackageHelper.java b/services/core/java/com/android/server/pm/MovePackageHelper.java
index 148e0df..9ad8318 100644
--- a/services/core/java/com/android/server/pm/MovePackageHelper.java
+++ b/services/core/java/com/android/server/pm/MovePackageHelper.java
@@ -89,6 +89,21 @@
if (packageState == null || packageState.getPkg() == null) {
throw new PackageManagerException(MOVE_FAILED_DOESNT_EXIST, "Missing package");
}
+ final int[] installedUserIds = PackageStateUtils.queryInstalledUsers(packageState,
+ mPm.mUserManager.getUserIds(), true);
+ final UserHandle userForMove;
+ if (installedUserIds.length > 0) {
+ userForMove = UserHandle.of(installedUserIds[0]);
+ } else {
+ throw new PackageManagerException(MOVE_FAILED_DOESNT_EXIST,
+ "Package is not installed for any user");
+ }
+ for (int userId : installedUserIds) {
+ if (snapshot.shouldFilterApplicationIncludingUninstalled(packageState, callingUid,
+ userId)) {
+ throw new PackageManagerException(MOVE_FAILED_DOESNT_EXIST, "Missing package");
+ }
+ }
final AndroidPackage pkg = packageState.getPkg();
if (packageState.isSystem()) {
throw new PackageManagerException(MOVE_FAILED_SYSTEM_PACKAGE,
@@ -134,8 +149,6 @@
final String label = String.valueOf(pm.getApplicationLabel(
AndroidPackageUtils.generateAppInfoWithoutState(pkg)));
final int targetSdkVersion = pkg.getTargetSdkVersion();
- final int[] installedUserIds = PackageStateUtils.queryInstalledUsers(packageState,
- mPm.mUserManager.getUserIds(), true);
final String fromCodePath;
if (codeFile.getParentFile().getName().startsWith(
PackageManagerService.RANDOM_DIR_PREFIX)) {
@@ -303,8 +316,8 @@
final PackageLite lite = ret.isSuccess() ? ret.getResult() : null;
final InstallingSession installingSession = new InstallingSession(origin, move,
installObserver, installFlags, /* developmentInstallFlags= */ 0, installSource,
- volumeUuid, user, packageAbiOverride, PackageInstaller.PACKAGE_SOURCE_UNSPECIFIED,
- lite, mPm);
+ volumeUuid, userForMove, packageAbiOverride,
+ PackageInstaller.PACKAGE_SOURCE_UNSPECIFIED, lite, mPm);
installingSession.movePackage();
}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 6627039..d0e5f96 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -2930,15 +2930,40 @@
* @return a future that will be completed when the whole process is completed.
*/
private CompletableFuture<Void> install() {
+ // `futures` either contains only one session (`this`) or contains one parent session
+ // (`this`) and n-1 child sessions.
List<CompletableFuture<InstallResult>> futures = installNonStaged();
CompletableFuture<InstallResult>[] arr = new CompletableFuture[futures.size()];
return CompletableFuture.allOf(futures.toArray(arr)).whenComplete((r, t) -> {
if (t == null) {
setSessionApplied();
+ var multiPackageWarnings = new ArrayList<String>();
+ if (isMultiPackage()) {
+ // This is a parent session. Collect warnings from children.
+ for (CompletableFuture<InstallResult> f : futures) {
+ InstallResult result = f.join();
+ if (result.session != this && result.extras != null) {
+ ArrayList<String> childWarnings = result.extras.getStringArrayList(
+ PackageInstaller.EXTRA_WARNINGS);
+ if (!ArrayUtils.isEmpty(childWarnings)) {
+ multiPackageWarnings.addAll(childWarnings);
+ }
+ }
+ }
+ }
for (CompletableFuture<InstallResult> f : futures) {
InstallResult result = f.join();
+ Bundle extras = result.extras;
+ if (isMultiPackage() && result.session == this
+ && !multiPackageWarnings.isEmpty()) {
+ if (extras == null) {
+ extras = new Bundle();
+ }
+ extras.putStringArrayList(
+ PackageInstaller.EXTRA_WARNINGS, multiPackageWarnings);
+ }
result.session.dispatchSessionFinished(
- INSTALL_SUCCEEDED, "Session installed", result.extras);
+ INSTALL_SUCCEEDED, "Session installed", extras);
}
} else {
PackageManagerException e = (PackageManagerException) t.getCause();
@@ -5189,6 +5214,10 @@
if (!TextUtils.isEmpty(existing)) {
fillIn.putExtra(PackageInstaller.EXTRA_OTHER_PACKAGE_NAME, existing);
}
+ ArrayList<String> warnings = extras.getStringArrayList(PackageInstaller.EXTRA_WARNINGS);
+ if (!ArrayUtils.isEmpty(warnings)) {
+ fillIn.putStringArrayListExtra(PackageInstaller.EXTRA_WARNINGS, warnings);
+ }
}
try {
final BroadcastOptions options = BroadcastOptions.makeBasic();
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index a99a0c0..abeabc9 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -1434,6 +1434,9 @@
break;
}
}
+ if (!request.getWarnings().isEmpty()) {
+ extras.putStringArrayList(PackageInstaller.EXTRA_WARNINGS, request.getWarnings());
+ }
return extras;
}
@@ -1466,8 +1469,7 @@
archPkg.versionCodeMajor = (int) (longVersionCode >> 32);
archPkg.versionCode = (int) longVersionCode;
- // TODO(b/297916136): extract target sdk version.
- archPkg.targetSdkVersion = MIN_INSTALLABLE_TARGET_SDK;
+ archPkg.targetSdkVersion = ps.getTargetSdkVersion();
// These get translated in flags important for user data management.
archPkg.defaultToDeviceProtectedStorage = String.valueOf(
@@ -4498,6 +4500,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);
@@ -4519,6 +4522,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);
@@ -4550,6 +4554,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);
+ });
+ }
}
}
@@ -6926,6 +6948,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/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 3a9272d..7264e2e 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -4397,10 +4397,21 @@
session.commit(receiver.getIntentSender());
if (!session.isStaged()) {
final Intent result = receiver.getResult();
- final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
- PackageInstaller.STATUS_FAILURE);
+ int status = result.getIntExtra(
+ PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_FAILURE);
+ List<String> warnings =
+ result.getStringArrayListExtra(PackageInstaller.EXTRA_WARNINGS);
if (status == PackageInstaller.STATUS_SUCCESS) {
- if (logSuccess) {
+ if (!ArrayUtils.isEmpty(warnings)) {
+ // Don't start the output string with "Success" because that will make adb
+ // treat this as a success.
+ for (String warning : warnings) {
+ pw.println("Warning: " + warning);
+ }
+ // Treat warnings as failure to draw app developers' attention.
+ status = PackageInstaller.STATUS_FAILURE;
+ pw.println("Completed with warning(s)");
+ } else if (logSuccess) {
pw.println("Success");
}
} else {
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index 2e60064..9f4e86d 100644
--- a/services/core/java/com/android/server/pm/PackageSetting.java
+++ b/services/core/java/com/android/server/pm/PackageSetting.java
@@ -99,6 +99,7 @@
private static final int DEFAULT_TO_DEVICE_PROTECTED_STORAGE = 1 << 1;
private static final int UPDATE_AVAILABLE = 1 << 2;
private static final int FORCE_QUERYABLE_OVERRIDE = 1 << 3;
+ private static final int PERSISTENT = 1 << 4;
}
private int mBooleans;
@@ -217,6 +218,11 @@
@Nullable
private String mAppMetadataFilePath;
+ private int mTargetSdkVersion;
+
+ @Nullable
+ private byte[] mRestrictUpdateHash;
+
/**
* Snapshot support.
*/
@@ -535,6 +541,24 @@
onChanged();
}
+ public PackageSetting setIsPersistent(boolean isPersistent) {
+ setBoolean(Booleans.PERSISTENT, isPersistent);
+ onChanged();
+ return this;
+ }
+
+ public PackageSetting setTargetSdkVersion(int targetSdkVersion) {
+ mTargetSdkVersion = targetSdkVersion;
+ onChanged();
+ return this;
+ }
+
+ public PackageSetting setRestrictUpdateHash(byte[] restrictUpdateHash) {
+ mRestrictUpdateHash = restrictUpdateHash;
+ onChanged();
+ return this;
+ }
+
@Override
public int getSharedUserAppId() {
return mSharedUserAppId;
@@ -701,6 +725,9 @@
categoryOverride = other.categoryOverride;
mDomainSetId = other.mDomainSetId;
mAppMetadataFilePath = other.mAppMetadataFilePath;
+ mTargetSdkVersion = other.mTargetSdkVersion;
+ mRestrictUpdateHash = other.mRestrictUpdateHash == null
+ ? null : other.mRestrictUpdateHash.clone();
usesSdkLibraries = other.usesSdkLibraries != null
? Arrays.copyOf(other.usesSdkLibraries,
@@ -1572,6 +1599,11 @@
return getBoolean(Booleans.DEFAULT_TO_DEVICE_PROTECTED_STORAGE);
}
+ @Override
+ public boolean isPersistent() {
+ return getBoolean(Booleans.PERSISTENT);
+ }
+
// Code below generated by codegen v1.0.23.
@@ -1714,11 +1746,21 @@
return mAppMetadataFilePath;
}
+ @DataClass.Generated.Member
+ public int getTargetSdkVersion() {
+ return mTargetSdkVersion;
+ }
+
+ @DataClass.Generated.Member
+ public @Nullable byte[] getRestrictUpdateHash() {
+ return mRestrictUpdateHash;
+ }
+
@DataClass.Generated(
- time = 1694196905013L,
+ time = 1696979728639L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/services/core/java/com/android/server/pm/PackageSetting.java",
- inputSignatures = "private int mBooleans\nprivate int mSharedUserAppId\nprivate @android.annotation.Nullable java.util.Map<java.lang.String,java.util.Set<java.lang.String>> mimeGroups\nprivate @java.lang.Deprecated @android.annotation.Nullable java.util.Set<java.lang.String> mOldCodePaths\nprivate @android.annotation.Nullable java.lang.String[] usesSdkLibraries\nprivate @android.annotation.Nullable long[] usesSdkLibrariesVersionsMajor\nprivate @android.annotation.Nullable java.lang.String[] usesStaticLibraries\nprivate @android.annotation.Nullable long[] usesStaticLibrariesVersions\nprivate @android.annotation.Nullable @java.lang.Deprecated java.lang.String legacyNativeLibraryPath\nprivate @android.annotation.NonNull java.lang.String mName\nprivate @android.annotation.Nullable java.lang.String mRealName\nprivate int mAppId\nprivate @android.annotation.Nullable com.android.server.pm.parsing.pkg.AndroidPackageInternal pkg\nprivate @android.annotation.NonNull java.io.File mPath\nprivate @android.annotation.NonNull java.lang.String mPathString\nprivate float mLoadingProgress\nprivate long mLoadingCompletedTime\nprivate @android.annotation.Nullable java.lang.String mPrimaryCpuAbi\nprivate @android.annotation.Nullable java.lang.String mSecondaryCpuAbi\nprivate @android.annotation.Nullable java.lang.String mCpuAbiOverride\nprivate long mLastModifiedTime\nprivate long lastUpdateTime\nprivate long versionCode\nprivate @android.annotation.NonNull com.android.server.pm.PackageSignatures signatures\nprivate @android.annotation.NonNull com.android.server.pm.PackageKeySetData keySetData\nprivate final @android.annotation.NonNull android.util.SparseArray<com.android.server.pm.pkg.PackageUserStateImpl> mUserStates\nprivate @android.annotation.NonNull com.android.server.pm.InstallSource installSource\nprivate @android.annotation.Nullable java.lang.String volumeUuid\nprivate int categoryOverride\nprivate final @android.annotation.NonNull com.android.server.pm.pkg.PackageStateUnserialized pkgState\nprivate @android.annotation.NonNull java.util.UUID mDomainSetId\nprivate @android.annotation.Nullable java.lang.String mAppMetadataFilePath\nprivate final @android.annotation.NonNull com.android.server.utils.SnapshotCache<com.android.server.pm.PackageSetting> mSnapshot\nprivate void setBoolean(int,boolean)\nprivate boolean getBoolean(int)\nprivate com.android.server.utils.SnapshotCache<com.android.server.pm.PackageSetting> makeCache()\npublic com.android.server.pm.PackageSetting snapshot()\npublic void dumpDebug(android.util.proto.ProtoOutputStream,long,java.util.List<android.content.pm.UserInfo>,com.android.server.pm.permission.LegacyPermissionDataProvider)\npublic com.android.server.pm.PackageSetting setAppId(int)\npublic com.android.server.pm.PackageSetting setCpuAbiOverride(java.lang.String)\npublic com.android.server.pm.PackageSetting setFirstInstallTimeFromReplaced(com.android.server.pm.pkg.PackageStateInternal,int[])\npublic com.android.server.pm.PackageSetting setFirstInstallTime(long,int)\npublic com.android.server.pm.PackageSetting setForceQueryableOverride(boolean)\npublic com.android.server.pm.PackageSetting setInstallerPackage(java.lang.String,int)\npublic com.android.server.pm.PackageSetting setUpdateOwnerPackage(java.lang.String)\npublic com.android.server.pm.PackageSetting setInstallSource(com.android.server.pm.InstallSource)\n com.android.server.pm.PackageSetting removeInstallerPackage(java.lang.String)\npublic com.android.server.pm.PackageSetting setIsOrphaned(boolean)\npublic com.android.server.pm.PackageSetting setKeySetData(com.android.server.pm.PackageKeySetData)\npublic com.android.server.pm.PackageSetting setLastModifiedTime(long)\npublic com.android.server.pm.PackageSetting setLastUpdateTime(long)\npublic com.android.server.pm.PackageSetting setLongVersionCode(long)\npublic boolean setMimeGroup(java.lang.String,android.util.ArraySet<java.lang.String>)\npublic com.android.server.pm.PackageSetting setPkg(com.android.server.pm.pkg.AndroidPackage)\npublic com.android.server.pm.PackageSetting setPkgStateLibraryFiles(java.util.Collection<java.lang.String>)\npublic com.android.server.pm.PackageSetting setPrimaryCpuAbi(java.lang.String)\npublic com.android.server.pm.PackageSetting setSecondaryCpuAbi(java.lang.String)\npublic com.android.server.pm.PackageSetting setSignatures(com.android.server.pm.PackageSignatures)\npublic com.android.server.pm.PackageSetting setVolumeUuid(java.lang.String)\npublic com.android.server.pm.PackageSetting setDefaultToDeviceProtectedStorage(boolean)\npublic @java.lang.Override boolean isExternalStorage()\npublic com.android.server.pm.PackageSetting setUpdateAvailable(boolean)\npublic void setSharedUserAppId(int)\npublic @java.lang.Override int getSharedUserAppId()\npublic @java.lang.Override boolean hasSharedUser()\npublic @java.lang.Override java.lang.String toString()\nprotected void copyMimeGroups(java.util.Map<java.lang.String,java.util.Set<java.lang.String>>)\npublic void updateFrom(com.android.server.pm.PackageSetting)\n com.android.server.pm.PackageSetting updateMimeGroups(java.util.Set<java.lang.String>)\npublic @java.lang.Deprecated @java.lang.Override com.android.server.pm.permission.LegacyPermissionState getLegacyPermissionState()\npublic com.android.server.pm.PackageSetting setInstallPermissionsFixed(boolean)\npublic boolean isPrivileged()\npublic boolean isOem()\npublic boolean isVendor()\npublic boolean isProduct()\npublic @java.lang.Override boolean isRequiredForSystemUser()\npublic boolean isSystemExt()\npublic boolean isOdm()\npublic boolean isSystem()\npublic android.content.pm.SigningDetails getSigningDetails()\npublic com.android.server.pm.PackageSetting setSigningDetails(android.content.pm.SigningDetails)\npublic void copyPackageSetting(com.android.server.pm.PackageSetting,boolean)\n @com.android.internal.annotations.VisibleForTesting com.android.server.pm.pkg.PackageUserStateImpl modifyUserState(int)\npublic com.android.server.pm.pkg.PackageUserStateImpl getOrCreateUserState(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateInternal readUserState(int)\n void setEnabled(int,int,java.lang.String)\n int getEnabled(int)\n void setInstalled(boolean,int)\n boolean getInstalled(int)\n int getInstallReason(int)\n void setInstallReason(int,int)\n int getUninstallReason(int)\n void setUninstallReason(int,int)\n @android.annotation.NonNull android.content.pm.overlay.OverlayPaths getOverlayPaths(int)\n boolean setOverlayPathsForLibrary(java.lang.String,android.content.pm.overlay.OverlayPaths,int)\n boolean isInstalledOrHasDataOnAnyOtherUser(int[],int)\n int[] queryInstalledUsers(int[],boolean)\n long getCeDataInode(int)\n void setCeDataInode(long,int)\n void setDeDataInode(long,int)\n boolean getStopped(int)\n void setStopped(boolean,int)\n boolean getNotLaunched(int)\n void setNotLaunched(boolean,int)\n boolean getHidden(int)\n void setHidden(boolean,int)\n int getDistractionFlags(int)\n void setDistractionFlags(int,int)\npublic boolean getInstantApp(int)\n void setInstantApp(boolean,int)\n boolean getVirtualPreload(int)\n void setVirtualPreload(boolean,int)\n void setUserState(int,long,int,boolean,boolean,boolean,boolean,int,android.util.ArrayMap<java.lang.String,com.android.server.pm.pkg.SuspendParams>,boolean,boolean,java.lang.String,android.util.ArraySet<java.lang.String>,android.util.ArraySet<java.lang.String>,int,int,java.lang.String,java.lang.String,long,int,com.android.server.pm.pkg.ArchiveState)\n void setUserState(int,com.android.server.pm.pkg.PackageUserStateInternal)\n com.android.server.utils.WatchedArraySet<java.lang.String> getEnabledComponents(int)\n com.android.server.utils.WatchedArraySet<java.lang.String> getDisabledComponents(int)\n void setEnabledComponents(com.android.server.utils.WatchedArraySet<java.lang.String>,int)\n void setDisabledComponents(com.android.server.utils.WatchedArraySet<java.lang.String>,int)\n void setEnabledComponentsCopy(com.android.server.utils.WatchedArraySet<java.lang.String>,int)\n void setDisabledComponentsCopy(com.android.server.utils.WatchedArraySet<java.lang.String>,int)\n com.android.server.pm.pkg.PackageUserStateImpl modifyUserStateComponents(int,boolean,boolean)\n void addDisabledComponent(java.lang.String,int)\n void addEnabledComponent(java.lang.String,int)\n boolean enableComponentLPw(java.lang.String,int)\n boolean disableComponentLPw(java.lang.String,int)\n boolean restoreComponentLPw(java.lang.String,int)\n int getCurrentEnabledStateLPr(java.lang.String,int)\n void removeUser(int)\npublic int[] getNotInstalledUserIds()\n void writePackageUserPermissionsProto(android.util.proto.ProtoOutputStream,long,java.util.List<android.content.pm.UserInfo>,com.android.server.pm.permission.LegacyPermissionDataProvider)\nprotected void writeUsersInfoToProto(android.util.proto.ProtoOutputStream,long)\nprivate static void writeArchiveState(android.util.proto.ProtoOutputStream,com.android.server.pm.pkg.ArchiveState)\n com.android.server.pm.PackageSetting setPath(java.io.File)\npublic @com.android.internal.annotations.VisibleForTesting boolean overrideNonLocalizedLabelAndIcon(android.content.ComponentName,java.lang.String,java.lang.Integer,int)\npublic void resetOverrideComponentLabelIcon(int)\npublic @android.annotation.Nullable java.lang.String getSplashScreenTheme(int)\npublic boolean isIncremental()\npublic boolean isLoading()\npublic com.android.server.pm.PackageSetting setLoadingProgress(float)\npublic com.android.server.pm.PackageSetting setLoadingCompletedTime(long)\npublic com.android.server.pm.PackageSetting setAppMetadataFilePath(java.lang.String)\npublic @android.annotation.NonNull @java.lang.Override long getVersionCode()\npublic @android.annotation.Nullable @java.lang.Override java.util.Map<java.lang.String,java.util.Set<java.lang.String>> getMimeGroups()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String getPackageName()\npublic @android.annotation.Nullable @java.lang.Override com.android.server.pm.pkg.AndroidPackage getAndroidPackage()\npublic @android.annotation.NonNull android.content.pm.SigningInfo getSigningInfo()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String[] getUsesSdkLibraries()\npublic @android.annotation.NonNull @java.lang.Override long[] getUsesSdkLibrariesVersionsMajor()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String[] getUsesStaticLibraries()\npublic @android.annotation.NonNull @java.lang.Override long[] getUsesStaticLibrariesVersions()\npublic @android.annotation.NonNull @java.lang.Override java.util.List<com.android.server.pm.pkg.SharedLibrary> getSharedLibraryDependencies()\npublic @android.annotation.NonNull com.android.server.pm.PackageSetting addUsesLibraryInfo(android.content.pm.SharedLibraryInfo)\npublic @android.annotation.NonNull @java.lang.Override java.util.List<java.lang.String> getUsesLibraryFiles()\npublic @android.annotation.NonNull com.android.server.pm.PackageSetting addUsesLibraryFile(java.lang.String)\npublic @java.lang.Override boolean isHiddenUntilInstalled()\npublic @android.annotation.NonNull @java.lang.Override long[] getLastPackageUsageTime()\npublic @java.lang.Override boolean isUpdatedSystemApp()\npublic @java.lang.Override boolean isApkInUpdatedApex()\npublic @android.annotation.Nullable @java.lang.Override java.lang.String getApexModuleName()\npublic com.android.server.pm.PackageSetting setDomainSetId(java.util.UUID)\npublic com.android.server.pm.PackageSetting setCategoryOverride(int)\npublic com.android.server.pm.PackageSetting setLegacyNativeLibraryPath(java.lang.String)\npublic com.android.server.pm.PackageSetting setMimeGroups(java.util.Map<java.lang.String,java.util.Set<java.lang.String>>)\npublic com.android.server.pm.PackageSetting setOldCodePaths(java.util.Set<java.lang.String>)\npublic com.android.server.pm.PackageSetting setUsesSdkLibraries(java.lang.String[])\npublic com.android.server.pm.PackageSetting setUsesSdkLibrariesVersionsMajor(long[])\npublic com.android.server.pm.PackageSetting setUsesStaticLibraries(java.lang.String[])\npublic com.android.server.pm.PackageSetting setUsesStaticLibrariesVersions(long[])\npublic com.android.server.pm.PackageSetting setApexModuleName(java.lang.String)\npublic @android.annotation.NonNull @java.lang.Override com.android.server.pm.pkg.PackageStateUnserialized getTransientState()\npublic @android.annotation.NonNull android.util.SparseArray<? extends PackageUserStateInternal> getUserStates()\npublic com.android.server.pm.PackageSetting addMimeTypes(java.lang.String,java.util.Set<java.lang.String>)\npublic @android.annotation.NonNull @java.lang.Override com.android.server.pm.pkg.PackageUserState getStateForUser(android.os.UserHandle)\npublic @android.annotation.Nullable java.lang.String getPrimaryCpuAbi()\npublic @android.annotation.Nullable java.lang.String getSecondaryCpuAbi()\npublic @android.annotation.Nullable @java.lang.Override java.lang.String getSeInfo()\npublic @android.annotation.Nullable java.lang.String getPrimaryCpuAbiLegacy()\npublic @android.annotation.Nullable java.lang.String getSecondaryCpuAbiLegacy()\npublic @android.content.pm.ApplicationInfo.HiddenApiEnforcementPolicy @java.lang.Override int getHiddenApiEnforcementPolicy()\npublic @java.lang.Override boolean isApex()\npublic @java.lang.Override boolean isForceQueryableOverride()\npublic @java.lang.Override boolean isUpdateAvailable()\npublic @java.lang.Override boolean isInstallPermissionsFixed()\npublic @java.lang.Override boolean isDefaultToDeviceProtectedStorage()\nclass PackageSetting extends com.android.server.pm.SettingBase implements [com.android.server.pm.pkg.PackageStateInternal]\nprivate static final int INSTALL_PERMISSION_FIXED\nprivate static final int DEFAULT_TO_DEVICE_PROTECTED_STORAGE\nprivate static final int UPDATE_AVAILABLE\nprivate static final int FORCE_QUERYABLE_OVERRIDE\nclass Booleans extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genGetters=true, genConstructor=false, genSetters=false, genBuilder=false)")
+ inputSignatures = "private int mBooleans\nprivate int mSharedUserAppId\nprivate @android.annotation.Nullable java.util.Map<java.lang.String,java.util.Set<java.lang.String>> mimeGroups\nprivate @java.lang.Deprecated @android.annotation.Nullable java.util.Set<java.lang.String> mOldCodePaths\nprivate @android.annotation.Nullable java.lang.String[] usesSdkLibraries\nprivate @android.annotation.Nullable long[] usesSdkLibrariesVersionsMajor\nprivate @android.annotation.Nullable java.lang.String[] usesStaticLibraries\nprivate @android.annotation.Nullable long[] usesStaticLibrariesVersions\nprivate @android.annotation.Nullable @java.lang.Deprecated java.lang.String legacyNativeLibraryPath\nprivate @android.annotation.NonNull java.lang.String mName\nprivate @android.annotation.Nullable java.lang.String mRealName\nprivate int mAppId\nprivate @android.annotation.Nullable com.android.server.pm.parsing.pkg.AndroidPackageInternal pkg\nprivate @android.annotation.NonNull java.io.File mPath\nprivate @android.annotation.NonNull java.lang.String mPathString\nprivate float mLoadingProgress\nprivate long mLoadingCompletedTime\nprivate @android.annotation.Nullable java.lang.String mPrimaryCpuAbi\nprivate @android.annotation.Nullable java.lang.String mSecondaryCpuAbi\nprivate @android.annotation.Nullable java.lang.String mCpuAbiOverride\nprivate long mLastModifiedTime\nprivate long lastUpdateTime\nprivate long versionCode\nprivate @android.annotation.NonNull com.android.server.pm.PackageSignatures signatures\nprivate @android.annotation.NonNull com.android.server.pm.PackageKeySetData keySetData\nprivate final @android.annotation.NonNull android.util.SparseArray<com.android.server.pm.pkg.PackageUserStateImpl> mUserStates\nprivate @android.annotation.NonNull com.android.server.pm.InstallSource installSource\nprivate @android.annotation.Nullable java.lang.String volumeUuid\nprivate int categoryOverride\nprivate final @android.annotation.NonNull com.android.server.pm.pkg.PackageStateUnserialized pkgState\nprivate @android.annotation.NonNull java.util.UUID mDomainSetId\nprivate @android.annotation.Nullable java.lang.String mAppMetadataFilePath\nprivate int mTargetSdkVersion\nprivate @android.annotation.Nullable byte[] mRestrictUpdateHash\nprivate final @android.annotation.NonNull com.android.server.utils.SnapshotCache<com.android.server.pm.PackageSetting> mSnapshot\nprivate void setBoolean(int,boolean)\nprivate boolean getBoolean(int)\nprivate com.android.server.utils.SnapshotCache<com.android.server.pm.PackageSetting> makeCache()\npublic com.android.server.pm.PackageSetting snapshot()\npublic void dumpDebug(android.util.proto.ProtoOutputStream,long,java.util.List<android.content.pm.UserInfo>,com.android.server.pm.permission.LegacyPermissionDataProvider)\npublic com.android.server.pm.PackageSetting setAppId(int)\npublic com.android.server.pm.PackageSetting setCpuAbiOverride(java.lang.String)\npublic com.android.server.pm.PackageSetting setFirstInstallTimeFromReplaced(com.android.server.pm.pkg.PackageStateInternal,int[])\npublic com.android.server.pm.PackageSetting setFirstInstallTime(long,int)\npublic com.android.server.pm.PackageSetting setForceQueryableOverride(boolean)\npublic com.android.server.pm.PackageSetting setInstallerPackage(java.lang.String,int)\npublic com.android.server.pm.PackageSetting setUpdateOwnerPackage(java.lang.String)\npublic com.android.server.pm.PackageSetting setInstallSource(com.android.server.pm.InstallSource)\n com.android.server.pm.PackageSetting removeInstallerPackage(java.lang.String)\npublic com.android.server.pm.PackageSetting setIsOrphaned(boolean)\npublic com.android.server.pm.PackageSetting setKeySetData(com.android.server.pm.PackageKeySetData)\npublic com.android.server.pm.PackageSetting setLastModifiedTime(long)\npublic com.android.server.pm.PackageSetting setLastUpdateTime(long)\npublic com.android.server.pm.PackageSetting setLongVersionCode(long)\npublic boolean setMimeGroup(java.lang.String,android.util.ArraySet<java.lang.String>)\npublic com.android.server.pm.PackageSetting setPkg(com.android.server.pm.pkg.AndroidPackage)\npublic com.android.server.pm.PackageSetting setPkgStateLibraryFiles(java.util.Collection<java.lang.String>)\npublic com.android.server.pm.PackageSetting setPrimaryCpuAbi(java.lang.String)\npublic com.android.server.pm.PackageSetting setSecondaryCpuAbi(java.lang.String)\npublic com.android.server.pm.PackageSetting setSignatures(com.android.server.pm.PackageSignatures)\npublic com.android.server.pm.PackageSetting setVolumeUuid(java.lang.String)\npublic com.android.server.pm.PackageSetting setDefaultToDeviceProtectedStorage(boolean)\npublic @java.lang.Override boolean isExternalStorage()\npublic com.android.server.pm.PackageSetting setUpdateAvailable(boolean)\npublic void setSharedUserAppId(int)\npublic com.android.server.pm.PackageSetting setIsPersistent(boolean)\npublic com.android.server.pm.PackageSetting setTargetSdkVersion(int)\npublic com.android.server.pm.PackageSetting setRestrictUpdateHash(byte[])\npublic @java.lang.Override int getSharedUserAppId()\npublic @java.lang.Override boolean hasSharedUser()\npublic @java.lang.Override java.lang.String toString()\nprotected void copyMimeGroups(java.util.Map<java.lang.String,java.util.Set<java.lang.String>>)\npublic void updateFrom(com.android.server.pm.PackageSetting)\n com.android.server.pm.PackageSetting updateMimeGroups(java.util.Set<java.lang.String>)\npublic @java.lang.Deprecated @java.lang.Override com.android.server.pm.permission.LegacyPermissionState getLegacyPermissionState()\npublic com.android.server.pm.PackageSetting setInstallPermissionsFixed(boolean)\npublic boolean isPrivileged()\npublic boolean isOem()\npublic boolean isVendor()\npublic boolean isProduct()\npublic @java.lang.Override boolean isRequiredForSystemUser()\npublic boolean isSystemExt()\npublic boolean isOdm()\npublic boolean isSystem()\npublic boolean isRequestLegacyExternalStorage()\npublic boolean isUserDataFragile()\npublic android.content.pm.SigningDetails getSigningDetails()\npublic com.android.server.pm.PackageSetting setSigningDetails(android.content.pm.SigningDetails)\npublic void copyPackageSetting(com.android.server.pm.PackageSetting,boolean)\n @com.android.internal.annotations.VisibleForTesting com.android.server.pm.pkg.PackageUserStateImpl modifyUserState(int)\npublic com.android.server.pm.pkg.PackageUserStateImpl getOrCreateUserState(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateInternal readUserState(int)\n void setEnabled(int,int,java.lang.String)\n int getEnabled(int)\n void setInstalled(boolean,int)\n boolean getInstalled(int)\n int getInstallReason(int)\n void setInstallReason(int,int)\n int getUninstallReason(int)\n void setUninstallReason(int,int)\n @android.annotation.NonNull android.content.pm.overlay.OverlayPaths getOverlayPaths(int)\n boolean setOverlayPathsForLibrary(java.lang.String,android.content.pm.overlay.OverlayPaths,int)\n boolean isInstalledOrHasDataOnAnyOtherUser(int[],int)\n int[] queryInstalledUsers(int[],boolean)\n int[] queryUsersInstalledOrHasData(int[])\n long getCeDataInode(int)\n long getDeDataInode(int)\n void setCeDataInode(long,int)\n void setDeDataInode(long,int)\n boolean getStopped(int)\n void setStopped(boolean,int)\n boolean getNotLaunched(int)\n void setNotLaunched(boolean,int)\n boolean getHidden(int)\n void setHidden(boolean,int)\n int getDistractionFlags(int)\n void setDistractionFlags(int,int)\npublic boolean getInstantApp(int)\n void setInstantApp(boolean,int)\n boolean getVirtualPreload(int)\n void setVirtualPreload(boolean,int)\n void setUserState(int,long,long,int,boolean,boolean,boolean,boolean,int,android.util.ArrayMap<java.lang.String,com.android.server.pm.pkg.SuspendParams>,boolean,boolean,java.lang.String,android.util.ArraySet<java.lang.String>,android.util.ArraySet<java.lang.String>,int,int,java.lang.String,java.lang.String,long,int,com.android.server.pm.pkg.ArchiveState)\n void setUserState(int,com.android.server.pm.pkg.PackageUserStateInternal)\n com.android.server.utils.WatchedArraySet<java.lang.String> getEnabledComponents(int)\n com.android.server.utils.WatchedArraySet<java.lang.String> getDisabledComponents(int)\n void setEnabledComponents(com.android.server.utils.WatchedArraySet<java.lang.String>,int)\n void setDisabledComponents(com.android.server.utils.WatchedArraySet<java.lang.String>,int)\n void setEnabledComponentsCopy(com.android.server.utils.WatchedArraySet<java.lang.String>,int)\n void setDisabledComponentsCopy(com.android.server.utils.WatchedArraySet<java.lang.String>,int)\n com.android.server.pm.pkg.PackageUserStateImpl modifyUserStateComponents(int,boolean,boolean)\n void addDisabledComponent(java.lang.String,int)\n void addEnabledComponent(java.lang.String,int)\n boolean enableComponentLPw(java.lang.String,int)\n boolean disableComponentLPw(java.lang.String,int)\n boolean restoreComponentLPw(java.lang.String,int)\n int getCurrentEnabledStateLPr(java.lang.String,int)\n void removeUser(int)\npublic int[] getNotInstalledUserIds()\n void writePackageUserPermissionsProto(android.util.proto.ProtoOutputStream,long,java.util.List<android.content.pm.UserInfo>,com.android.server.pm.permission.LegacyPermissionDataProvider)\nprotected void writeUsersInfoToProto(android.util.proto.ProtoOutputStream,long)\nprivate static void writeArchiveState(android.util.proto.ProtoOutputStream,com.android.server.pm.pkg.ArchiveState)\n com.android.server.pm.PackageSetting setPath(java.io.File)\npublic @com.android.internal.annotations.VisibleForTesting boolean overrideNonLocalizedLabelAndIcon(android.content.ComponentName,java.lang.String,java.lang.Integer,int)\npublic void resetOverrideComponentLabelIcon(int)\npublic @android.annotation.Nullable java.lang.String getSplashScreenTheme(int)\npublic boolean isIncremental()\npublic boolean isLoading()\npublic com.android.server.pm.PackageSetting setLoadingProgress(float)\npublic com.android.server.pm.PackageSetting setLoadingCompletedTime(long)\npublic com.android.server.pm.PackageSetting setAppMetadataFilePath(java.lang.String)\npublic @android.annotation.NonNull @java.lang.Override long getVersionCode()\npublic @android.annotation.Nullable @java.lang.Override java.util.Map<java.lang.String,java.util.Set<java.lang.String>> getMimeGroups()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String getPackageName()\npublic @android.annotation.Nullable @java.lang.Override com.android.server.pm.pkg.AndroidPackage getAndroidPackage()\npublic @android.annotation.NonNull android.content.pm.SigningInfo getSigningInfo()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String[] getUsesSdkLibraries()\npublic @android.annotation.NonNull @java.lang.Override long[] getUsesSdkLibrariesVersionsMajor()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String[] getUsesStaticLibraries()\npublic @android.annotation.NonNull @java.lang.Override long[] getUsesStaticLibrariesVersions()\npublic @android.annotation.NonNull @java.lang.Override java.util.List<com.android.server.pm.pkg.SharedLibrary> getSharedLibraryDependencies()\npublic @android.annotation.NonNull com.android.server.pm.PackageSetting addUsesLibraryInfo(android.content.pm.SharedLibraryInfo)\npublic @android.annotation.NonNull @java.lang.Override java.util.List<java.lang.String> getUsesLibraryFiles()\npublic @android.annotation.NonNull com.android.server.pm.PackageSetting addUsesLibraryFile(java.lang.String)\npublic @java.lang.Override boolean isHiddenUntilInstalled()\npublic @android.annotation.NonNull @java.lang.Override long[] getLastPackageUsageTime()\npublic @java.lang.Override boolean isUpdatedSystemApp()\npublic @java.lang.Override boolean isApkInUpdatedApex()\npublic @android.annotation.Nullable @java.lang.Override java.lang.String getApexModuleName()\npublic com.android.server.pm.PackageSetting setDomainSetId(java.util.UUID)\npublic com.android.server.pm.PackageSetting setCategoryOverride(int)\npublic com.android.server.pm.PackageSetting setLegacyNativeLibraryPath(java.lang.String)\npublic com.android.server.pm.PackageSetting setMimeGroups(java.util.Map<java.lang.String,java.util.Set<java.lang.String>>)\npublic com.android.server.pm.PackageSetting setOldCodePaths(java.util.Set<java.lang.String>)\npublic com.android.server.pm.PackageSetting setUsesSdkLibraries(java.lang.String[])\npublic com.android.server.pm.PackageSetting setUsesSdkLibrariesVersionsMajor(long[])\npublic com.android.server.pm.PackageSetting setUsesStaticLibraries(java.lang.String[])\npublic com.android.server.pm.PackageSetting setUsesStaticLibrariesVersions(long[])\npublic com.android.server.pm.PackageSetting setApexModuleName(java.lang.String)\npublic @android.annotation.NonNull @java.lang.Override com.android.server.pm.pkg.PackageStateUnserialized getTransientState()\npublic @android.annotation.NonNull android.util.SparseArray<? extends PackageUserStateInternal> getUserStates()\npublic com.android.server.pm.PackageSetting addMimeTypes(java.lang.String,java.util.Set<java.lang.String>)\npublic @android.annotation.NonNull @java.lang.Override com.android.server.pm.pkg.PackageUserState getStateForUser(android.os.UserHandle)\npublic @android.annotation.Nullable java.lang.String getPrimaryCpuAbi()\npublic @android.annotation.Nullable java.lang.String getSecondaryCpuAbi()\npublic @android.annotation.Nullable @java.lang.Override java.lang.String getSeInfo()\npublic @android.annotation.Nullable java.lang.String getPrimaryCpuAbiLegacy()\npublic @android.annotation.Nullable java.lang.String getSecondaryCpuAbiLegacy()\npublic @android.content.pm.ApplicationInfo.HiddenApiEnforcementPolicy @java.lang.Override int getHiddenApiEnforcementPolicy()\npublic @java.lang.Override boolean isApex()\npublic @java.lang.Override boolean isForceQueryableOverride()\npublic @java.lang.Override boolean isUpdateAvailable()\npublic @java.lang.Override boolean isInstallPermissionsFixed()\npublic @java.lang.Override boolean isDefaultToDeviceProtectedStorage()\npublic @java.lang.Override boolean isPersistent()\nclass PackageSetting extends com.android.server.pm.SettingBase implements [com.android.server.pm.pkg.PackageStateInternal]\nprivate static final int INSTALL_PERMISSION_FIXED\nprivate static final int DEFAULT_TO_DEVICE_PROTECTED_STORAGE\nprivate static final int UPDATE_AVAILABLE\nprivate static final int FORCE_QUERYABLE_OVERRIDE\nprivate static final int PERSISTENT\nclass Booleans extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genGetters=true, genConstructor=false, genSetters=false, genBuilder=false)")
@Deprecated
private void __metadata() {}
diff --git a/services/core/java/com/android/server/pm/ResolveIntentHelper.java b/services/core/java/com/android/server/pm/ResolveIntentHelper.java
index da14397..203e1de 100644
--- a/services/core/java/com/android/server/pm/ResolveIntentHelper.java
+++ b/services/core/java/com/android/server/pm/ResolveIntentHelper.java
@@ -517,12 +517,6 @@
if (!mUserManager.exists(userId)) return Collections.emptyList();
final int callingUid = Binder.getCallingUid();
- // Only if the service query is coming from the system process,
- // it should be allowed to match quarantined components
- if (callingUid != Process.SYSTEM_UID) {
- flags |= PackageManager.FILTER_OUT_QUARANTINED_COMPONENTS;
- }
-
final String instantAppPkgName = computer.getInstantAppPackageName(callingUid);
flags = computer.updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/,
false /* isImplicitImageCaptureIntentAndNotSetByDpc */);
diff --git a/services/core/java/com/android/server/pm/ScanPackageUtils.java b/services/core/java/com/android/server/pm/ScanPackageUtils.java
index 0cac790..8d8acfd4 100644
--- a/services/core/java/com/android/server/pm/ScanPackageUtils.java
+++ b/services/core/java/com/android/server/pm/ScanPackageUtils.java
@@ -220,7 +220,8 @@
UserManagerService.getInstance(), usesSdkLibraries,
parsedPackage.getUsesSdkLibrariesVersionsMajor(), usesStaticLibraries,
parsedPackage.getUsesStaticLibrariesVersions(), parsedPackage.getMimeGroups(),
- newDomainSetId);
+ newDomainSetId, parsedPackage.isPersistent(),
+ parsedPackage.getTargetSdkVersion(), parsedPackage.getRestrictUpdateHash());
} else {
// make a deep copy to avoid modifying any existing system state.
pkgSetting = new PackageSetting(pkgSetting);
@@ -240,7 +241,8 @@
UserManagerService.getInstance(),
usesSdkLibraries, parsedPackage.getUsesSdkLibrariesVersionsMajor(),
usesStaticLibraries, parsedPackage.getUsesStaticLibrariesVersions(),
- parsedPackage.getMimeGroups(), newDomainSetId);
+ parsedPackage.getMimeGroups(), newDomainSetId, parsedPackage.isPersistent(),
+ parsedPackage.getTargetSdkVersion(), parsedPackage.getRestrictUpdateHash());
}
if (createNewPackage && originalPkgSetting != null) {
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 6e3b538..397a841 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -1060,7 +1060,8 @@
boolean virtualPreload, boolean isStoppedSystemApp, UserManagerService userManager,
String[] usesSdkLibraries, long[] usesSdkLibrariesVersions,
String[] usesStaticLibraries, long[] usesStaticLibrariesVersions,
- Set<String> mimeGroupNames, @NonNull UUID domainSetId) {
+ Set<String> mimeGroupNames, @NonNull UUID domainSetId, boolean isPersistent,
+ int targetSdkVersion, byte[] restrictUpdatedHash) {
final PackageSetting pkgSetting;
if (originalPkg != null) {
if (PackageManagerService.DEBUG_UPGRADE) Log.v(PackageManagerService.TAG, "Package "
@@ -1080,7 +1081,10 @@
.setUsesStaticLibrariesVersions(usesStaticLibrariesVersions)
// Update new package state.
.setLastModifiedTime(codePath.lastModified())
- .setDomainSetId(domainSetId);
+ .setDomainSetId(domainSetId)
+ .setIsPersistent(isPersistent)
+ .setTargetSdkVersion(targetSdkVersion)
+ .setRestrictUpdateHash(restrictUpdatedHash);
pkgSetting.setFlags(pkgFlags)
.setPrivateFlags(pkgPrivateFlags);
} else {
@@ -1092,7 +1096,10 @@
null /*cpuAbiOverrideString*/, versionCode, pkgFlags, pkgPrivateFlags,
0 /*sharedUserAppId*/, usesSdkLibraries, usesSdkLibrariesVersions,
usesStaticLibraries, usesStaticLibrariesVersions,
- createMimeGroups(mimeGroupNames), domainSetId);
+ createMimeGroups(mimeGroupNames), domainSetId)
+ .setIsPersistent(isPersistent)
+ .setTargetSdkVersion(targetSdkVersion)
+ .setRestrictUpdateHash(restrictUpdatedHash);
pkgSetting.setLastModifiedTime(codePath.lastModified());
if (sharedUser != null) {
pkgSetting.setSharedUserAppId(sharedUser.mAppId);
@@ -1206,7 +1213,8 @@
int pkgPrivateFlags, @NonNull UserManagerService userManager,
@Nullable String[] usesSdkLibraries, @Nullable long[] usesSdkLibrariesVersions,
@Nullable String[] usesStaticLibraries, @Nullable long[] usesStaticLibrariesVersions,
- @Nullable Set<String> mimeGroupNames, @NonNull UUID domainSetId)
+ @Nullable Set<String> mimeGroupNames, @NonNull UUID domainSetId, boolean isPersistent,
+ int targetSdkVersion, byte[] restrictUpdatedHash)
throws PackageManagerException {
final String pkgName = pkgSetting.getPackageName();
if (sharedUser != null) {
@@ -1258,7 +1266,10 @@
pkgSetting.setPrimaryCpuAbi(primaryCpuAbi)
.setSecondaryCpuAbi(secondaryCpuAbi)
.updateMimeGroups(mimeGroupNames)
- .setDomainSetId(domainSetId);
+ .setDomainSetId(domainSetId)
+ .setIsPersistent(isPersistent)
+ .setTargetSdkVersion(targetSdkVersion)
+ .setRestrictUpdateHash(restrictUpdatedHash);
// Update SDK library dependencies if needed.
if (usesSdkLibraries != null && usesSdkLibrariesVersions != null
&& usesSdkLibraries.length == usesSdkLibrariesVersions.length) {
@@ -6400,17 +6411,26 @@
}
boolean clearPersistentPreferredActivity(IntentFilter filter, int userId) {
+ ArrayList<PersistentPreferredActivity> removed = null;
PersistentPreferredIntentResolver ppir = mPersistentPreferredActivities.get(userId);
Iterator<PersistentPreferredActivity> it = ppir.filterIterator();
boolean changed = false;
while (it.hasNext()) {
PersistentPreferredActivity ppa = it.next();
if (IntentFilter.filterEquals(ppa.getIntentFilter(), filter)) {
- ppir.removeFilter(ppa);
- changed = true;
- break;
+ if (removed == null) {
+ removed = new ArrayList<>();
+ }
+ removed.add(ppa);
}
}
+ if (removed != null) {
+ for (int i = 0; i < removed.size(); i++) {
+ PersistentPreferredActivity ppa = removed.get(i);
+ ppir.removeFilter(ppa);
+ }
+ changed = true;
+ }
if (changed) {
onChanged();
}
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 68a8e40..7331bc1 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;
@@ -1335,18 +1335,6 @@
&& user.profileGroupId == profile.profileGroupId);
}
- private Intent buildProfileAvailabilityIntent(UserInfo profile, boolean enableQuietMode,
- boolean useManagedActions) {
- Intent intent = new Intent();
- intent.setAction(getAvailabilityIntentAction(enableQuietMode, useManagedActions));
- intent.putExtra(Intent.EXTRA_QUIET_MODE, enableQuietMode);
- intent.putExtra(Intent.EXTRA_USER, profile.getUserHandle());
- intent.putExtra(Intent.EXTRA_USER_HANDLE, profile.getUserHandle().getIdentifier());
- intent.addFlags(
- Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND);
- return intent;
- }
-
private String getAvailabilityIntentAction(boolean enableQuietMode, boolean useManagedActions) {
return useManagedActions ?
enableQuietMode ?
@@ -1359,12 +1347,20 @@
private void broadcastProfileAvailabilityChanges(UserInfo profileInfo,
UserHandle parentHandle, boolean enableQuietMode, boolean useManagedActions) {
- Intent availabilityIntent = buildProfileAvailabilityIntent(profileInfo, enableQuietMode,
- useManagedActions);
+ Intent availabilityIntent = new Intent();
+ availabilityIntent.setAction(
+ getAvailabilityIntentAction(enableQuietMode, useManagedActions));
+ availabilityIntent.putExtra(Intent.EXTRA_QUIET_MODE, enableQuietMode);
+ availabilityIntent.putExtra(Intent.EXTRA_USER, profileInfo.getUserHandle());
+ availabilityIntent.putExtra(Intent.EXTRA_USER_HANDLE,
+ profileInfo.getUserHandle().getIdentifier());
if (profileInfo.isManagedProfile()) {
getDevicePolicyManagerInternal().broadcastIntentToManifestReceivers(
availabilityIntent, parentHandle, /* requiresPermission= */ true);
}
+ availabilityIntent.addFlags(
+ Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND);
+
// TODO(b/302708423): Restrict the apps that can receive these intents in case of a private
// profile.
final Bundle options = new BroadcastOptions()
@@ -1559,7 +1555,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);
}
@@ -3785,6 +3781,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 {
@@ -3834,6 +3832,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) {
@@ -3852,6 +3851,7 @@
updateUserIds();
upgradeIfNecessaryLP();
+ updateUsersWithFeatureFlags(guestRestrictionsArePresentOnUserListXml);
} catch (Exception e) {
// Remove corrupted file and retry.
file.failRead(fin, e);
@@ -3877,6 +3877,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()}.
*/
@@ -4393,9 +4411,24 @@
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),
@@ -4471,12 +4504,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()];
@@ -4620,6 +4656,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) {
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..61e96ca 100644
--- a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
+++ b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
@@ -379,7 +379,7 @@
ai.privateFlags |= flag(state.isInstantApp(), ApplicationInfo.PRIVATE_FLAG_INSTANT)
| flag(state.isVirtualPreload(), ApplicationInfo.PRIVATE_FLAG_VIRTUAL_PRELOAD)
| flag(state.isHidden(), ApplicationInfo.PRIVATE_FLAG_HIDDEN);
- if ((flags & PackageManager.FILTER_OUT_QUARANTINED_COMPONENTS) != 0
+ if ((flags & PackageManager.MATCH_QUARANTINED_COMPONENTS) == 0
&& state.isQuarantined()) {
ai.enabled = false;
} else if (state.getEnabledState() == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) {
@@ -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/PackageState.java b/services/core/java/com/android/server/pm/pkg/PackageState.java
index 3f347e4..e7137bb 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageState.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageState.java
@@ -434,4 +434,26 @@
*/
@Nullable
String getApexModuleName();
+
+ /**
+ * @see ApplicationInfo#FLAG_PERSISTENT
+ * @see R.styleable#AndroidManifestApplication_persistent
+ * @hide
+ */
+ boolean isPersistent();
+
+ /**
+ * @see ApplicationInfo#targetSdkVersion
+ * @see R.styleable#AndroidManifestUsesSdk_targetSdkVersion
+ * @hide
+ */
+ int getTargetSdkVersion();
+
+ /**
+ * @see R.styleable#AndroidManifestRestrictUpdate
+ * @hide
+ */
+ @Immutable.Ignore
+ @Nullable
+ byte[] getRestrictUpdateHash();
}
diff --git a/services/core/java/com/android/server/pm/pkg/PackageUserStateUtils.java b/services/core/java/com/android/server/pm/pkg/PackageUserStateUtils.java
index 7b07e5b..cd3583b 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageUserStateUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageUserStateUtils.java
@@ -16,9 +16,9 @@
package com.android.server.pm.pkg;
-import static android.content.pm.PackageManager.FILTER_OUT_QUARANTINED_COMPONENTS;
import static android.content.pm.PackageManager.MATCH_DISABLED_COMPONENTS;
import static android.content.pm.PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS;
+import static android.content.pm.PackageManager.MATCH_QUARANTINED_COMPONENTS;
import android.annotation.NonNull;
import android.content.pm.ComponentInfo;
@@ -147,7 +147,7 @@
return true;
}
- if ((flags & FILTER_OUT_QUARANTINED_COMPONENTS) != 0 && state.isQuarantined()) {
+ if ((flags & MATCH_QUARANTINED_COMPONENTS) == 0 && state.isQuarantined()) {
return false;
}
diff --git a/services/core/java/com/android/server/pm/pkg/SuspendParams.java b/services/core/java/com/android/server/pm/pkg/SuspendParams.java
index 86391c9..153238fa 100644
--- a/services/core/java/com/android/server/pm/pkg/SuspendParams.java
+++ b/services/core/java/com/android/server/pm/pkg/SuspendParams.java
@@ -17,7 +17,6 @@
package com.android.server.pm.pkg;
import android.annotation.Nullable;
-import android.content.pm.Flags;
import android.content.pm.SuspendDialogInfo;
import android.os.BaseBundle;
import android.os.PersistableBundle;
@@ -142,8 +141,7 @@
PersistableBundle readAppExtras = null;
PersistableBundle readLauncherExtras = null;
- final boolean quarantined = in.getAttributeBoolean(null, ATTR_QUARANTINED, false)
- && Flags.quarantinedEnabled();
+ final boolean quarantined = in.getAttributeBoolean(null, ATTR_QUARANTINED, false);
final int currentDepth = in.getDepth();
int type;
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/power/stats/AggregatedPowerStats.java b/services/core/java/com/android/server/power/stats/AggregatedPowerStats.java
index 6cc9d0a..bc90f5c 100644
--- a/services/core/java/com/android/server/power/stats/AggregatedPowerStats.java
+++ b/services/core/java/com/android/server/power/stats/AggregatedPowerStats.java
@@ -18,15 +18,26 @@
import android.annotation.CurrentTimeMillisLong;
import android.annotation.DurationMillisLong;
+import android.annotation.NonNull;
+import android.os.BatteryConsumer;
import android.os.UserHandle;
import android.text.format.DateFormat;
import android.util.IndentingPrintWriter;
+import android.util.Slog;
+import android.util.TimeUtils;
import com.android.internal.os.PowerStats;
+import com.android.modules.utils.TypedXmlPullParser;
+import com.android.modules.utils.TypedXmlSerializer;
-import java.io.PrintWriter;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
+import java.util.List;
import java.util.Set;
/**
@@ -34,25 +45,75 @@
* etc) covering a specific period of power usage history.
*/
class AggregatedPowerStats {
+ private static final String TAG = "AggregatedPowerStats";
+ private static final int MAX_CLOCK_UPDATES = 100;
+ private static final String XML_TAG_AGGREGATED_POWER_STATS = "agg-power-stats";
+
private final PowerComponentAggregatedPowerStats[] mPowerComponentStats;
- @CurrentTimeMillisLong
- private long mStartTime;
+ static class ClockUpdate {
+ public long monotonicTime;
+ @CurrentTimeMillisLong public long currentTime;
+ }
+
+ private final List<ClockUpdate> mClockUpdates = new ArrayList<>();
@DurationMillisLong
private long mDurationMs;
- AggregatedPowerStats(PowerComponentAggregatedPowerStats... powerComponentAggregatedPowerStats) {
- this.mPowerComponentStats = powerComponentAggregatedPowerStats;
+ AggregatedPowerStats(AggregatedPowerStatsConfig aggregatedPowerStatsConfig) {
+ List<AggregatedPowerStatsConfig.PowerComponent> configs =
+ aggregatedPowerStatsConfig.getPowerComponentsAggregatedStatsConfigs();
+ mPowerComponentStats = new PowerComponentAggregatedPowerStats[configs.size()];
+ for (int i = 0; i < configs.size(); i++) {
+ mPowerComponentStats[i] = createPowerComponentAggregatedPowerStats(configs.get(i));
+ }
}
- void setStartTime(@CurrentTimeMillisLong long startTime) {
- mStartTime = startTime;
+ private PowerComponentAggregatedPowerStats createPowerComponentAggregatedPowerStats(
+ AggregatedPowerStatsConfig.PowerComponent config) {
+ switch (config.getPowerComponentId()) {
+ case BatteryConsumer.POWER_COMPONENT_CPU:
+ return new CpuAggregatedPowerStats(config);
+ default:
+ return new PowerComponentAggregatedPowerStats(config);
+ }
}
- @CurrentTimeMillisLong
- public long getStartTime() {
- return mStartTime;
+ /**
+ * Records a mapping of monotonic time to wall-clock time. Since wall-clock time can change,
+ * there may be multiple clock updates in one set of aggregated stats.
+ *
+ * @param monotonicTime monotonic time in milliseconds, see
+ * {@link com.android.internal.os.MonotonicClock}
+ * @param currentTime current time in milliseconds, see {@link System#currentTimeMillis()}
+ */
+ void addClockUpdate(long monotonicTime, @CurrentTimeMillisLong long currentTime) {
+ ClockUpdate clockUpdate = new ClockUpdate();
+ clockUpdate.monotonicTime = monotonicTime;
+ clockUpdate.currentTime = currentTime;
+ if (mClockUpdates.size() < MAX_CLOCK_UPDATES) {
+ mClockUpdates.add(clockUpdate);
+ } else {
+ Slog.i(TAG, "Too many clock updates. Replacing the previous update with "
+ + DateFormat.format("yyyy-MM-dd-HH-mm-ss", currentTime));
+ mClockUpdates.set(mClockUpdates.size() - 1, clockUpdate);
+ }
+ }
+
+ /**
+ * Start time according to {@link com.android.internal.os.MonotonicClock}
+ */
+ long getStartTime() {
+ if (mClockUpdates.isEmpty()) {
+ return 0;
+ } else {
+ return mClockUpdates.get(0).monotonicTime;
+ }
+ }
+
+ List<ClockUpdate> getClockUpdates() {
+ return mClockUpdates;
}
void setDuration(long durationMs) {
@@ -73,13 +134,14 @@
return null;
}
- void setDeviceState(@PowerStatsAggregator.TrackedState int stateId, int state, long time) {
+ void setDeviceState(@AggregatedPowerStatsConfig.TrackedState int stateId, int state,
+ long time) {
for (PowerComponentAggregatedPowerStats stats : mPowerComponentStats) {
stats.setState(stateId, state, time);
}
}
- void setUidState(int uid, @PowerStatsAggregator.TrackedState int stateId, int state,
+ void setUidState(int uid, @AggregatedPowerStatsConfig.TrackedState int stateId, int state,
long time) {
for (PowerComponentAggregatedPowerStats stats : mPowerComponentStats) {
stats.setUidState(uid, stateId, state, time);
@@ -106,20 +168,90 @@
}
void reset() {
- mStartTime = 0;
+ mClockUpdates.clear();
mDurationMs = 0;
for (PowerComponentAggregatedPowerStats stats : mPowerComponentStats) {
stats.reset();
}
}
- void dump(PrintWriter pw) {
- IndentingPrintWriter ipw = new IndentingPrintWriter(pw);
- ipw.print("Start time: ");
- ipw.print(DateFormat.format("yyyy-MM-dd-HH-mm-ss", mStartTime));
- ipw.print(" duration: ");
- ipw.print(mDurationMs);
- ipw.println();
+ public void writeXml(TypedXmlSerializer serializer) throws IOException {
+ serializer.startTag(null, XML_TAG_AGGREGATED_POWER_STATS);
+ for (PowerComponentAggregatedPowerStats stats : mPowerComponentStats) {
+ stats.writeXml(serializer);
+ }
+ serializer.endTag(null, XML_TAG_AGGREGATED_POWER_STATS);
+ serializer.flush();
+ }
+
+ @NonNull
+ public static AggregatedPowerStats createFromXml(
+ TypedXmlPullParser parser, AggregatedPowerStatsConfig aggregatedPowerStatsConfig)
+ throws XmlPullParserException, IOException {
+ AggregatedPowerStats stats = new AggregatedPowerStats(aggregatedPowerStatsConfig);
+ boolean inElement = false;
+ boolean skipToEnd = false;
+ int eventType = parser.getEventType();
+ while (eventType != XmlPullParser.END_DOCUMENT
+ && !(eventType == XmlPullParser.END_TAG
+ && parser.getName().equals(XML_TAG_AGGREGATED_POWER_STATS))) {
+ if (!skipToEnd && eventType == XmlPullParser.START_TAG) {
+ switch (parser.getName()) {
+ case XML_TAG_AGGREGATED_POWER_STATS:
+ inElement = true;
+ break;
+ case PowerComponentAggregatedPowerStats.XML_TAG_POWER_COMPONENT:
+ if (!inElement) {
+ break;
+ }
+
+ int powerComponentId = parser.getAttributeInt(null,
+ PowerComponentAggregatedPowerStats.XML_ATTR_ID);
+ for (PowerComponentAggregatedPowerStats powerComponent :
+ stats.mPowerComponentStats) {
+ if (powerComponent.powerComponentId == powerComponentId) {
+ if (!powerComponent.readFromXml(parser)) {
+ skipToEnd = true;
+ }
+ break;
+ }
+ }
+ break;
+ }
+ }
+ eventType = parser.next();
+ }
+ return stats;
+ }
+
+ void dump(IndentingPrintWriter ipw) {
+ StringBuilder sb = new StringBuilder();
+ long baseTime = 0;
+ for (int i = 0; i < mClockUpdates.size(); i++) {
+ ClockUpdate clockUpdate = mClockUpdates.get(i);
+ sb.setLength(0);
+ if (i == 0) {
+ baseTime = clockUpdate.monotonicTime;
+ sb.append("Start time: ")
+ .append(DateFormat.format("yyyy-MM-dd-HH-mm-ss", clockUpdate.currentTime))
+ .append(" (")
+ .append(baseTime)
+ .append(") duration: ")
+ .append(mDurationMs);
+ ipw.println(sb);
+ } else {
+ sb.setLength(0);
+ sb.append("Clock update: ");
+ TimeUtils.formatDuration(
+ clockUpdate.monotonicTime - baseTime, sb,
+ TimeUtils.HUNDRED_DAY_FIELD_LEN + 3);
+ sb.append(" ").append(
+ DateFormat.format("yyyy-MM-dd-HH-mm-ss", clockUpdate.currentTime));
+ ipw.increaseIndent();
+ ipw.println(sb);
+ ipw.decreaseIndent();
+ }
+ }
ipw.println("Device");
ipw.increaseIndent();
diff --git a/services/core/java/com/android/server/power/stats/AggregatedPowerStatsConfig.java b/services/core/java/com/android/server/power/stats/AggregatedPowerStatsConfig.java
new file mode 100644
index 0000000..477c228
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/AggregatedPowerStatsConfig.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.power.stats;
+
+import android.annotation.IntDef;
+import android.os.BatteryConsumer;
+
+import com.android.internal.os.MultiStateStats;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Configuration that controls how power stats are aggregated. It determines which state changes
+ * are to be considered as essential dimensions ("tracked states") for each power component (CPU,
+ * WiFi, etc). Also, it determines which states are tracked globally and which ones on a per-UID
+ * basis.
+ */
+public class AggregatedPowerStatsConfig {
+ public static final int STATE_POWER = 0;
+ public static final int STATE_SCREEN = 1;
+ public static final int STATE_PROCESS_STATE = 2;
+
+ @IntDef({
+ STATE_POWER,
+ STATE_SCREEN,
+ STATE_PROCESS_STATE,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface TrackedState {
+ }
+
+ static final String STATE_NAME_POWER = "pwr";
+ static final int POWER_STATE_BATTERY = 0;
+ static final int POWER_STATE_OTHER = 1; // Plugged in, or on wireless charger, etc.
+ static final String[] STATE_LABELS_POWER = {"pwr-battery", "pwr-other"};
+
+ static final String STATE_NAME_SCREEN = "scr";
+ static final int SCREEN_STATE_ON = 0;
+ static final int SCREEN_STATE_OTHER = 1; // Off, doze etc
+ static final String[] STATE_LABELS_SCREEN = {"scr-on", "scr-other"};
+
+ static final String STATE_NAME_PROCESS_STATE = "ps";
+ static final String[] STATE_LABELS_PROCESS_STATE;
+
+ static {
+ String[] procStateLabels = new String[BatteryConsumer.PROCESS_STATE_COUNT];
+ for (int i = 0; i < BatteryConsumer.PROCESS_STATE_COUNT; i++) {
+ procStateLabels[i] = BatteryConsumer.processStateToString(i);
+ }
+ STATE_LABELS_PROCESS_STATE = procStateLabels;
+ }
+
+ /**
+ * Configuration for a give power component (CPU, WiFi, etc)
+ */
+ public static class PowerComponent {
+ private final int mPowerComponentId;
+ private @TrackedState int[] mTrackedDeviceStates;
+ private @TrackedState int[] mTrackedUidStates;
+
+ PowerComponent(int powerComponentId) {
+ this.mPowerComponentId = powerComponentId;
+ }
+
+ /**
+ * Configures which states should be tracked as separate dimensions for the entire device.
+ */
+ public PowerComponent trackDeviceStates(@TrackedState int... states) {
+ mTrackedDeviceStates = states;
+ return this;
+ }
+
+ /**
+ * Configures which states should be tracked as separate dimensions on a per-UID basis.
+ */
+ public PowerComponent trackUidStates(@TrackedState int... states) {
+ mTrackedUidStates = states;
+ return this;
+ }
+
+ public int getPowerComponentId() {
+ return mPowerComponentId;
+ }
+
+ public MultiStateStats.States[] getDeviceStateConfig() {
+ return new MultiStateStats.States[]{
+ new MultiStateStats.States(STATE_NAME_POWER,
+ isTracked(mTrackedDeviceStates, STATE_POWER),
+ STATE_LABELS_POWER),
+ new MultiStateStats.States(STATE_NAME_SCREEN,
+ isTracked(mTrackedDeviceStates, STATE_SCREEN),
+ STATE_LABELS_SCREEN),
+ };
+ }
+
+ public MultiStateStats.States[] getUidStateConfig() {
+ return new MultiStateStats.States[]{
+ new MultiStateStats.States(STATE_NAME_POWER,
+ isTracked(mTrackedUidStates, STATE_POWER),
+ AggregatedPowerStatsConfig.STATE_LABELS_POWER),
+ new MultiStateStats.States(STATE_NAME_SCREEN,
+ isTracked(mTrackedUidStates, STATE_SCREEN),
+ AggregatedPowerStatsConfig.STATE_LABELS_SCREEN),
+ new MultiStateStats.States(STATE_NAME_PROCESS_STATE,
+ isTracked(mTrackedUidStates, STATE_PROCESS_STATE),
+ AggregatedPowerStatsConfig.STATE_LABELS_PROCESS_STATE),
+ };
+ }
+
+ private boolean isTracked(int[] trackedStates, int state) {
+ if (trackedStates == null) {
+ return false;
+ }
+
+ for (int trackedState : trackedStates) {
+ if (trackedState == state) {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+
+ private final List<PowerComponent> mPowerComponents = new ArrayList<>();
+
+ /**
+ * Creates a configuration for the specified power component, which may be one of the
+ * standard power component IDs, e.g. {@link BatteryConsumer#POWER_COMPONENT_CPU}, or
+ * a custom power component.
+ */
+ public PowerComponent trackPowerComponent(int powerComponentId) {
+ PowerComponent builder = new PowerComponent(powerComponentId);
+ mPowerComponents.add(builder);
+ return builder;
+ }
+
+ public List<PowerComponent> getPowerComponentsAggregatedStatsConfigs() {
+ return mPowerComponents;
+ }
+}
diff --git a/services/core/java/com/android/server/power/stats/AggregatedPowerStatsSection.java b/services/core/java/com/android/server/power/stats/AggregatedPowerStatsSection.java
new file mode 100644
index 0000000..7ba4330
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/AggregatedPowerStatsSection.java
@@ -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.server.power.stats;
+
+import android.util.IndentingPrintWriter;
+
+import com.android.modules.utils.TypedXmlSerializer;
+
+import java.io.IOException;
+
+class AggregatedPowerStatsSection extends PowerStatsSpan.Section {
+ public static final String TYPE = "aggregated-power-stats";
+
+ private final AggregatedPowerStats mAggregatedPowerStats;
+
+ AggregatedPowerStatsSection(AggregatedPowerStats aggregatedPowerStats) {
+ super(TYPE);
+ mAggregatedPowerStats = aggregatedPowerStats;
+ }
+
+ public AggregatedPowerStats getAggregatedPowerStats() {
+ return mAggregatedPowerStats;
+ }
+
+ @Override
+ void write(TypedXmlSerializer serializer) throws IOException {
+ mAggregatedPowerStats.writeXml(serializer);
+ }
+
+ @Override
+ public void dump(IndentingPrintWriter ipw) {
+ mAggregatedPowerStats.dump(ipw);
+ }
+}
diff --git a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
index 613f189..a9c2bc2 100644
--- a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
+++ b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
@@ -124,6 +124,7 @@
import com.android.internal.os.KernelSingleUidTimeReader;
import com.android.internal.os.LongArrayMultiStateCounter;
import com.android.internal.os.LongMultiStateCounter;
+import com.android.internal.os.MonotonicClock;
import com.android.internal.os.PowerProfile;
import com.android.internal.os.PowerStats;
import com.android.internal.os.RailStats;
@@ -283,7 +284,6 @@
private final LongSparseArray<SamplingTimer> mKernelMemoryStats = new LongSparseArray<>();
private int[] mCpuPowerBracketMap;
private final CpuPowerStatsCollector mCpuPowerStatsCollector;
- private final PowerStatsAggregator mPowerStatsAggregator;
public LongSparseArray<SamplingTimer> getKernelMemoryStats() {
return mKernelMemoryStats;
@@ -356,6 +356,11 @@
protected Queue<UidToRemove> mPendingRemovedUids = new LinkedList<>();
@NonNull
+ public BatteryStatsHistory getHistory() {
+ return mHistory;
+ }
+
+ @NonNull
BatteryStatsHistory copyHistory() {
return mHistory.copy();
}
@@ -1718,14 +1723,7 @@
return mMaxLearnedBatteryCapacityUah;
}
- public BatteryStatsImpl() {
- this(Clock.SYSTEM_CLOCK);
- }
-
- public BatteryStatsImpl(Clock clock) {
- this(clock, null);
- }
-
+ @VisibleForTesting
public BatteryStatsImpl(Clock clock, File historyDirectory) {
init(clock);
mBatteryStatsConfig = new BatteryStatsConfig.Builder().build();
@@ -1737,18 +1735,19 @@
mCheckinFile = null;
mStatsFile = null;
mHistory = new BatteryStatsHistory(mConstants.MAX_HISTORY_FILES,
- mConstants.MAX_HISTORY_BUFFER, mStepDetailsCalculator, mClock);
+ mConstants.MAX_HISTORY_BUFFER, mStepDetailsCalculator, mClock,
+ new MonotonicClock(0, mClock));
} else {
mCheckinFile = new AtomicFile(new File(historyDirectory, "batterystats-checkin.bin"));
mStatsFile = new AtomicFile(new File(historyDirectory, "batterystats.bin"));
mHistory = new BatteryStatsHistory(historyDirectory, mConstants.MAX_HISTORY_FILES,
- mConstants.MAX_HISTORY_BUFFER, mStepDetailsCalculator, mClock);
+ mConstants.MAX_HISTORY_BUFFER, mStepDetailsCalculator, mClock,
+ new MonotonicClock(0, mClock));
}
mPlatformIdleStateCallback = null;
mEnergyConsumerRetriever = null;
mUserInfoProvider = null;
mCpuPowerStatsCollector = null;
- mPowerStatsAggregator = null;
}
private void init(Clock clock) {
@@ -10906,20 +10905,12 @@
return mTmpCpuTimeInFreq;
}
- public BatteryStatsImpl(@NonNull BatteryStatsConfig config, @Nullable File systemDir,
+ public BatteryStatsImpl(@NonNull BatteryStatsConfig config, @NonNull Clock clock,
+ @NonNull MonotonicClock monotonicClock, @Nullable File systemDir,
@NonNull Handler handler, @Nullable PlatformIdleStateCallback cb,
@Nullable EnergyStatsRetriever energyStatsCb,
@NonNull UserInfoProvider userInfoProvider, @NonNull PowerProfile powerProfile,
@NonNull CpuScalingPolicies cpuScalingPolicies) {
- this(config, Clock.SYSTEM_CLOCK, systemDir, handler, cb, energyStatsCb, userInfoProvider,
- powerProfile, cpuScalingPolicies);
- }
-
- private BatteryStatsImpl(@NonNull BatteryStatsConfig config, @NonNull Clock clock,
- @Nullable File systemDir, @NonNull Handler handler,
- @Nullable PlatformIdleStateCallback cb, @Nullable EnergyStatsRetriever energyStatsCb,
- @NonNull UserInfoProvider userInfoProvider, @NonNull PowerProfile powerProfile,
- @NonNull CpuScalingPolicies cpuScalingPolicies) {
init(clock);
mBatteryStatsConfig = config;
@@ -10936,31 +10927,19 @@
mCheckinFile = null;
mDailyFile = null;
mHistory = new BatteryStatsHistory(mConstants.MAX_HISTORY_FILES,
- mConstants.MAX_HISTORY_BUFFER, mStepDetailsCalculator, mClock);
+ mConstants.MAX_HISTORY_BUFFER, mStepDetailsCalculator, mClock, monotonicClock);
} else {
mStatsFile = new AtomicFile(new File(systemDir, "batterystats.bin"));
mCheckinFile = new AtomicFile(new File(systemDir, "batterystats-checkin.bin"));
mDailyFile = new AtomicFile(new File(systemDir, "batterystats-daily.xml"));
mHistory = new BatteryStatsHistory(systemDir, mConstants.MAX_HISTORY_FILES,
- mConstants.MAX_HISTORY_BUFFER, mStepDetailsCalculator, mClock);
+ mConstants.MAX_HISTORY_BUFFER, mStepDetailsCalculator, mClock, monotonicClock);
}
mCpuPowerStatsCollector = new CpuPowerStatsCollector(mCpuScalingPolicies, mPowerProfile,
mHandler, mBatteryStatsConfig.getPowerStatsThrottlePeriodCpu());
mCpuPowerStatsCollector.addConsumer(this::recordPowerStats);
- PowerStatsAggregator.Builder builder = new PowerStatsAggregator.Builder(mHistory);
- builder.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_CPU)
- .trackDeviceStates(
- PowerStatsAggregator.STATE_POWER,
- PowerStatsAggregator.STATE_SCREEN)
- .trackUidStates(
- PowerStatsAggregator.STATE_POWER,
- PowerStatsAggregator.STATE_SCREEN,
- PowerStatsAggregator.STATE_PROCESS_STATE);
-
- mPowerStatsAggregator = builder.build();
-
mStartCount++;
initTimersAndCounters();
mOnBattery = mOnBatteryInternal = false;
@@ -15702,20 +15681,23 @@
}
/**
+ * Schedules an immediate (but asynchronous) collection of PowerStats samples.
+ * Callers will need to wait for the collection to complete on the handler thread.
+ */
+ public void schedulePowerStatsSampleCollection() {
+ if (mCpuPowerStatsCollector == null) {
+ return;
+ }
+ mCpuPowerStatsCollector.forceSchedule();
+ }
+
+ /**
* Grabs one sample of PowerStats and prints it.
*/
public void dumpStatsSample(PrintWriter pw) {
mCpuPowerStatsCollector.collectAndDump(pw);
}
- /**
- * Aggregates power stats between the specified times and prints them.
- */
- public void dumpAggregatedStats(PrintWriter pw, long startTimeMs, long endTimeMs) {
- mPowerStatsAggregator.aggregateBatteryStats(startTimeMs, endTimeMs,
- stats-> stats.dump(pw));
- }
-
private final Runnable mWriteAsyncRunnable = () -> {
synchronized (BatteryStatsImpl.this) {
writeSyncLocked();
diff --git a/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java b/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java
index f6fa9f2..851a3f7 100644
--- a/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java
+++ b/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java
@@ -46,7 +46,7 @@
private static final String TAG = "BatteryUsageStatsProv";
private final Context mContext;
private final BatteryStats mStats;
- private final BatteryUsageStatsStore mBatteryUsageStatsStore;
+ private final PowerStatsStore mPowerStatsStore;
private final PowerProfile mPowerProfile;
private final CpuScalingPolicies mCpuScalingPolicies;
private final Object mLock = new Object();
@@ -58,10 +58,10 @@
@VisibleForTesting
public BatteryUsageStatsProvider(Context context, BatteryStats stats,
- BatteryUsageStatsStore batteryUsageStatsStore) {
+ PowerStatsStore powerStatsStore) {
mContext = context;
mStats = stats;
- mBatteryUsageStatsStore = batteryUsageStatsStore;
+ mPowerStatsStore = powerStatsStore;
mPowerProfile = stats instanceof BatteryStatsImpl
? ((BatteryStatsImpl) stats).getPowerProfile()
: new PowerProfile(context);
@@ -314,20 +314,52 @@
final BatteryUsageStats.Builder builder = new BatteryUsageStats.Builder(
customEnergyConsumerNames, includePowerModels, includeProcessStateData,
minConsumedPowerThreshold);
- if (mBatteryUsageStatsStore == null) {
- Log.e(TAG, "BatteryUsageStatsStore is unavailable");
+ if (mPowerStatsStore == null) {
+ Log.e(TAG, "PowerStatsStore is unavailable");
return builder.build();
}
- final long[] timestamps = mBatteryUsageStatsStore.listBatteryUsageStatsTimestamps();
- for (long timestamp : timestamps) {
- if (timestamp > query.getFromTimestamp() && timestamp <= query.getToTimestamp()) {
- final BatteryUsageStats snapshot =
- mBatteryUsageStatsStore.loadBatteryUsageStats(timestamp);
- if (snapshot == null) {
- continue;
- }
+ List<PowerStatsSpan.Metadata> toc = mPowerStatsStore.getTableOfContents();
+ for (PowerStatsSpan.Metadata spanMetadata : toc) {
+ if (!spanMetadata.getSections().contains(BatteryUsageStatsSection.TYPE)) {
+ continue;
+ }
+ // BatteryUsageStatsQuery is expressed in terms of wall-clock time range for the
+ // session end time.
+ //
+ // The following algorithm is correct when there is only one time frame in the span.
+ // When the wall-clock time is adjusted in the middle of an stats span,
+ // constraining it by wall-clock time becomes ambiguous. In this case, the algorithm
+ // only covers some situations, but not others. When using the resulting data for
+ // analysis, we should always pay attention to the full set of included timeframes.
+ // TODO(b/298459065): switch to monotonic clock
+ long minTime = Long.MAX_VALUE;
+ long maxTime = 0;
+ for (PowerStatsSpan.TimeFrame timeFrame : spanMetadata.getTimeFrames()) {
+ long spanEndTime = timeFrame.startTime + timeFrame.duration;
+ minTime = Math.min(minTime, spanEndTime);
+ maxTime = Math.max(maxTime, spanEndTime);
+ }
+
+ // Per BatteryUsageStatsQuery API, the "from" timestamp is *exclusive*,
+ // while the "to" timestamp is *inclusive*.
+ boolean isInRange =
+ (query.getFromTimestamp() == 0 || minTime > query.getFromTimestamp())
+ && (query.getToTimestamp() == 0 || maxTime <= query.getToTimestamp());
+ if (!isInRange) {
+ continue;
+ }
+
+ PowerStatsSpan powerStatsSpan = mPowerStatsStore.loadPowerStatsSpan(
+ spanMetadata.getId(), BatteryUsageStatsSection.TYPE);
+ if (powerStatsSpan == null) {
+ continue;
+ }
+
+ for (PowerStatsSpan.Section section : powerStatsSpan.getSections()) {
+ BatteryUsageStats snapshot =
+ ((BatteryUsageStatsSection) section).getBatteryUsageStats();
if (!Arrays.equals(snapshot.getCustomPowerComponentNames(),
customEnergyConsumerNames)) {
Log.w(TAG, "Ignoring older BatteryUsageStats snapshot, which has different "
diff --git a/services/core/java/com/android/server/power/stats/BatteryUsageStatsSection.java b/services/core/java/com/android/server/power/stats/BatteryUsageStatsSection.java
new file mode 100644
index 0000000..b95faac
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/BatteryUsageStatsSection.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.power.stats;
+
+import android.os.BatteryUsageStats;
+import android.util.IndentingPrintWriter;
+
+import com.android.modules.utils.TypedXmlSerializer;
+
+import java.io.IOException;
+
+class BatteryUsageStatsSection extends PowerStatsSpan.Section {
+ public static final String TYPE = "battery-usage-stats";
+
+ private final BatteryUsageStats mBatteryUsageStats;
+
+ BatteryUsageStatsSection(BatteryUsageStats batteryUsageStats) {
+ super(TYPE);
+ mBatteryUsageStats = batteryUsageStats;
+ }
+
+ public BatteryUsageStats getBatteryUsageStats() {
+ return mBatteryUsageStats;
+ }
+
+ @Override
+ void write(TypedXmlSerializer serializer) throws IOException {
+ mBatteryUsageStats.writeXml(serializer);
+ }
+
+ @Override
+ public void dump(IndentingPrintWriter ipw) {
+ mBatteryUsageStats.dump(ipw, "");
+ }
+}
diff --git a/services/core/java/com/android/server/power/stats/BatteryUsageStatsStore.java b/services/core/java/com/android/server/power/stats/BatteryUsageStatsStore.java
deleted file mode 100644
index 0d7a140..0000000
--- a/services/core/java/com/android/server/power/stats/BatteryUsageStatsStore.java
+++ /dev/null
@@ -1,338 +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 com.android.server.power.stats;
-
-import android.annotation.Nullable;
-import android.content.Context;
-import android.os.BatteryUsageStats;
-import android.os.BatteryUsageStatsQuery;
-import android.os.Handler;
-import android.util.AtomicFile;
-import android.util.Log;
-import android.util.LongArray;
-import android.util.Slog;
-import android.util.Xml;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.modules.utils.TypedXmlPullParser;
-import com.android.modules.utils.TypedXmlSerializer;
-
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.nio.channels.FileChannel;
-import java.nio.channels.FileLock;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.StandardOpenOption;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Properties;
-import java.util.TreeMap;
-import java.util.concurrent.locks.ReentrantLock;
-
-/**
- * A storage mechanism for BatteryUsageStats snapshots.
- */
-public class BatteryUsageStatsStore {
- private static final String TAG = "BatteryUsageStatsStore";
-
- private static final List<BatteryUsageStatsQuery> BATTERY_USAGE_STATS_QUERY = List.of(
- new BatteryUsageStatsQuery.Builder()
- .setMaxStatsAgeMs(0)
- .includePowerModels()
- .includeProcessStateData()
- .build());
- private static final String BATTERY_USAGE_STATS_DIR = "battery-usage-stats";
- private static final String SNAPSHOT_FILE_EXTENSION = ".bus";
- private static final String DIR_LOCK_FILENAME = ".lock";
- private static final String CONFIG_FILENAME = "config";
- private static final String BATTERY_USAGE_STATS_BEFORE_RESET_TIMESTAMP_PROPERTY =
- "BATTERY_USAGE_STATS_BEFORE_RESET_TIMESTAMP";
- private static final long MAX_BATTERY_STATS_SNAPSHOT_STORAGE_BYTES = 100 * 1024;
-
- private final Context mContext;
- private final BatteryStatsImpl mBatteryStats;
- private boolean mSystemReady;
- private final File mStoreDir;
- private final File mLockFile;
- private final ReentrantLock mFileLock = new ReentrantLock();
- private FileLock mJvmLock;
- private final AtomicFile mConfigFile;
- private final long mMaxStorageBytes;
- private final Handler mHandler;
- private final BatteryUsageStatsProvider mBatteryUsageStatsProvider;
-
- public BatteryUsageStatsStore(Context context, BatteryStatsImpl stats, File systemDir,
- Handler handler) {
- this(context, stats, systemDir, handler, MAX_BATTERY_STATS_SNAPSHOT_STORAGE_BYTES);
- }
-
- @VisibleForTesting
- public BatteryUsageStatsStore(Context context, BatteryStatsImpl batteryStats, File systemDir,
- Handler handler, long maxStorageBytes) {
- mContext = context;
- mBatteryStats = batteryStats;
- mStoreDir = new File(systemDir, BATTERY_USAGE_STATS_DIR);
- mLockFile = new File(mStoreDir, DIR_LOCK_FILENAME);
- mConfigFile = new AtomicFile(new File(mStoreDir, CONFIG_FILENAME));
- mHandler = handler;
- mMaxStorageBytes = maxStorageBytes;
- mBatteryStats.setBatteryResetListener(this::prepareForBatteryStatsReset);
- mBatteryUsageStatsProvider = new BatteryUsageStatsProvider(mContext, mBatteryStats);
- }
-
- /**
- * Notifies BatteryUsageStatsStore that the system server is ready.
- */
- public void onSystemReady() {
- mSystemReady = true;
- }
-
- private void prepareForBatteryStatsReset(int resetReason) {
- if (resetReason == BatteryStatsImpl.RESET_REASON_CORRUPT_FILE || !mSystemReady) {
- return;
- }
-
- final List<BatteryUsageStats> stats =
- mBatteryUsageStatsProvider.getBatteryUsageStats(BATTERY_USAGE_STATS_QUERY);
- if (stats.isEmpty()) {
- Slog.wtf(TAG, "No battery usage stats generated");
- return;
- }
-
- mHandler.post(() -> storeBatteryUsageStats(stats.get(0)));
- }
-
- private void storeBatteryUsageStats(BatteryUsageStats stats) {
- lockSnapshotDirectory();
- try {
- if (!mStoreDir.exists()) {
- if (!mStoreDir.mkdirs()) {
- Slog.e(TAG, "Could not create a directory for battery usage stats snapshots");
- return;
- }
- }
- File file = makeSnapshotFilename(stats.getStatsEndTimestamp());
- try {
- writeXmlFileLocked(stats, file);
- } catch (Exception e) {
- Slog.e(TAG, "Cannot save battery usage stats", e);
- }
-
- removeOldSnapshotsLocked();
- } finally {
- unlockSnapshotDirectory();
- }
- }
-
- /**
- * Returns the timestamps of the stored BatteryUsageStats snapshots. The timestamp corresponds
- * to the time the snapshot was taken {@link BatteryUsageStats#getStatsEndTimestamp()}.
- */
- public long[] listBatteryUsageStatsTimestamps() {
- LongArray timestamps = new LongArray(100);
- lockSnapshotDirectory();
- try {
- for (File file : mStoreDir.listFiles()) {
- String fileName = file.getName();
- if (fileName.endsWith(SNAPSHOT_FILE_EXTENSION)) {
- try {
- String fileNameWithoutExtension = fileName.substring(0,
- fileName.length() - SNAPSHOT_FILE_EXTENSION.length());
- timestamps.add(Long.parseLong(fileNameWithoutExtension));
- } catch (NumberFormatException e) {
- Slog.wtf(TAG, "Invalid format of BatteryUsageStats snapshot file name: "
- + fileName);
- }
- }
- }
- } finally {
- unlockSnapshotDirectory();
- }
- return timestamps.toArray();
- }
-
- /**
- * Reads the specified snapshot of BatteryUsageStats. Returns null if the snapshot
- * does not exist.
- */
- @Nullable
- public BatteryUsageStats loadBatteryUsageStats(long timestamp) {
- lockSnapshotDirectory();
- try {
- File file = makeSnapshotFilename(timestamp);
- try {
- return readXmlFileLocked(file);
- } catch (Exception e) {
- Slog.e(TAG, "Cannot read battery usage stats", e);
- }
- } finally {
- unlockSnapshotDirectory();
- }
- return null;
- }
-
- /**
- * Saves the supplied timestamp of the BATTERY_USAGE_STATS_BEFORE_RESET statsd atom pull
- * in persistent file.
- */
- public void setLastBatteryUsageStatsBeforeResetAtomPullTimestamp(long timestamp) {
- Properties props = new Properties();
- lockSnapshotDirectory();
- try {
- try (InputStream in = mConfigFile.openRead()) {
- props.load(in);
- } catch (IOException e) {
- Slog.e(TAG, "Cannot load config file " + mConfigFile, e);
- }
- props.put(BATTERY_USAGE_STATS_BEFORE_RESET_TIMESTAMP_PROPERTY,
- String.valueOf(timestamp));
- FileOutputStream out = null;
- try {
- out = mConfigFile.startWrite();
- props.store(out, "Statsd atom pull timestamps");
- mConfigFile.finishWrite(out);
- } catch (IOException e) {
- mConfigFile.failWrite(out);
- Slog.e(TAG, "Cannot save config file " + mConfigFile, e);
- }
- } finally {
- unlockSnapshotDirectory();
- }
- }
-
- /**
- * Retrieves the previously saved timestamp of the last BATTERY_USAGE_STATS_BEFORE_RESET
- * statsd atom pull.
- */
- public long getLastBatteryUsageStatsBeforeResetAtomPullTimestamp() {
- Properties props = new Properties();
- lockSnapshotDirectory();
- try {
- try (InputStream in = mConfigFile.openRead()) {
- props.load(in);
- } catch (IOException e) {
- Slog.e(TAG, "Cannot load config file " + mConfigFile, e);
- }
- } finally {
- unlockSnapshotDirectory();
- }
- return Long.parseLong(
- props.getProperty(BATTERY_USAGE_STATS_BEFORE_RESET_TIMESTAMP_PROPERTY, "0"));
- }
-
- private void lockSnapshotDirectory() {
- mFileLock.lock();
-
- // Lock the directory from access by other JVMs
- try {
- mLockFile.getParentFile().mkdirs();
- mLockFile.createNewFile();
- mJvmLock = FileChannel.open(mLockFile.toPath(), StandardOpenOption.WRITE).lock();
- } catch (IOException e) {
- Log.e(TAG, "Cannot lock snapshot directory", e);
- }
- }
-
- private void unlockSnapshotDirectory() {
- try {
- mJvmLock.close();
- } catch (IOException e) {
- Log.e(TAG, "Cannot unlock snapshot directory", e);
- } finally {
- mFileLock.unlock();
- }
- }
-
- /**
- * Creates a file name by formatting the timestamp as 19-digit zero-padded number.
- * This ensures that sorted directory list follows the chronological order.
- */
- private File makeSnapshotFilename(long statsEndTimestamp) {
- return new File(mStoreDir, String.format(Locale.ENGLISH, "%019d", statsEndTimestamp)
- + SNAPSHOT_FILE_EXTENSION);
- }
-
- private void writeXmlFileLocked(BatteryUsageStats stats, File file) throws IOException {
- try (OutputStream out = new FileOutputStream(file)) {
- TypedXmlSerializer serializer = Xml.newBinarySerializer();
- serializer.setOutput(out, StandardCharsets.UTF_8.name());
- serializer.startDocument(null, true);
- stats.writeXml(serializer);
- serializer.endDocument();
- }
- }
-
- private BatteryUsageStats readXmlFileLocked(File file)
- throws IOException, XmlPullParserException {
- try (InputStream in = new FileInputStream(file)) {
- TypedXmlPullParser parser = Xml.newBinaryPullParser();
- parser.setInput(in, StandardCharsets.UTF_8.name());
- return BatteryUsageStats.createFromXml(parser);
- }
- }
-
- private void removeOldSnapshotsLocked() {
- // Read the directory list into a _sorted_ map. The alphanumeric ordering
- // corresponds to the historical order of snapshots because the file names
- // are timestamps zero-padded to the same length.
- long totalSize = 0;
- TreeMap<File, Long> mFileSizes = new TreeMap<>();
- for (File file : mStoreDir.listFiles()) {
- final long fileSize = file.length();
- totalSize += fileSize;
- if (file.getName().endsWith(SNAPSHOT_FILE_EXTENSION)) {
- mFileSizes.put(file, fileSize);
- }
- }
-
- while (totalSize > mMaxStorageBytes) {
- final Map.Entry<File, Long> entry = mFileSizes.firstEntry();
- if (entry == null) {
- break;
- }
-
- File file = entry.getKey();
- if (!file.delete()) {
- Slog.e(TAG, "Cannot delete battery usage stats " + file);
- }
- totalSize -= entry.getValue();
- mFileSizes.remove(file);
- }
- }
-
- public void removeAllSnapshots() {
- lockSnapshotDirectory();
- try {
- for (File file : mStoreDir.listFiles()) {
- if (file.getName().endsWith(SNAPSHOT_FILE_EXTENSION)) {
- if (!file.delete()) {
- Slog.e(TAG, "Cannot delete battery usage stats " + file);
- }
- }
- }
- } finally {
- unlockSnapshotDirectory();
- }
- }
-}
diff --git a/services/core/java/com/android/server/power/stats/CpuAggregatedPowerStats.java b/services/core/java/com/android/server/power/stats/CpuAggregatedPowerStats.java
index 5b3fe06..fbf6928 100644
--- a/services/core/java/com/android/server/power/stats/CpuAggregatedPowerStats.java
+++ b/services/core/java/com/android/server/power/stats/CpuAggregatedPowerStats.java
@@ -16,14 +16,8 @@
package com.android.server.power.stats;
-import android.os.BatteryConsumer;
-
-import com.android.internal.os.MultiStateStats;
-
class CpuAggregatedPowerStats extends PowerComponentAggregatedPowerStats {
-
- CpuAggregatedPowerStats(MultiStateStats.States[] deviceStates,
- MultiStateStats.States[] uidStates) {
- super(BatteryConsumer.POWER_COMPONENT_CPU, deviceStates, uidStates);
+ CpuAggregatedPowerStats(AggregatedPowerStatsConfig.PowerComponent config) {
+ super(config);
}
}
diff --git a/services/core/java/com/android/server/power/stats/PowerComponentAggregatedPowerStats.java b/services/core/java/com/android/server/power/stats/PowerComponentAggregatedPowerStats.java
index 686268f..05c0a13 100644
--- a/services/core/java/com/android/server/power/stats/PowerComponentAggregatedPowerStats.java
+++ b/services/core/java/com/android/server/power/stats/PowerComponentAggregatedPowerStats.java
@@ -21,7 +21,13 @@
import com.android.internal.os.MultiStateStats;
import com.android.internal.os.PowerStats;
+import com.android.modules.utils.TypedXmlPullParser;
+import com.android.modules.utils.TypedXmlSerializer;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
import java.util.Collection;
/**
@@ -31,6 +37,12 @@
* as part of the {@link PowerStats.Descriptor}.
*/
class PowerComponentAggregatedPowerStats {
+ static final String XML_TAG_POWER_COMPONENT = "power_component";
+ static final String XML_ATTR_ID = "id";
+ private static final String XML_TAG_DEVICE_STATS = "device-stats";
+ private static final String XML_TAG_UID_STATS = "uid-stats";
+ private static final String XML_ATTR_UID = "uid";
+
public final int powerComponentId;
private final MultiStateStats.States[] mDeviceStateConfig;
private final MultiStateStats.States[] mUidStateConfig;
@@ -49,22 +61,24 @@
public MultiStateStats stats;
}
- PowerComponentAggregatedPowerStats(int powerComponentId,
- MultiStateStats.States[] deviceStates,
- MultiStateStats.States[] uidStates) {
- this.powerComponentId = powerComponentId;
- mDeviceStateConfig = deviceStates;
- mUidStateConfig = uidStates;
+ PowerComponentAggregatedPowerStats(AggregatedPowerStatsConfig.PowerComponent config) {
+ this.powerComponentId = config.getPowerComponentId();
+ mDeviceStateConfig = config.getDeviceStateConfig();
+ mUidStateConfig = config.getUidStateConfig();
mDeviceStates = new int[mDeviceStateConfig.length];
mDeviceStateTimestamps = new long[mDeviceStateConfig.length];
}
- void setState(@PowerStatsAggregator.TrackedState int stateId, int state, long time) {
+ public PowerStats.Descriptor getPowerStatsDescriptor() {
+ return mPowerStatsDescriptor;
+ }
+
+ void setState(@AggregatedPowerStatsConfig.TrackedState int stateId, int state, long time) {
mDeviceStates[stateId] = state;
mDeviceStateTimestamps[stateId] = time;
if (mDeviceStateConfig[stateId].isTracked()) {
- if (mDeviceStats != null || createDeviceStats()) {
+ if (mDeviceStats != null) {
mDeviceStats.setState(stateId, state, time);
}
}
@@ -72,14 +86,14 @@
if (mUidStateConfig[stateId].isTracked()) {
for (int i = mUidStats.size() - 1; i >= 0; i--) {
PowerComponentAggregatedPowerStats.UidStats uidStats = mUidStats.valueAt(i);
- if (uidStats.stats != null || createUidStats(uidStats)) {
+ if (uidStats.stats != null) {
uidStats.stats.setState(stateId, state, time);
}
}
}
}
- void setUidState(int uid, @PowerStatsAggregator.TrackedState int stateId, int state,
+ void setUidState(int uid, @AggregatedPowerStatsConfig.TrackedState int stateId, int state,
long time) {
if (!mUidStateConfig[stateId].isTracked()) {
return;
@@ -89,7 +103,7 @@
uidStats.states[stateId] = state;
uidStats.stateTimestampMs[stateId] = time;
- if (uidStats.stats != null || createUidStats(uidStats)) {
+ if (uidStats.stats != null) {
uidStats.stats.setState(stateId, state, time);
}
}
@@ -102,13 +116,6 @@
mPowerStatsDescriptor = powerStats.descriptor;
if (mDeviceStats == null) {
- if (mStatsFactory == null) {
- mStatsFactory = new MultiStateStats.Factory(
- mPowerStatsDescriptor.statsArrayLength, mDeviceStateConfig);
- mUidStatsFactory = new MultiStateStats.Factory(
- mPowerStatsDescriptor.uidStatsArrayLength, mUidStateConfig);
- }
-
createDeviceStats();
}
@@ -183,7 +190,11 @@
private boolean createDeviceStats() {
if (mStatsFactory == null) {
- return false;
+ if (mPowerStatsDescriptor == null) {
+ return false;
+ }
+ mStatsFactory = new MultiStateStats.Factory(
+ mPowerStatsDescriptor.statsArrayLength, mDeviceStateConfig);
}
mDeviceStats = mStatsFactory.create();
@@ -196,7 +207,11 @@
private boolean createUidStats(UidStats uidStats) {
if (mUidStatsFactory == null) {
- return false;
+ if (mPowerStatsDescriptor == null) {
+ return false;
+ }
+ mUidStatsFactory = new MultiStateStats.Factory(
+ mPowerStatsDescriptor.uidStatsArrayLength, mUidStateConfig);
}
uidStats.stats = mUidStatsFactory.create();
@@ -211,6 +226,74 @@
return true;
}
+ public void writeXml(TypedXmlSerializer serializer) throws IOException {
+ // No stats aggregated - can skip writing XML altogether
+ if (mPowerStatsDescriptor == null) {
+ return;
+ }
+
+ serializer.startTag(null, XML_TAG_POWER_COMPONENT);
+ serializer.attributeInt(null, XML_ATTR_ID, powerComponentId);
+ mPowerStatsDescriptor.writeXml(serializer);
+
+ if (mDeviceStats != null) {
+ serializer.startTag(null, XML_TAG_DEVICE_STATS);
+ mDeviceStats.writeXml(serializer);
+ serializer.endTag(null, XML_TAG_DEVICE_STATS);
+ }
+
+ for (int i = mUidStats.size() - 1; i >= 0; i--) {
+ int uid = mUidStats.keyAt(i);
+ UidStats uidStats = mUidStats.valueAt(i);
+ if (uidStats.stats != null) {
+ serializer.startTag(null, XML_TAG_UID_STATS);
+ serializer.attributeInt(null, XML_ATTR_UID, uid);
+ uidStats.stats.writeXml(serializer);
+ serializer.endTag(null, XML_TAG_UID_STATS);
+ }
+ }
+
+ serializer.endTag(null, XML_TAG_POWER_COMPONENT);
+ serializer.flush();
+ }
+
+ public boolean readFromXml(TypedXmlPullParser parser) throws XmlPullParserException,
+ IOException {
+ int eventType = parser.getEventType();
+ while (eventType != XmlPullParser.END_DOCUMENT) {
+ if (eventType == XmlPullParser.START_TAG) {
+ switch (parser.getName()) {
+ case PowerStats.Descriptor.XML_TAG_DESCRIPTOR:
+ mPowerStatsDescriptor = PowerStats.Descriptor.createFromXml(parser);
+ if (mPowerStatsDescriptor == null) {
+ return false;
+ }
+ break;
+ case XML_TAG_DEVICE_STATS:
+ if (mDeviceStats == null) {
+ createDeviceStats();
+ }
+ if (!mDeviceStats.readFromXml(parser)) {
+ return false;
+ }
+ break;
+ case XML_TAG_UID_STATS:
+ int uid = parser.getAttributeInt(null, XML_ATTR_UID);
+ UidStats uidStats = getUidStats(uid);
+ if (uidStats.stats == null) {
+ createUidStats(uidStats);
+ }
+ if (!uidStats.stats.readFromXml(parser)) {
+ return false;
+ }
+ break;
+ }
+ }
+ eventType = parser.next();
+ }
+ return true;
+ }
+
void dumpDevice(IndentingPrintWriter ipw) {
if (mDeviceStats != null) {
ipw.println(mPowerStatsDescriptor.name);
diff --git a/services/core/java/com/android/server/power/stats/PowerStatsAggregator.java b/services/core/java/com/android/server/power/stats/PowerStatsAggregator.java
index 6a1c1da..f374fb7 100644
--- a/services/core/java/com/android/server/power/stats/PowerStatsAggregator.java
+++ b/services/core/java/com/android/server/power/stats/PowerStatsAggregator.java
@@ -15,59 +15,26 @@
*/
package com.android.server.power.stats;
-import android.annotation.IntDef;
-import android.os.BatteryConsumer;
import android.os.BatteryStats;
import com.android.internal.os.BatteryStatsHistory;
import com.android.internal.os.BatteryStatsHistoryIterator;
-import com.android.internal.os.MultiStateStats;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
-import java.util.List;
import java.util.function.Consumer;
-class PowerStatsAggregator {
- public static final int STATE_POWER = 0;
- public static final int STATE_SCREEN = 1;
- public static final int STATE_PROCESS_STATE = 2;
-
- @IntDef({
- STATE_POWER,
- STATE_SCREEN,
- STATE_PROCESS_STATE,
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface TrackedState {
- }
-
- static final int POWER_STATE_BATTERY = 0;
- static final int POWER_STATE_OTHER = 1; // Plugged in, or on wireless charger, etc.
- static final String[] STATE_LABELS_POWER = {"pwr-battery", "pwr-other"};
-
- static final int SCREEN_STATE_ON = 0;
- static final int SCREEN_STATE_OTHER = 1; // Off, doze etc
- static final String[] STATE_LABELS_SCREEN = {"scr-on", "scr-other"};
-
- static final String[] STATE_LABELS_PROCESS_STATE;
-
- static {
- String[] procStateLabels = new String[BatteryConsumer.PROCESS_STATE_COUNT];
- for (int i = 0; i < BatteryConsumer.PROCESS_STATE_COUNT; i++) {
- procStateLabels[i] = BatteryConsumer.processStateToString(i);
- }
- STATE_LABELS_PROCESS_STATE = procStateLabels;
- }
-
- private final BatteryStatsHistory mHistory;
+/**
+ * Power stats aggregator. It reads through portions of battery stats history, finds
+ * relevant items (state changes, power stats etc) and produces one or more
+ * {@link AggregatedPowerStats} that adds up power stats from the samples found in battery history.
+ */
+public class PowerStatsAggregator {
private final AggregatedPowerStats mStats;
+ private final BatteryStatsHistory mHistory;
- private PowerStatsAggregator(BatteryStatsHistory history,
- AggregatedPowerStats aggregatedPowerStats) {
+ public PowerStatsAggregator(AggregatedPowerStatsConfig aggregatedPowerStatsConfig,
+ BatteryStatsHistory history) {
+ mStats = new AggregatedPowerStats(aggregatedPowerStatsConfig);
mHistory = history;
- mStats = aggregatedPowerStats;
}
/**
@@ -82,12 +49,10 @@
* Note: the AggregatedPowerStats object is reused, so the consumer should fully consume
* the stats in the <code>accept</code> method and never cache it.
*/
- void aggregateBatteryStats(long startTimeMs, long endTimeMs,
+ public void aggregatePowerStats(long startTimeMs, long endTimeMs,
Consumer<AggregatedPowerStats> consumer) {
- mStats.reset();
-
- int currentBatteryState = POWER_STATE_BATTERY;
- int currentScreenState = SCREEN_STATE_OTHER;
+ int currentBatteryState = AggregatedPowerStatsConfig.POWER_STATE_BATTERY;
+ int currentScreenState = AggregatedPowerStatsConfig.SCREEN_STATE_OTHER;
long baseTime = -1;
long lastTime = 0;
try (BatteryStatsHistoryIterator iterator =
@@ -96,131 +61,60 @@
BatteryStats.HistoryItem item = iterator.next();
if (baseTime < 0) {
- mStats.setStartTime(item.currentTime);
+ mStats.addClockUpdate(item.time, item.currentTime);
baseTime = item.time;
+ } else if (item.cmd == BatteryStats.HistoryItem.CMD_CURRENT_TIME
+ || item.cmd == BatteryStats.HistoryItem.CMD_RESET) {
+ mStats.addClockUpdate(item.time, item.currentTime);
}
lastTime = item.time;
int batteryState =
(item.states & BatteryStats.HistoryItem.STATE_BATTERY_PLUGGED_FLAG) != 0
- ? POWER_STATE_OTHER : POWER_STATE_BATTERY;
+ ? AggregatedPowerStatsConfig.POWER_STATE_OTHER
+ : AggregatedPowerStatsConfig.POWER_STATE_BATTERY;
if (batteryState != currentBatteryState) {
- mStats.setDeviceState(STATE_POWER, batteryState, item.time);
+ mStats.setDeviceState(AggregatedPowerStatsConfig.STATE_POWER, batteryState,
+ item.time);
currentBatteryState = batteryState;
}
int screenState =
(item.states & BatteryStats.HistoryItem.STATE_SCREEN_ON_FLAG) != 0
- ? SCREEN_STATE_ON : SCREEN_STATE_OTHER;
+ ? AggregatedPowerStatsConfig.SCREEN_STATE_ON
+ : AggregatedPowerStatsConfig.SCREEN_STATE_OTHER;
if (screenState != currentScreenState) {
- mStats.setDeviceState(STATE_SCREEN, screenState, item.time);
+ mStats.setDeviceState(AggregatedPowerStatsConfig.STATE_SCREEN, screenState,
+ item.time);
currentScreenState = screenState;
}
if (item.processStateChange != null) {
- mStats.setUidState(item.processStateChange.uid, STATE_PROCESS_STATE,
+ mStats.setUidState(item.processStateChange.uid,
+ AggregatedPowerStatsConfig.STATE_PROCESS_STATE,
item.processStateChange.processState, item.time);
}
if (item.powerStats != null) {
if (!mStats.isCompatible(item.powerStats)) {
- mStats.setDuration(lastTime - baseTime);
- consumer.accept(mStats);
+ if (lastTime > baseTime) {
+ mStats.setDuration(lastTime - baseTime);
+ consumer.accept(mStats);
+ }
mStats.reset();
- mStats.setStartTime(item.currentTime);
+ mStats.addClockUpdate(item.time, item.currentTime);
baseTime = lastTime = item.time;
}
mStats.addPowerStats(item.powerStats, item.time);
}
}
}
- mStats.setDuration(lastTime - baseTime);
- consumer.accept(mStats);
- }
-
- static class Builder {
- static class PowerComponentAggregateStatsBuilder {
- private final int mPowerComponentId;
- private @TrackedState int[] mTrackedDeviceStates;
- private @TrackedState int[] mTrackedUidStates;
-
- PowerComponentAggregateStatsBuilder(int powerComponentId) {
- this.mPowerComponentId = powerComponentId;
- }
-
- public PowerComponentAggregateStatsBuilder trackDeviceStates(
- @TrackedState int... states) {
- mTrackedDeviceStates = states;
- return this;
- }
-
- public PowerComponentAggregateStatsBuilder trackUidStates(@TrackedState int... states) {
- mTrackedUidStates = states;
- return this;
- }
-
- private PowerComponentAggregatedPowerStats build() {
- MultiStateStats.States[] deviceStates = new MultiStateStats.States[]{
- new MultiStateStats.States(isTracked(mTrackedDeviceStates, STATE_POWER),
- PowerStatsAggregator.STATE_LABELS_POWER),
- new MultiStateStats.States(isTracked(mTrackedDeviceStates, STATE_SCREEN),
- PowerStatsAggregator.STATE_LABELS_SCREEN),
- };
-
- MultiStateStats.States[] uidStates = new MultiStateStats.States[]{
- new MultiStateStats.States(isTracked(mTrackedUidStates, STATE_POWER),
- PowerStatsAggregator.STATE_LABELS_POWER),
- new MultiStateStats.States(isTracked(mTrackedUidStates, STATE_SCREEN),
- PowerStatsAggregator.STATE_LABELS_SCREEN),
- new MultiStateStats.States(
- isTracked(mTrackedUidStates, STATE_PROCESS_STATE),
- PowerStatsAggregator.STATE_LABELS_PROCESS_STATE),
- };
-
- switch (mPowerComponentId) {
- case BatteryConsumer.POWER_COMPONENT_CPU:
- return new CpuAggregatedPowerStats(deviceStates, uidStates);
- default:
- return new PowerComponentAggregatedPowerStats(mPowerComponentId,
- deviceStates, uidStates);
- }
- }
-
- private boolean isTracked(int[] trackedStates, int state) {
- if (trackedStates == null) {
- return false;
- }
-
- for (int trackedState : trackedStates) {
- if (trackedState == state) {
- return true;
- }
- }
- return false;
- }
+ if (lastTime > baseTime) {
+ mStats.setDuration(lastTime - baseTime);
+ consumer.accept(mStats);
}
- private final BatteryStatsHistory mHistory;
- private final List<PowerComponentAggregateStatsBuilder> mPowerComponents =
- new ArrayList<>();
-
- Builder(BatteryStatsHistory history) {
- mHistory = history;
- }
-
- PowerComponentAggregateStatsBuilder trackPowerComponent(int powerComponentId) {
- PowerComponentAggregateStatsBuilder builder = new PowerComponentAggregateStatsBuilder(
- powerComponentId);
- mPowerComponents.add(builder);
- return builder;
- }
-
- PowerStatsAggregator build() {
- return new PowerStatsAggregator(mHistory, new AggregatedPowerStats(
- mPowerComponents.stream()
- .map(PowerComponentAggregateStatsBuilder::build)
- .toArray(PowerComponentAggregatedPowerStats[]::new)));
- }
+ mStats.reset(); // to free up memory
}
}
diff --git a/services/core/java/com/android/server/power/stats/PowerStatsScheduler.java b/services/core/java/com/android/server/power/stats/PowerStatsScheduler.java
new file mode 100644
index 0000000..58619c7
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/PowerStatsScheduler.java
@@ -0,0 +1,261 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.power.stats;
+
+import android.annotation.DurationMillisLong;
+import android.app.AlarmManager;
+import android.content.Context;
+import android.os.BatteryUsageStats;
+import android.os.BatteryUsageStatsQuery;
+import android.os.ConditionVariable;
+import android.os.Handler;
+import android.util.IndentingPrintWriter;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.Clock;
+import com.android.internal.os.MonotonicClock;
+
+import java.io.PrintWriter;
+import java.util.Calendar;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Controls the frequency at which {@link PowerStatsSpan}'s are generated and stored in
+ * {@link PowerStatsStore}.
+ */
+public class PowerStatsScheduler {
+ private static final long MINUTE_IN_MILLIS = TimeUnit.MINUTES.toMillis(1);
+ private static final long HOUR_IN_MILLIS = TimeUnit.HOURS.toMillis(1);
+
+ private final Context mContext;
+ private boolean mEnablePeriodicPowerStatsCollection;
+ @DurationMillisLong
+ private final long mAggregatedPowerStatsSpanDuration;
+ @DurationMillisLong
+ private final long mPowerStatsAggregationPeriod;
+ private final PowerStatsStore mPowerStatsStore;
+ private final Clock mClock;
+ private final MonotonicClock mMonotonicClock;
+ private final Handler mHandler;
+ private final BatteryStatsImpl mBatteryStats;
+ private final BatteryUsageStatsProvider mBatteryUsageStatsProvider;
+ private final PowerStatsAggregator mPowerStatsAggregator;
+ private long mLastSavedSpanEndMonotonicTime;
+
+ public PowerStatsScheduler(Context context, PowerStatsAggregator powerStatsAggregator,
+ @DurationMillisLong long aggregatedPowerStatsSpanDuration,
+ @DurationMillisLong long powerStatsAggregationPeriod, PowerStatsStore powerStatsStore,
+ Clock clock, MonotonicClock monotonicClock, Handler handler,
+ BatteryStatsImpl batteryStats, BatteryUsageStatsProvider batteryUsageStatsProvider) {
+ mContext = context;
+ mPowerStatsAggregator = powerStatsAggregator;
+ mAggregatedPowerStatsSpanDuration = aggregatedPowerStatsSpanDuration;
+ mPowerStatsAggregationPeriod = powerStatsAggregationPeriod;
+ mPowerStatsStore = powerStatsStore;
+ mClock = clock;
+ mMonotonicClock = monotonicClock;
+ mHandler = handler;
+ mBatteryStats = batteryStats;
+ mBatteryUsageStatsProvider = batteryUsageStatsProvider;
+ }
+
+ /**
+ * Kicks off the scheduling of power stats aggregation spans.
+ */
+ public void start(boolean enablePeriodicPowerStatsCollection) {
+ mBatteryStats.setBatteryResetListener(this::storeBatteryUsageStatsOnReset);
+ mEnablePeriodicPowerStatsCollection = enablePeriodicPowerStatsCollection;
+ if (mEnablePeriodicPowerStatsCollection) {
+ scheduleNextPowerStatsAggregation();
+ }
+ }
+
+ private void scheduleNextPowerStatsAggregation() {
+ AlarmManager alarmManager = mContext.getSystemService(AlarmManager.class);
+ alarmManager.set(AlarmManager.ELAPSED_REALTIME,
+ mClock.elapsedRealtime() + mPowerStatsAggregationPeriod, "PowerStats",
+ () -> {
+ schedulePowerStatsAggregation();
+ mHandler.post(this::scheduleNextPowerStatsAggregation);
+ }, mHandler);
+ }
+
+ /**
+ * Initiate an asynchronous process of aggregation of power stats.
+ */
+ @VisibleForTesting
+ public void schedulePowerStatsAggregation() {
+ // Catch up the power stats collectors
+ mBatteryStats.schedulePowerStatsSampleCollection();
+ mHandler.post(this::aggregateAndStorePowerStats);
+ }
+
+ private void aggregateAndStorePowerStats() {
+ long currentTimeMillis = mClock.currentTimeMillis();
+ long currentMonotonicTime = mMonotonicClock.monotonicTime();
+ long startTime = getLastSavedSpanEndMonotonicTime();
+ long endTimeMs = alignToWallClock(startTime + mAggregatedPowerStatsSpanDuration,
+ mAggregatedPowerStatsSpanDuration, currentMonotonicTime, currentTimeMillis);
+ while (endTimeMs <= currentMonotonicTime) {
+ mPowerStatsAggregator.aggregatePowerStats(startTime, endTimeMs,
+ stats -> {
+ storeAggregatedPowerStats(stats);
+ mLastSavedSpanEndMonotonicTime = stats.getStartTime() + stats.getDuration();
+ });
+
+ startTime = endTimeMs;
+ endTimeMs += mAggregatedPowerStatsSpanDuration;
+ }
+ }
+
+ /**
+ * Performs a power stats aggregation pass and then dumps all stored aggregated power stats
+ * spans followed by the remainder that has not been stored yet.
+ */
+ public void aggregateAndDumpPowerStats(PrintWriter pw) {
+ if (mHandler.getLooper().isCurrentThread()) {
+ throw new IllegalStateException("Should not be executed on the bg handler thread.");
+ }
+
+ schedulePowerStatsAggregation();
+
+ // Wait for the aggregation process to finish storing aggregated stats spans in the store.
+ awaitCompletion();
+
+ IndentingPrintWriter ipw = new IndentingPrintWriter(pw);
+ mHandler.post(() -> {
+ mPowerStatsStore.dump(ipw);
+ // Aggregate the remainder of power stats and dump the results without storing them yet.
+ long powerStoreEndMonotonicTime = getLastSavedSpanEndMonotonicTime();
+ mPowerStatsAggregator.aggregatePowerStats(powerStoreEndMonotonicTime, 0,
+ stats -> {
+ // Create a PowerStatsSpan for consistency of the textual output
+ PowerStatsSpan span = PowerStatsStore.createPowerStatsSpan(stats);
+ if (span != null) {
+ span.dump(ipw);
+ }
+ });
+ });
+
+ awaitCompletion();
+ }
+
+ /**
+ * Align the supplied time to the wall clock, for aesthetic purposes. For example, if
+ * the schedule is configured with a 15-min interval, the captured aggregated stats will
+ * be for spans XX:00-XX:15, XX:15-XX:30, XX:30-XX:45 and XX:45-XX:60. Only the current
+ * time is used for the alignment, so if the wall clock changed during an aggregation span,
+ * or if the device was off (which stops the monotonic clock), the alignment may be
+ * temporarily broken.
+ */
+ @VisibleForTesting
+ public static long alignToWallClock(long targetMonotonicTime, long interval,
+ long currentMonotonicTime, long currentTimeMillis) {
+
+ // Estimate the wall clock time for the requested targetMonotonicTime
+ long targetWallClockTime = currentTimeMillis + (targetMonotonicTime - currentMonotonicTime);
+
+ if (interval >= MINUTE_IN_MILLIS && TimeUnit.HOURS.toMillis(1) % interval == 0) {
+ // If the interval is a divisor of an hour, e.g. 10 minutes, 15 minutes, etc
+
+ // First, round up to the next whole minute
+ Calendar cal = Calendar.getInstance();
+ cal.setTimeInMillis(targetWallClockTime + MINUTE_IN_MILLIS - 1);
+ cal.set(Calendar.SECOND, 0);
+ cal.set(Calendar.MILLISECOND, 0);
+
+ // Now set the minute to a multiple of the requested interval
+ int intervalInMinutes = (int) (interval / MINUTE_IN_MILLIS);
+ cal.set(Calendar.MINUTE,
+ ((cal.get(Calendar.MINUTE) + intervalInMinutes - 1) / intervalInMinutes)
+ * intervalInMinutes);
+
+ long adjustment = cal.getTimeInMillis() - targetWallClockTime;
+ return targetMonotonicTime + adjustment;
+ } else if (interval >= HOUR_IN_MILLIS && TimeUnit.DAYS.toMillis(1) % interval == 0) {
+ // If the interval is a divisor of a day, e.g. 2h, 3h, etc
+
+ // First, round up to the next whole hour
+ Calendar cal = Calendar.getInstance();
+ cal.setTimeInMillis(targetWallClockTime + HOUR_IN_MILLIS - 1);
+ cal.set(Calendar.MINUTE, 0);
+ cal.set(Calendar.SECOND, 0);
+ cal.set(Calendar.MILLISECOND, 0);
+
+ // Now set the hour of day to a multiple of the requested interval
+ int intervalInHours = (int) (interval / HOUR_IN_MILLIS);
+ cal.set(Calendar.HOUR_OF_DAY,
+ ((cal.get(Calendar.HOUR_OF_DAY) + intervalInHours - 1) / intervalInHours)
+ * intervalInHours);
+
+ long adjustment = cal.getTimeInMillis() - targetWallClockTime;
+ return targetMonotonicTime + adjustment;
+ }
+
+ return targetMonotonicTime;
+ }
+
+ private long getLastSavedSpanEndMonotonicTime() {
+ if (mLastSavedSpanEndMonotonicTime != 0) {
+ return mLastSavedSpanEndMonotonicTime;
+ }
+
+ for (PowerStatsSpan.Metadata metadata : mPowerStatsStore.getTableOfContents()) {
+ if (metadata.getSections().contains(AggregatedPowerStatsSection.TYPE)) {
+ for (PowerStatsSpan.TimeFrame timeFrame : metadata.getTimeFrames()) {
+ long endMonotonicTime = timeFrame.startMonotonicTime + timeFrame.duration;
+ if (endMonotonicTime > mLastSavedSpanEndMonotonicTime) {
+ mLastSavedSpanEndMonotonicTime = endMonotonicTime;
+ }
+ }
+ }
+ }
+ return mLastSavedSpanEndMonotonicTime;
+ }
+
+ private void storeAggregatedPowerStats(AggregatedPowerStats stats) {
+ mPowerStatsStore.storeAggregatedPowerStats(stats);
+ }
+
+ private void storeBatteryUsageStatsOnReset(int resetReason) {
+ if (resetReason == BatteryStatsImpl.RESET_REASON_CORRUPT_FILE) {
+ return;
+ }
+
+ final BatteryUsageStats batteryUsageStats =
+ mBatteryUsageStatsProvider.getBatteryUsageStats(
+ new BatteryUsageStatsQuery.Builder()
+ .setMaxStatsAgeMs(0)
+ .includePowerModels()
+ .includeProcessStateData()
+ .build());
+
+ // TODO(b/188068523): BatteryUsageStats should use monotonic time for start and end
+ // Once that change is made, we will be able to use the BatteryUsageStats' monotonic
+ // start time
+ long monotonicStartTime =
+ mMonotonicClock.monotonicTime() - batteryUsageStats.getStatsDuration();
+ mHandler.post(() ->
+ mPowerStatsStore.storeBatteryUsageStats(monotonicStartTime, batteryUsageStats));
+ }
+
+ private void awaitCompletion() {
+ ConditionVariable done = new ConditionVariable();
+ mHandler.post(done::open);
+ done.block();
+ }
+}
diff --git a/services/core/java/com/android/server/power/stats/PowerStatsSpan.java b/services/core/java/com/android/server/power/stats/PowerStatsSpan.java
new file mode 100644
index 0000000..3b260ca
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/PowerStatsSpan.java
@@ -0,0 +1,433 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.power.stats;
+
+import android.annotation.CurrentTimeMillisLong;
+import android.annotation.DurationMillisLong;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.util.IndentingPrintWriter;
+import android.util.Slog;
+import android.util.TimeUtils;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.modules.utils.TypedXmlPullParser;
+import com.android.modules.utils.TypedXmlSerializer;
+
+import com.google.android.collect.Sets;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.StringWriter;
+import java.nio.charset.StandardCharsets;
+import java.time.Instant;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Contains power stats of various kinds, aggregated over a time span.
+ */
+public class PowerStatsSpan {
+ private static final String TAG = "PowerStatsStore";
+
+ /**
+ * Increment VERSION when the XML format of the store changes. Also, update
+ * {@link #isCompatibleXmlFormat} to return true for all legacy versions
+ * that are compatible with the new one.
+ */
+ private static final int VERSION = 1;
+
+ private static final String XML_TAG_METADATA = "metadata";
+ private static final String XML_ATTR_ID = "id";
+ private static final String XML_ATTR_VERSION = "version";
+ private static final String XML_TAG_TIMEFRAME = "timeframe";
+ private static final String XML_ATTR_MONOTONIC = "monotonic";
+ private static final String XML_ATTR_START_TIME = "start";
+ private static final String XML_ATTR_DURATION = "duration";
+ private static final String XML_TAG_SECTION = "section";
+ private static final String XML_ATTR_SECTION_TYPE = "type";
+
+ private static final DateTimeFormatter DATE_FORMAT =
+ DateTimeFormatter.ofPattern("MM-dd HH:mm:ss.SSS").withZone(ZoneId.systemDefault());
+
+ static class TimeFrame {
+ public final long startMonotonicTime;
+ @CurrentTimeMillisLong
+ public final long startTime;
+ @DurationMillisLong
+ public final long duration;
+
+ TimeFrame(long startMonotonicTime, @CurrentTimeMillisLong long startTime,
+ @DurationMillisLong long duration) {
+ this.startMonotonicTime = startMonotonicTime;
+ this.startTime = startTime;
+ this.duration = duration;
+ }
+
+ void write(TypedXmlSerializer serializer) throws IOException {
+ serializer.startTag(null, XML_TAG_TIMEFRAME);
+ serializer.attributeLong(null, XML_ATTR_START_TIME, startTime);
+ serializer.attributeLong(null, XML_ATTR_MONOTONIC, startMonotonicTime);
+ serializer.attributeLong(null, XML_ATTR_DURATION, duration);
+ serializer.endTag(null, XML_TAG_TIMEFRAME);
+ }
+
+ static TimeFrame read(TypedXmlPullParser parser) throws XmlPullParserException {
+ return new TimeFrame(
+ parser.getAttributeLong(null, XML_ATTR_MONOTONIC),
+ parser.getAttributeLong(null, XML_ATTR_START_TIME),
+ parser.getAttributeLong(null, XML_ATTR_DURATION));
+ }
+
+ /**
+ * Prints the contents of this TimeFrame.
+ */
+ public void dump(IndentingPrintWriter pw) {
+ StringBuilder sb = new StringBuilder();
+ sb.append(DATE_FORMAT.format(Instant.ofEpochMilli(startTime)))
+ .append(" (monotonic=").append(startMonotonicTime).append(") ")
+ .append(" duration=");
+ String durationString = TimeUtils.formatDuration(duration);
+ if (durationString.startsWith("+")) {
+ sb.append(durationString.substring(1));
+ } else {
+ sb.append(durationString);
+ }
+ pw.print(sb);
+ }
+ }
+
+ static class Metadata {
+ static final Comparator<Metadata> COMPARATOR = Comparator.comparing(Metadata::getId);
+
+ private final long mId;
+ private final List<TimeFrame> mTimeFrames = new ArrayList<>();
+ private final List<String> mSections = new ArrayList<>();
+
+ Metadata(long id) {
+ mId = id;
+ }
+
+ public long getId() {
+ return mId;
+ }
+
+ public List<TimeFrame> getTimeFrames() {
+ return mTimeFrames;
+ }
+
+ public List<String> getSections() {
+ return mSections;
+ }
+
+ void addTimeFrame(TimeFrame timeFrame) {
+ mTimeFrames.add(timeFrame);
+ }
+
+ void addSection(String sectionType) {
+ // The number of sections per span is small, so there is no need to use a Set
+ if (!mSections.contains(sectionType)) {
+ mSections.add(sectionType);
+ }
+ }
+
+ void write(TypedXmlSerializer serializer) throws IOException {
+ serializer.startTag(null, XML_TAG_METADATA);
+ serializer.attributeLong(null, XML_ATTR_ID, mId);
+ serializer.attributeInt(null, XML_ATTR_VERSION, VERSION);
+ for (TimeFrame timeFrame : mTimeFrames) {
+ timeFrame.write(serializer);
+ }
+ for (String section : mSections) {
+ serializer.startTag(null, XML_TAG_SECTION);
+ serializer.attribute(null, XML_ATTR_SECTION_TYPE, section);
+ serializer.endTag(null, XML_TAG_SECTION);
+ }
+ serializer.endTag(null, XML_TAG_METADATA);
+ }
+
+ /**
+ * Reads just the header of the XML file containing metadata.
+ * Returns null if the file does not contain a compatible <metadata> element.
+ */
+ @Nullable
+ public static Metadata read(TypedXmlPullParser parser)
+ throws IOException, XmlPullParserException {
+ Metadata metadata = null;
+ int eventType = parser.getEventType();
+ while (eventType != XmlPullParser.END_DOCUMENT
+ && !(eventType == XmlPullParser.END_TAG
+ && parser.getName().equals(XML_TAG_METADATA))) {
+ if (eventType == XmlPullParser.START_TAG) {
+ String tagName = parser.getName();
+ if (tagName.equals(XML_TAG_METADATA)) {
+ int version = parser.getAttributeInt(null, XML_ATTR_VERSION);
+ if (!isCompatibleXmlFormat(version)) {
+ Slog.e(TAG,
+ "Incompatible version " + version + "; expected " + VERSION);
+ return null;
+ }
+
+ long id = parser.getAttributeLong(null, XML_ATTR_ID);
+ metadata = new Metadata(id);
+ } else if (metadata != null && tagName.equals(XML_TAG_TIMEFRAME)) {
+ metadata.addTimeFrame(TimeFrame.read(parser));
+ } else if (metadata != null && tagName.equals(XML_TAG_SECTION)) {
+ metadata.addSection(parser.getAttributeValue(null, XML_ATTR_SECTION_TYPE));
+ }
+ }
+ eventType = parser.next();
+ }
+ return metadata;
+ }
+
+ /**
+ * Prints the metadata.
+ */
+ public void dump(IndentingPrintWriter pw) {
+ dump(pw, true);
+ }
+
+ void dump(IndentingPrintWriter pw, boolean includeSections) {
+ pw.print("Span ");
+ if (mTimeFrames.size() > 0) {
+ mTimeFrames.get(0).dump(pw);
+ pw.println();
+ }
+
+ // Sometimes, when the wall clock is adjusted in the middle of a stats session,
+ // we will have more than one time frame.
+ for (int i = 1; i < mTimeFrames.size(); i++) {
+ TimeFrame timeFrame = mTimeFrames.get(i);
+ pw.print(" "); // Aligned below "Span "
+ timeFrame.dump(pw);
+ pw.println();
+ }
+
+ if (includeSections) {
+ pw.increaseIndent();
+ for (String section : mSections) {
+ pw.print("section", section);
+ pw.println();
+ }
+ pw.decreaseIndent();
+ }
+ }
+
+ @Override
+ public String toString() {
+ StringWriter sw = new StringWriter();
+ IndentingPrintWriter ipw = new IndentingPrintWriter(sw);
+ ipw.print("id", mId);
+ for (int i = 0; i < mTimeFrames.size(); i++) {
+ TimeFrame timeFrame = mTimeFrames.get(i);
+ ipw.print("timeframe=[");
+ timeFrame.dump(ipw);
+ ipw.print("] ");
+ }
+ for (String section : mSections) {
+ ipw.print("section", section);
+ }
+ ipw.flush();
+ return sw.toString().trim();
+ }
+ }
+
+ /**
+ * Contains a specific type of aggregate power stats. The contents type is determined by
+ * the section type.
+ */
+ public abstract static class Section {
+ private final String mType;
+
+ Section(String type) {
+ mType = type;
+ }
+
+ /**
+ * Returns the section type, which determines the type of data stored in the corresponding
+ * section of {@link PowerStatsSpan}
+ */
+ public String getType() {
+ return mType;
+ }
+
+ abstract void write(TypedXmlSerializer serializer) throws IOException;
+
+ /**
+ * Prints the section type.
+ */
+ public void dump(IndentingPrintWriter ipw) {
+ ipw.println(mType);
+ }
+ }
+
+ /**
+ * A universal XML parser for {@link PowerStatsSpan.Section}'s. It is aware of all
+ * supported section types as well as their corresponding XML formats.
+ */
+ public interface SectionReader {
+ /**
+ * Reads the contents of the section using the parser. The type of the object
+ * read and the corresponding XML format are determined by the section type.
+ */
+ Section read(String sectionType, TypedXmlPullParser parser)
+ throws IOException, XmlPullParserException;
+ }
+
+ private final Metadata mMetadata;
+ private final List<Section> mSections = new ArrayList<>();
+
+ public PowerStatsSpan(long id) {
+ this(new Metadata(id));
+ }
+
+ private PowerStatsSpan(Metadata metadata) {
+ mMetadata = metadata;
+ }
+
+ public Metadata getMetadata() {
+ return mMetadata;
+ }
+
+ public long getId() {
+ return mMetadata.mId;
+ }
+
+ void addTimeFrame(long monotonicTime, @CurrentTimeMillisLong long wallClockTime,
+ @DurationMillisLong long duration) {
+ mMetadata.mTimeFrames.add(new TimeFrame(monotonicTime, wallClockTime, duration));
+ }
+
+ void addSection(Section section) {
+ mMetadata.addSection(section.getType());
+ mSections.add(section);
+ }
+
+ @NonNull
+ public List<Section> getSections() {
+ return mSections;
+ }
+
+ private static boolean isCompatibleXmlFormat(int version) {
+ return version == VERSION;
+ }
+
+ /**
+ * Creates an XML file containing the persistent state of the power stats span.
+ */
+ @VisibleForTesting
+ public void writeXml(OutputStream out, TypedXmlSerializer serializer) throws IOException {
+ serializer.setOutput(out, StandardCharsets.UTF_8.name());
+ serializer.startDocument(null, true);
+ mMetadata.write(serializer);
+ for (Section section : mSections) {
+ serializer.startTag(null, XML_TAG_SECTION);
+ serializer.attribute(null, XML_ATTR_SECTION_TYPE, section.mType);
+ section.write(serializer);
+ serializer.endTag(null, XML_TAG_SECTION);
+ }
+ serializer.endDocument();
+ }
+
+ @Nullable
+ static PowerStatsSpan read(InputStream in, TypedXmlPullParser parser,
+ SectionReader sectionReader, String... sectionTypes)
+ throws IOException, XmlPullParserException {
+ Set<String> neededSections = Sets.newArraySet(sectionTypes);
+ boolean selectSections = !neededSections.isEmpty();
+ parser.setInput(in, StandardCharsets.UTF_8.name());
+
+ Metadata metadata = Metadata.read(parser);
+ if (metadata == null) {
+ return null;
+ }
+
+ PowerStatsSpan span = new PowerStatsSpan(metadata);
+ boolean skipSection = false;
+ int nestingLevel = 0;
+ int eventType = parser.getEventType();
+ while (eventType != XmlPullParser.END_DOCUMENT) {
+ if (skipSection) {
+ if (eventType == XmlPullParser.END_TAG
+ && parser.getName().equals(XML_TAG_SECTION)) {
+ nestingLevel--;
+ if (nestingLevel == 0) {
+ skipSection = false;
+ }
+ } else if (eventType == XmlPullParser.START_TAG
+ && parser.getName().equals(XML_TAG_SECTION)) {
+ nestingLevel++;
+ }
+ } else if (eventType == XmlPullParser.START_TAG) {
+ String tag = parser.getName();
+ if (tag.equals(XML_TAG_SECTION)) {
+ String sectionType = parser.getAttributeValue(null, XML_ATTR_SECTION_TYPE);
+ if (!selectSections || neededSections.contains(sectionType)) {
+ Section section = sectionReader.read(sectionType, parser);
+ if (section == null) {
+ if (selectSections) {
+ throw new XmlPullParserException(
+ "Unsupported PowerStatsStore section type: " + sectionType);
+ } else {
+ section = new Section(sectionType) {
+ @Override
+ public void dump(IndentingPrintWriter ipw) {
+ ipw.println("Unsupported PowerStatsStore section type: "
+ + sectionType);
+ }
+
+ @Override
+ void write(TypedXmlSerializer serializer) {
+ }
+ };
+ }
+ }
+ span.addSection(section);
+ } else {
+ skipSection = true;
+ }
+ } else if (tag.equals(XML_TAG_METADATA)) {
+ Metadata.read(parser);
+ }
+ }
+ eventType = parser.next();
+ }
+ return span;
+ }
+
+ /**
+ * Prints the contents of this power stats span.
+ */
+ public void dump(IndentingPrintWriter ipw) {
+ mMetadata.dump(ipw, /* includeSections */ false);
+ for (Section section : mSections) {
+ ipw.increaseIndent();
+ ipw.println(section.mType);
+ section.dump(ipw);
+ ipw.decreaseIndent();
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/power/stats/PowerStatsStore.java b/services/core/java/com/android/server/power/stats/PowerStatsStore.java
new file mode 100644
index 0000000..7123bcb
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/PowerStatsStore.java
@@ -0,0 +1,371 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.power.stats;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.BatteryUsageStats;
+import android.os.FileUtils;
+import android.os.Handler;
+import android.util.AtomicFile;
+import android.util.IndentingPrintWriter;
+import android.util.Slog;
+import android.util.Xml;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.modules.utils.TypedXmlPullParser;
+
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.channels.FileChannel;
+import java.nio.channels.FileLock;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.StandardOpenOption;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.concurrent.locks.ReentrantLock;
+
+/**
+ * A storage mechanism for aggregated power/battery stats.
+ */
+public class PowerStatsStore {
+ private static final String TAG = "PowerStatsStore";
+
+ private static final String POWER_STATS_DIR = "power-stats";
+ private static final String POWER_STATS_SPAN_FILE_EXTENSION = ".pss";
+ private static final String DIR_LOCK_FILENAME = ".lock";
+ private static final long MAX_POWER_STATS_SPAN_STORAGE_BYTES = 100 * 1024;
+
+ private final File mSystemDir;
+ private final File mStoreDir;
+ private final File mLockFile;
+ private final ReentrantLock mFileLock = new ReentrantLock();
+ private FileLock mJvmLock;
+ private final long mMaxStorageBytes;
+ private final Handler mHandler;
+ private final PowerStatsSpan.SectionReader mSectionReader;
+ private volatile List<PowerStatsSpan.Metadata> mTableOfContents;
+
+ public PowerStatsStore(@NonNull File systemDir, Handler handler,
+ AggregatedPowerStatsConfig aggregatedPowerStatsConfig) {
+ this(systemDir, MAX_POWER_STATS_SPAN_STORAGE_BYTES, handler,
+ new DefaultSectionReader(aggregatedPowerStatsConfig));
+ }
+
+ @VisibleForTesting
+ public PowerStatsStore(@NonNull File systemDir, long maxStorageBytes, Handler handler,
+ @NonNull PowerStatsSpan.SectionReader sectionReader) {
+ mSystemDir = systemDir;
+ mStoreDir = new File(systemDir, POWER_STATS_DIR);
+ mLockFile = new File(mStoreDir, DIR_LOCK_FILENAME);
+ mHandler = handler;
+ mMaxStorageBytes = maxStorageBytes;
+ mSectionReader = sectionReader;
+ mHandler.post(this::maybeClearLegacyStore);
+ }
+
+ /**
+ * Returns the metadata for all {@link PowerStatsSpan}'s contained in the store.
+ */
+ @NonNull
+ public List<PowerStatsSpan.Metadata> getTableOfContents() {
+ List<PowerStatsSpan.Metadata> toc = mTableOfContents;
+ if (toc != null) {
+ return toc;
+ }
+
+ TypedXmlPullParser parser = Xml.newBinaryPullParser();
+ lockStoreDirectory();
+ try {
+ toc = new ArrayList<>();
+ for (File file : mStoreDir.listFiles()) {
+ String fileName = file.getName();
+ if (!fileName.endsWith(POWER_STATS_SPAN_FILE_EXTENSION)) {
+ continue;
+ }
+ try (InputStream inputStream = new BufferedInputStream(new FileInputStream(file))) {
+ parser.setInput(inputStream, StandardCharsets.UTF_8.name());
+ PowerStatsSpan.Metadata metadata = PowerStatsSpan.Metadata.read(parser);
+ if (metadata != null) {
+ toc.add(metadata);
+ } else {
+ Slog.e(TAG, "Removing incompatible PowerStatsSpan file: " + fileName);
+ file.delete();
+ }
+ } catch (IOException | XmlPullParserException e) {
+ Slog.wtf(TAG, "Cannot read PowerStatsSpan file: " + fileName);
+ }
+ }
+ toc.sort(PowerStatsSpan.Metadata.COMPARATOR);
+ mTableOfContents = Collections.unmodifiableList(toc);
+ } finally {
+ unlockStoreDirectory();
+ }
+
+ return toc;
+ }
+
+ /**
+ * Saves the specified span in the store.
+ */
+ public void storePowerStatsSpan(PowerStatsSpan span) {
+ maybeClearLegacyStore();
+ lockStoreDirectory();
+ try {
+ if (!mStoreDir.exists()) {
+ if (!mStoreDir.mkdirs()) {
+ Slog.e(TAG, "Could not create a directory for power stats store");
+ return;
+ }
+ }
+
+ AtomicFile file = new AtomicFile(makePowerStatsSpanFilename(span.getId()));
+ file.write(out-> {
+ try {
+ span.writeXml(out, Xml.newBinarySerializer());
+ } catch (Exception e) {
+ // AtomicFile will log the exception and delete the file.
+ throw new RuntimeException(e);
+ }
+ });
+ mTableOfContents = null;
+ removeOldSpansLocked();
+ } finally {
+ unlockStoreDirectory();
+ }
+ }
+
+ /**
+ * Loads the PowerStatsSpan identified by its ID. Only loads the sections with
+ * the specified types. Loads all sections if no sectionTypes is empty.
+ */
+ @Nullable
+ public PowerStatsSpan loadPowerStatsSpan(long id, String... sectionTypes) {
+ TypedXmlPullParser parser = Xml.newBinaryPullParser();
+ lockStoreDirectory();
+ try {
+ File file = makePowerStatsSpanFilename(id);
+ try (InputStream inputStream = new BufferedInputStream(new FileInputStream(file))) {
+ return PowerStatsSpan.read(inputStream, parser, mSectionReader, sectionTypes);
+ } catch (IOException | XmlPullParserException e) {
+ Slog.wtf(TAG, "Cannot read PowerStatsSpan file: " + file);
+ }
+ } finally {
+ unlockStoreDirectory();
+ }
+ return null;
+ }
+
+ void storeAggregatedPowerStats(AggregatedPowerStats stats) {
+ PowerStatsSpan span = createPowerStatsSpan(stats);
+ if (span == null) {
+ return;
+ }
+ storePowerStatsSpan(span);
+ }
+
+ static PowerStatsSpan createPowerStatsSpan(AggregatedPowerStats stats) {
+ List<AggregatedPowerStats.ClockUpdate> clockUpdates = stats.getClockUpdates();
+ if (clockUpdates.isEmpty()) {
+ Slog.w(TAG, "No clock updates in aggregated power stats " + stats);
+ return null;
+ }
+
+ long monotonicTime = clockUpdates.get(0).monotonicTime;
+ long durationSum = 0;
+ PowerStatsSpan span = new PowerStatsSpan(monotonicTime);
+ for (int i = 0; i < clockUpdates.size(); i++) {
+ AggregatedPowerStats.ClockUpdate clockUpdate = clockUpdates.get(i);
+ long duration;
+ if (i == clockUpdates.size() - 1) {
+ duration = stats.getDuration() - durationSum;
+ } else {
+ duration = clockUpdate.monotonicTime - monotonicTime;
+ }
+ span.addTimeFrame(clockUpdate.monotonicTime, clockUpdate.currentTime, duration);
+ monotonicTime = clockUpdate.monotonicTime;
+ durationSum += duration;
+ }
+
+ span.addSection(new AggregatedPowerStatsSection(stats));
+ return span;
+ }
+
+ /**
+ * Stores a {@link PowerStatsSpan} containing a single section for the supplied
+ * battery usage stats.
+ */
+ public void storeBatteryUsageStats(long monotonicStartTime,
+ BatteryUsageStats batteryUsageStats) {
+ PowerStatsSpan span = new PowerStatsSpan(monotonicStartTime);
+ span.addTimeFrame(monotonicStartTime, batteryUsageStats.getStatsStartTimestamp(),
+ batteryUsageStats.getStatsDuration());
+ span.addSection(new BatteryUsageStatsSection(batteryUsageStats));
+ storePowerStatsSpan(span);
+ }
+
+ /**
+ * Creates a file name by formatting the span ID as a 19-digit zero-padded number.
+ * This ensures that the lexicographically sorted directory follows the chronological order.
+ */
+ private File makePowerStatsSpanFilename(long id) {
+ return new File(mStoreDir, String.format(Locale.ENGLISH, "%019d", id)
+ + POWER_STATS_SPAN_FILE_EXTENSION);
+ }
+
+ private void maybeClearLegacyStore() {
+ File legacyStoreDir = new File(mSystemDir, "battery-usage-stats");
+ if (legacyStoreDir.exists()) {
+ FileUtils.deleteContentsAndDir(legacyStoreDir);
+ }
+ }
+
+ private void lockStoreDirectory() {
+ mFileLock.lock();
+
+ // Lock the directory from access by other JVMs
+ try {
+ mLockFile.getParentFile().mkdirs();
+ mLockFile.createNewFile();
+ mJvmLock = FileChannel.open(mLockFile.toPath(), StandardOpenOption.WRITE).lock();
+ } catch (IOException e) {
+ Slog.e(TAG, "Cannot lock snapshot directory", e);
+ }
+ }
+
+ private void unlockStoreDirectory() {
+ try {
+ mJvmLock.close();
+ } catch (IOException e) {
+ Slog.e(TAG, "Cannot unlock snapshot directory", e);
+ } finally {
+ mFileLock.unlock();
+ }
+ }
+
+ private void removeOldSpansLocked() {
+ // Read the directory list into a _sorted_ map. The alphanumeric ordering
+ // corresponds to the historical order of snapshots because the file names
+ // are timestamps zero-padded to the same length.
+ long totalSize = 0;
+ TreeMap<File, Long> mFileSizes = new TreeMap<>();
+ for (File file : mStoreDir.listFiles()) {
+ final long fileSize = file.length();
+ totalSize += fileSize;
+ if (file.getName().endsWith(POWER_STATS_SPAN_FILE_EXTENSION)) {
+ mFileSizes.put(file, fileSize);
+ }
+ }
+
+ while (totalSize > mMaxStorageBytes) {
+ final Map.Entry<File, Long> entry = mFileSizes.firstEntry();
+ if (entry == null) {
+ break;
+ }
+
+ File file = entry.getKey();
+ if (!file.delete()) {
+ Slog.e(TAG, "Cannot delete power stats span " + file);
+ }
+ totalSize -= entry.getValue();
+ mFileSizes.remove(file);
+ mTableOfContents = null;
+ }
+ }
+
+ /**
+ * Deletes all contents from the store.
+ */
+ public void reset() {
+ lockStoreDirectory();
+ try {
+ for (File file : mStoreDir.listFiles()) {
+ if (file.getName().endsWith(POWER_STATS_SPAN_FILE_EXTENSION)) {
+ if (!file.delete()) {
+ Slog.e(TAG, "Cannot delete power stats span " + file);
+ }
+ }
+ }
+ mTableOfContents = List.of();
+ } finally {
+ unlockStoreDirectory();
+ }
+ }
+
+ /**
+ * Prints the summary of contents of the store: only metadata, but not the actual stored
+ * objects.
+ */
+ public void dumpTableOfContents(IndentingPrintWriter ipw) {
+ ipw.println("Power stats store TOC");
+ ipw.increaseIndent();
+ List<PowerStatsSpan.Metadata> contents = getTableOfContents();
+ for (PowerStatsSpan.Metadata metadata : contents) {
+ metadata.dump(ipw);
+ }
+ ipw.decreaseIndent();
+ }
+
+ /**
+ * Prints the contents of the store.
+ */
+ public void dump(IndentingPrintWriter ipw) {
+ ipw.println("Power stats store");
+ ipw.increaseIndent();
+ List<PowerStatsSpan.Metadata> contents = getTableOfContents();
+ for (PowerStatsSpan.Metadata metadata : contents) {
+ PowerStatsSpan span = loadPowerStatsSpan(metadata.getId());
+ if (span != null) {
+ span.dump(ipw);
+ }
+ }
+ ipw.decreaseIndent();
+ }
+
+ private static class DefaultSectionReader implements PowerStatsSpan.SectionReader {
+ private final AggregatedPowerStatsConfig mAggregatedPowerStatsConfig;
+
+ DefaultSectionReader(AggregatedPowerStatsConfig aggregatedPowerStatsConfig) {
+ mAggregatedPowerStatsConfig = aggregatedPowerStatsConfig;
+ }
+
+ @Override
+ public PowerStatsSpan.Section read(String sectionType, TypedXmlPullParser parser)
+ throws IOException, XmlPullParserException {
+ switch (sectionType) {
+ case AggregatedPowerStatsSection.TYPE:
+ return new AggregatedPowerStatsSection(
+ AggregatedPowerStats.createFromXml(parser,
+ mAggregatedPowerStatsConfig));
+ case BatteryUsageStatsSection.TYPE:
+ return new BatteryUsageStatsSection(
+ BatteryUsageStats.createFromXml(parser));
+ default:
+ return null;
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/power/stats/flags.aconfig b/services/core/java/com/android/server/power/stats/flags.aconfig
index d61bebc..add806f 100644
--- a/services/core/java/com/android/server/power/stats/flags.aconfig
+++ b/services/core/java/com/android/server/power/stats/flags.aconfig
@@ -1,6 +1,13 @@
package: "com.android.server.power.optimization"
flag {
+ name: "power_monitor_api"
+ namespace: "power_optimization"
+ description: "Feature flag for ODPM API"
+ bug: "295027807"
+}
+
+flag {
name: "streamlined_battery_stats"
namespace: "power_optimization"
description: "Feature flag for streamlined battery stats"
diff --git a/services/core/java/com/android/server/powerstats/PowerStatsService.java b/services/core/java/com/android/server/powerstats/PowerStatsService.java
index 5609f69..77290fd 100644
--- a/services/core/java/com/android/server/powerstats/PowerStatsService.java
+++ b/services/core/java/com/android/server/powerstats/PowerStatsService.java
@@ -694,7 +694,7 @@
Log.d(TAG, String.format(Locale.ENGLISH,
"Monitor=%s timestamp=%d energy=%d"
+ " uid=%d noise=%.1f%% returned=%d",
- state.powerMonitor.name,
+ state.powerMonitor.getName(),
state.timestampMs,
state.energyUws,
callingUid,
@@ -728,7 +728,7 @@
}
for (PowerMonitorState powerMonitorState : powerMonitorStates) {
- if (powerMonitorState.powerMonitor.type
+ if (powerMonitorState.powerMonitor.getType()
== PowerMonitor.POWER_MONITOR_TYPE_CONSUMER) {
for (EnergyConsumerResult energyConsumerResult : energyConsumerResults) {
if (energyConsumerResult.id == powerMonitorState.id) {
@@ -754,7 +754,7 @@
}
for (PowerMonitorState powerMonitorState : powerMonitorStates) {
- if (powerMonitorState.powerMonitor.type
+ if (powerMonitorState.powerMonitor.getType()
== PowerMonitor.POWER_MONITOR_TYPE_MEASUREMENT) {
for (EnergyMeasurement energyMeasurement : energyMeasurements) {
if (energyMeasurement.id == powerMonitorState.id) {
@@ -773,7 +773,7 @@
@PowerMonitor.PowerMonitorType int type) {
int count = 0;
for (PowerMonitorState monitorState : powerMonitorStates) {
- if (monitorState.powerMonitor.type == type) {
+ if (monitorState.powerMonitor.getType() == type) {
count++;
}
}
@@ -785,7 +785,7 @@
int[] ids = new int[count];
int index = 0;
for (PowerMonitorState monitorState : powerMonitorStates) {
- if (monitorState.powerMonitor.type == type) {
+ if (monitorState.powerMonitor.getType() == type) {
ids[index++] = monitorState.id;
}
}
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/sensorprivacy/SensorPrivacyService.java b/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java
index c9db343..0656a6a 100644
--- a/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java
+++ b/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java
@@ -174,8 +174,6 @@
private CallStateHelper mCallStateHelper;
private KeyguardManager mKeyguardManager;
- private SafetyCenterManager mSafetyCenterManager;
-
private int mCurrentUser = USER_NULL;
public SensorPrivacyService(Context context) {
@@ -191,7 +189,6 @@
mTelephonyManager = context.getSystemService(TelephonyManager.class);
mPackageManagerInternal = getLocalService(PackageManagerInternal.class);
mSensorPrivacyServiceImpl = new SensorPrivacyServiceImpl();
- mSafetyCenterManager = mContext.getSystemService(SafetyCenterManager.class);
}
@Override
@@ -656,7 +653,9 @@
String contentTitle = getUiContext().getString(messageRes);
Spanned contentText = Html.fromHtml(getUiContext().getString(
R.string.sensor_privacy_start_use_notification_content_text, packageLabel), 0);
- String action = mSafetyCenterManager.isSafetyCenterEnabled()
+ SafetyCenterManager safetyCenterManager =
+ mContext.getSystemService(SafetyCenterManager.class);
+ String action = safetyCenterManager.isSafetyCenterEnabled()
? Settings.ACTION_PRIVACY_CONTROLS : Settings.ACTION_PRIVACY_SETTINGS;
PendingIntent contentIntent = PendingIntent.getActivity(mContext, sensor,
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/stats/OWNERS b/services/core/java/com/android/server/stats/OWNERS
index 174ad3a..c33f3d9 100644
--- a/services/core/java/com/android/server/stats/OWNERS
+++ b/services/core/java/com/android/server/stats/OWNERS
@@ -1,11 +1,10 @@
jeffreyhuang@google.com
joeo@google.com
-jtnguyen@google.com
+monicamwang@google.com
muhammadq@google.com
+rayhdez@google.com
rslawik@google.com
-ruchirr@google.com
sharaienko@google.com
singhtejinder@google.com
tsaichristine@google.com
yaochen@google.com
-yro@google.com
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/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/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..a01113b 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -1316,6 +1316,9 @@
if (mLaunchIntoPipHostActivity != null) {
pw.println(prefix + "launchIntoPipHostActivity=" + mLaunchIntoPipHostActivity);
}
+ if (mWaitForEnteringPinnedMode) {
+ pw.print(prefix); pw.println("mWaitForEnteringPinnedMode=true");
+ }
mLetterboxUiController.dump(pw, prefix);
@@ -3132,9 +3135,7 @@
}
boolean canReceiveKeys() {
- // TODO(156521483): Propagate the state down the hierarchy instead of checking the parent
- return getWindowConfiguration().canReceiveKeys()
- && (task == null || task.getWindowConfiguration().canReceiveKeys());
+ return getWindowConfiguration().canReceiveKeys() && !mWaitForEnteringPinnedMode;
}
boolean isResizeable() {
@@ -5367,18 +5368,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 +8265,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 +8277,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 +9263,6 @@
Slog.w(TAG, errorMessage);
}
- // Configuration's equality doesn't consider seq so if only seq number changes in resolved
- // override configuration. Therefore ConfigurationContainer doesn't change merged override
- // configuration, but it's used to push configuration changes so explicitly update that.
- if (getMergedOverrideConfiguration().seq != getResolvedOverrideConfiguration().seq) {
- onMergedOverrideConfigurationChanged();
- }
-
// Before PiP animation is done, th windowing mode of the activity is still the previous
// mode (see RootWindowContainer#moveActivityToPinnedRootTask). So once the windowing mode
// of activity is changed, it is the signal of the last step to update the PiP states.
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 27315bb..7cccf6b 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -707,7 +707,7 @@
}
}
- int res;
+ int res = START_CANCELED;
synchronized (mService.mGlobalLock) {
final boolean globalConfigWillChange = mRequest.globalConfig != null
&& mService.getGlobalConfiguration().diff(mRequest.globalConfig) != 0;
@@ -719,22 +719,20 @@
+ "will change = %b", globalConfigWillChange);
final long origId = Binder.clearCallingIdentity();
-
- res = resolveToHeavyWeightSwitcherIfNeeded();
- if (res != START_SUCCESS) {
- return res;
- }
-
try {
+ res = resolveToHeavyWeightSwitcherIfNeeded();
+ if (res != START_SUCCESS) {
+ return res;
+ }
+
res = executeRequest(mRequest);
} finally {
+ Binder.restoreCallingIdentity(origId);
mRequest.logMessage.append(" result code=").append(res);
Slog.i(TAG, mRequest.logMessage.toString());
mRequest.logMessage.setLength(0);
}
- Binder.restoreCallingIdentity(origId);
-
if (globalConfigWillChange) {
// If the caller also wants to switch to a new configuration, do so now.
// This allows a clean switch, as we are waiting for the current activity
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 0b67321..de335d3 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -68,6 +68,7 @@
import static android.view.WindowManager.TRANSIT_PIP;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_TO_LAUNCHER_CLEAR_SNAPSHOT;
+import static android.window.TransitionInfo.FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_DREAM;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_FOCUS;
@@ -3686,6 +3687,11 @@
getTransitionController(), mWindowManager.mSyncEngine)
: null;
+ if (r.getTaskFragment() != null && r.getTaskFragment().isEmbeddedWithBoundsOverride()
+ && transition != null) {
+ transition.addFlag(FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY);
+ }
+
final Runnable enterPipRunnable = () -> {
synchronized (mGlobalLock) {
if (r.getParent() == null) {
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index 12e1e2c..777b5cd 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -2832,17 +2832,22 @@
static class OpaqueActivityHelper implements Predicate<ActivityRecord> {
private ActivityRecord mStarting;
private boolean mIncludeInvisibleAndFinishing;
+ private boolean mIgnoringKeyguard;
- ActivityRecord getOpaqueActivity(@NonNull WindowContainer<?> container) {
+ ActivityRecord getOpaqueActivity(
+ @NonNull WindowContainer<?> container, boolean ignoringKeyguard) {
mIncludeInvisibleAndFinishing = true;
+ mIgnoringKeyguard = ignoringKeyguard;
return container.getActivity(this,
true /* traverseTopToBottom */, null /* boundary */);
}
- ActivityRecord getVisibleOpaqueActivity(@NonNull WindowContainer<?> container,
- @Nullable ActivityRecord starting) {
+ ActivityRecord getVisibleOpaqueActivity(
+ @NonNull WindowContainer<?> container, @Nullable ActivityRecord starting,
+ boolean ignoringKeyguard) {
mStarting = starting;
mIncludeInvisibleAndFinishing = false;
+ mIgnoringKeyguard = ignoringKeyguard;
final ActivityRecord opaque = container.getActivity(this,
true /* traverseTopToBottom */, null /* boundary */);
mStarting = null;
@@ -2851,7 +2856,9 @@
@Override
public boolean test(ActivityRecord r) {
- if (!mIncludeInvisibleAndFinishing && !r.visibleIgnoringKeyguard && r != mStarting) {
+ if (!mIncludeInvisibleAndFinishing && r != mStarting
+ && ((mIgnoringKeyguard && !r.visibleIgnoringKeyguard)
+ || (!mIgnoringKeyguard && !r.isVisible()))) {
// Ignore invisible activities that are not the currently starting activity
// (about to be visible).
return false;
diff --git a/services/core/java/com/android/server/wm/AnimationAdapter.java b/services/core/java/com/android/server/wm/AnimationAdapter.java
index b039646..3dc377d 100644
--- a/services/core/java/com/android/server/wm/AnimationAdapter.java
+++ b/services/core/java/com/android/server/wm/AnimationAdapter.java
@@ -22,6 +22,7 @@
import android.view.SurfaceControl.Transaction;
import android.view.animation.Animation;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.server.wm.SurfaceAnimator.AnimationType;
import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
@@ -31,7 +32,8 @@
* Interface that describes an animation and bridges the animation start to the component
* responsible for running the animation.
*/
-interface AnimationAdapter {
+@VisibleForTesting
+public interface AnimationAdapter {
long STATUS_BAR_TRANSITION_DURATION = 120L;
diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java
index 4237668..6d59b29 100644
--- a/services/core/java/com/android/server/wm/BackNavigationController.java
+++ b/services/core/java/com/android/server/wm/BackNavigationController.java
@@ -276,6 +276,10 @@
// activity, we won't close the activity.
backType = BackNavigationInfo.TYPE_DIALOG_CLOSE;
removedWindowContainer = window;
+ } else if (!currentActivity.occludesParent() || currentActivity.showWallpaper()) {
+ // skip if current activity is translucent
+ backType = BackNavigationInfo.TYPE_CALLBACK;
+ removedWindowContainer = window;
} else if (prevActivity != null) {
if (!isOccluded || prevActivity.canShowWhenLocked()) {
// We have another Activity in the same currentTask to go to
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/Dimmer.java b/services/core/java/com/android/server/wm/Dimmer.java
index ae29afa..64a230e 100644
--- a/services/core/java/com/android/server/wm/Dimmer.java
+++ b/services/core/java/com/android/server/wm/Dimmer.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -11,172 +11,36 @@
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT 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.server.wm;
-import static com.android.server.wm.AlphaAnimationSpecProto.DURATION_MS;
-import static com.android.server.wm.AlphaAnimationSpecProto.FROM;
-import static com.android.server.wm.AlphaAnimationSpecProto.TO;
-import static com.android.server.wm.AnimationSpecProto.ALPHA;
-import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_DIMMER;
-
import android.annotation.NonNull;
import android.graphics.Rect;
-import android.util.Log;
-import android.util.proto.ProtoOutputStream;
-import android.view.Surface;
import android.view.SurfaceControl;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.wm.SurfaceAnimator.AnimationType;
-
-import java.io.PrintWriter;
+import com.android.window.flags.Flags;
/**
* Utility class for use by a WindowContainer implementation to add "DimLayer" support, that is
* black layers of varying opacity at various Z-levels which create the effect of a Dim.
*/
-class Dimmer {
- private static final String TAG = "WindowManager";
- // This is in milliseconds.
- private static final int DEFAULT_DIM_ANIM_DURATION = 200;
-
- private class DimAnimatable implements SurfaceAnimator.Animatable {
- private SurfaceControl mDimLayer;
-
- private DimAnimatable(SurfaceControl dimLayer) {
- mDimLayer = dimLayer;
- }
-
- @Override
- public SurfaceControl.Transaction getSyncTransaction() {
- return mHost.getSyncTransaction();
- }
-
- @Override
- public SurfaceControl.Transaction getPendingTransaction() {
- return mHost.getPendingTransaction();
- }
-
- @Override
- public void commitPendingTransaction() {
- mHost.commitPendingTransaction();
- }
-
- @Override
- public void onAnimationLeashCreated(SurfaceControl.Transaction t, SurfaceControl leash) {
- }
-
- @Override
- public void onAnimationLeashLost(SurfaceControl.Transaction t) {
- }
-
- @Override
- public SurfaceControl.Builder makeAnimationLeash() {
- return mHost.makeAnimationLeash();
- }
-
- @Override
- public SurfaceControl getAnimationLeashParent() {
- return mHost.getSurfaceControl();
- }
-
- @Override
- public SurfaceControl getSurfaceControl() {
- return mDimLayer;
- }
-
- @Override
- public SurfaceControl getParentSurfaceControl() {
- return mHost.getSurfaceControl();
- }
-
- @Override
- public int getSurfaceWidth() {
- // This will determine the size of the leash created. This should be the size of the
- // host and not the dim layer since the dim layer may get bigger during animation. If
- // that occurs, the leash size cannot change so we need to ensure the leash is big
- // enough that the dim layer can grow.
- // This works because the mHost will be a Task which has the display bounds.
- return mHost.getSurfaceWidth();
- }
-
- @Override
- public int getSurfaceHeight() {
- // See getSurfaceWidth() above for explanation.
- return mHost.getSurfaceHeight();
- }
-
- void removeSurface() {
- if (mDimLayer != null && mDimLayer.isValid()) {
- getSyncTransaction().remove(mDimLayer);
- }
- mDimLayer = null;
- }
- }
-
- @VisibleForTesting
- class DimState {
- /**
- * The layer where property changes should be invoked on.
- */
- SurfaceControl mDimLayer;
- boolean mDimming;
- boolean isVisible;
- SurfaceAnimator mSurfaceAnimator;
-
- // TODO(b/64816140): Remove after confirming dimmer layer always matches its container.
- final Rect mDimBounds = new Rect();
-
- /**
- * Determines whether the dim layer should animate before destroying.
- */
- boolean mAnimateExit = true;
-
- /**
- * Used for Dims not associated with a WindowContainer. See {@link Dimmer#dimAbove} for
- * details on Dim lifecycle.
- */
- boolean mDontReset;
-
- DimState(SurfaceControl dimLayer) {
- mDimLayer = dimLayer;
- mDimming = true;
- final DimAnimatable dimAnimatable = new DimAnimatable(dimLayer);
- mSurfaceAnimator = new SurfaceAnimator(dimAnimatable, (type, anim) -> {
- if (!mDimming) {
- dimAnimatable.removeSurface();
- }
- }, mHost.mWmService);
- }
- }
-
+public abstract class Dimmer {
/**
- * The {@link WindowContainer} that our Dim's are bounded to. We may be dimming on behalf of the
+ * The {@link WindowContainer} that our Dims are bounded to. We may be dimming on behalf of the
* host, some controller of it, or one of the hosts children.
*/
- private WindowContainer mHost;
- private WindowContainer mLastRequestedDimContainer;
- @VisibleForTesting
- DimState mDimState;
+ protected final WindowContainer mHost;
- private final SurfaceAnimatorStarter mSurfaceAnimatorStarter;
-
- @VisibleForTesting
- interface SurfaceAnimatorStarter {
- void startAnimation(SurfaceAnimator surfaceAnimator, SurfaceControl.Transaction t,
- AnimationAdapter anim, boolean hidden, @AnimationType int type);
- }
-
- Dimmer(WindowContainer host) {
- this(host, SurfaceAnimator::startAnimation);
- }
-
- Dimmer(WindowContainer host, SurfaceAnimatorStarter surfaceAnimatorStarter) {
+ protected Dimmer(WindowContainer host) {
mHost = host;
- mSurfaceAnimatorStarter = surfaceAnimatorStarter;
+ }
+
+ // Constructs the correct type of dimmer
+ static Dimmer create(WindowContainer host) {
+ return Flags.dimmerRefactor() ? new SmoothDimmer(host) : new LegacyDimmer(host);
}
@NonNull
@@ -184,49 +48,8 @@
return mHost;
}
- private SurfaceControl makeDimLayer() {
- return mHost.makeChildSurface(null)
- .setParent(mHost.getSurfaceControl())
- .setColorLayer()
- .setName("Dim Layer for - " + mHost.getName())
- .setCallsite("Dimmer.makeDimLayer")
- .build();
- }
-
- /**
- * Retrieve the DimState, creating one if it doesn't exist.
- */
- private DimState getDimState(WindowContainer container) {
- if (mDimState == null) {
- try {
- final SurfaceControl ctl = makeDimLayer();
- mDimState = new DimState(ctl);
- } catch (Surface.OutOfResourcesException e) {
- Log.w(TAG, "OutOfResourcesException creating dim surface");
- }
- }
-
- mLastRequestedDimContainer = container;
- return mDimState;
- }
-
- private void dim(WindowContainer container, int relativeLayer, float alpha, int blurRadius) {
- final DimState d = getDimState(container);
-
- if (d == null) {
- return;
- }
-
- // The dim method is called from WindowState.prepareSurfaces(), which is always called
- // in the correct Z from lowest Z to highest. This ensures that the dim layer is always
- // relative to the highest Z layer with a dim.
- SurfaceControl.Transaction t = mHost.getPendingTransaction();
- t.setRelativeLayer(d.mDimLayer, container.getSurfaceControl(), relativeLayer);
- t.setAlpha(d.mDimLayer, alpha);
- t.setBackgroundBlurRadius(d.mDimLayer, blurRadius);
-
- d.mDimming = true;
- }
+ protected abstract void dim(
+ WindowContainer container, int relativeLayer, float alpha, int blurRadius);
/**
* Place a dim above the given container, which should be a child of the host container.
@@ -260,25 +83,15 @@
* chain {@link WindowContainer#prepareSurfaces} down to it's children to give them
* a chance to request dims to continue.
*/
- void resetDimStates() {
- if (mDimState == null) {
- return;
- }
- if (!mDimState.mDontReset) {
- mDimState.mDimming = false;
- }
- }
+ abstract void resetDimStates();
/** Returns non-null bounds if the dimmer is showing. */
- Rect getDimBounds() {
- return mDimState != null ? mDimState.mDimBounds : null;
- }
+ abstract Rect getDimBounds();
- void dontAnimateExit() {
- if (mDimState != null) {
- mDimState.mAnimateExit = false;
- }
- }
+ abstract void dontAnimateExit();
+
+ @VisibleForTesting
+ abstract SurfaceControl getDimLayer();
/**
* Call after invoking {@link WindowContainer#prepareSurfaces} on children as
@@ -288,109 +101,5 @@
* @param t A transaction in which to update the dims.
* @return true if any Dims were updated.
*/
- boolean updateDims(SurfaceControl.Transaction t) {
- if (mDimState == null) {
- return false;
- }
-
- if (!mDimState.mDimming) {
- if (!mDimState.mAnimateExit) {
- if (mDimState.mDimLayer.isValid()) {
- t.remove(mDimState.mDimLayer);
- }
- } else {
- startDimExit(mLastRequestedDimContainer, mDimState.mSurfaceAnimator, t);
- }
- mDimState = null;
- return false;
- } else {
- final Rect bounds = mDimState.mDimBounds;
- // TODO: Once we use geometry from hierarchy this falls away.
- t.setPosition(mDimState.mDimLayer, bounds.left, bounds.top);
- t.setWindowCrop(mDimState.mDimLayer, bounds.width(), bounds.height());
- if (!mDimState.isVisible) {
- mDimState.isVisible = true;
- t.show(mDimState.mDimLayer);
- // Skip enter animation while starting window is on top of its activity
- final WindowState ws = mLastRequestedDimContainer.asWindowState();
- if (ws == null || ws.mActivityRecord == null
- || ws.mActivityRecord.mStartingData == null) {
- startDimEnter(mLastRequestedDimContainer, mDimState.mSurfaceAnimator, t);
- }
- }
- return true;
- }
- }
-
- private void startDimEnter(WindowContainer container, SurfaceAnimator animator,
- SurfaceControl.Transaction t) {
- startAnim(container, animator, t, 0 /* startAlpha */, 1 /* endAlpha */);
- }
-
- private void startDimExit(WindowContainer container, SurfaceAnimator animator,
- SurfaceControl.Transaction t) {
- startAnim(container, animator, t, 1 /* startAlpha */, 0 /* endAlpha */);
- }
-
- private void startAnim(WindowContainer container, SurfaceAnimator animator,
- SurfaceControl.Transaction t, float startAlpha, float endAlpha) {
- mSurfaceAnimatorStarter.startAnimation(animator, t, new LocalAnimationAdapter(
- new AlphaAnimationSpec(startAlpha, endAlpha, getDimDuration(container)),
- mHost.mWmService.mSurfaceAnimationRunner), false /* hidden */,
- ANIMATION_TYPE_DIMMER);
- }
-
- private long getDimDuration(WindowContainer container) {
- // If there's no container, then there isn't an animation occurring while dimming. Set the
- // duration to 0 so it immediately dims to the set alpha.
- if (container == null) {
- return 0;
- }
-
- // Otherwise use the same duration as the animation on the WindowContainer
- AnimationAdapter animationAdapter = container.mSurfaceAnimator.getAnimation();
- final float durationScale = container.mWmService.getTransitionAnimationScaleLocked();
- return animationAdapter == null ? (long) (DEFAULT_DIM_ANIM_DURATION * durationScale)
- : animationAdapter.getDurationHint();
- }
-
- private static class AlphaAnimationSpec implements LocalAnimationAdapter.AnimationSpec {
- private final long mDuration;
- private final float mFromAlpha;
- private final float mToAlpha;
-
- AlphaAnimationSpec(float fromAlpha, float toAlpha, long duration) {
- mFromAlpha = fromAlpha;
- mToAlpha = toAlpha;
- mDuration = duration;
- }
-
- @Override
- public long getDuration() {
- return mDuration;
- }
-
- @Override
- public void apply(SurfaceControl.Transaction t, SurfaceControl sc, long currentPlayTime) {
- final float fraction = getFraction(currentPlayTime);
- final float alpha = fraction * (mToAlpha - mFromAlpha) + mFromAlpha;
- t.setAlpha(sc, alpha);
- }
-
- @Override
- public void dump(PrintWriter pw, String prefix) {
- pw.print(prefix); pw.print("from="); pw.print(mFromAlpha);
- pw.print(" to="); pw.print(mToAlpha);
- pw.print(" duration="); pw.println(mDuration);
- }
-
- @Override
- public void dumpDebugInner(ProtoOutputStream proto) {
- final long token = proto.start(ALPHA);
- proto.write(FROM, mFromAlpha);
- proto.write(TO, mToAlpha);
- proto.write(DURATION_MS, mDuration);
- proto.end(token);
- }
- }
+ abstract boolean updateDims(SurfaceControl.Transaction t);
}
diff --git a/services/core/java/com/android/server/wm/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java
index f81e5d4..f51bf7f 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;
@@ -53,7 +54,6 @@
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
-
/**
* Container for grouping WindowContainer below DisplayContent.
*
@@ -79,6 +79,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 +588,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 +622,7 @@
super.onConfigurationChanged(newParentConfig);
if (mOrganizer != null && getConfiguration().diff(mTmpConfiguration) != 0) {
- mOrganizerController.onDisplayAreaInfoChanged(mOrganizer, this);
+ sendDisplayAreaInfoChanged();
}
}
@@ -766,7 +785,7 @@
* DisplayArea that can be dimmed.
*/
static class Dimmable extends DisplayArea<DisplayArea> {
- private final Dimmer mDimmer = new Dimmer(this);
+ private final Dimmer mDimmer = Dimmer.create(this);
Dimmable(WindowManagerService wms, Type type, String name, int featureId) {
super(wms, type, name, featureId);
diff --git a/services/core/java/com/android/server/wm/InputConsumerImpl.java b/services/core/java/com/android/server/wm/InputConsumerImpl.java
index c21930d..1fa7d2a 100644
--- a/services/core/java/com/android/server/wm/InputConsumerImpl.java
+++ b/services/core/java/com/android/server/wm/InputConsumerImpl.java
@@ -51,7 +51,8 @@
private final Rect mOldWindowCrop = new Rect();
InputConsumerImpl(WindowManagerService service, IBinder token, String name,
- InputChannel inputChannel, int clientPid, UserHandle clientUser, int displayId) {
+ InputChannel inputChannel, int clientPid, UserHandle clientUser, int displayId,
+ SurfaceControl.Transaction t) {
mService = service;
mToken = token;
mName = name;
@@ -82,6 +83,7 @@
.setName("Input Consumer " + name)
.setCallsite("InputConsumerImpl")
.build();
+ mWindowHandle.setTrustedOverlay(t, mInputSurface, true);
}
void linkToDeathRecipient() {
@@ -129,14 +131,12 @@
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 af307ec3..5c0bc28 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -224,7 +224,7 @@
}
final InputConsumerImpl consumer = new InputConsumerImpl(mService, token, name,
- inputChannel, clientPid, clientUser, mDisplayId);
+ inputChannel, clientPid, clientUser, mDisplayId, mInputTransaction);
switch (name) {
case INPUT_CONSUMER_WALLPAPER:
consumer.mWindowHandle.inputConfig |= InputConfig.DUPLICATE_TOUCH_TO_WALLPAPER;
@@ -675,11 +675,6 @@
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);
diff --git a/services/core/java/com/android/server/wm/LegacyDimmer.java b/services/core/java/com/android/server/wm/LegacyDimmer.java
new file mode 100644
index 0000000..ccf956e
--- /dev/null
+++ b/services/core/java/com/android/server/wm/LegacyDimmer.java
@@ -0,0 +1,341 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 com.android.server.wm.AlphaAnimationSpecProto.DURATION_MS;
+import static com.android.server.wm.AlphaAnimationSpecProto.FROM;
+import static com.android.server.wm.AlphaAnimationSpecProto.TO;
+import static com.android.server.wm.AnimationSpecProto.ALPHA;
+import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_DIMMER;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+
+import android.graphics.Rect;
+import android.util.Log;
+import android.util.proto.ProtoOutputStream;
+import android.view.Surface;
+import android.view.SurfaceControl;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.io.PrintWriter;
+
+public class LegacyDimmer extends Dimmer {
+ private static final String TAG = TAG_WITH_CLASS_NAME ? "Dimmer" : TAG_WM;
+ // This is in milliseconds.
+ private static final int DEFAULT_DIM_ANIM_DURATION = 200;
+ DimState mDimState;
+ private WindowContainer mLastRequestedDimContainer;
+ private final SurfaceAnimatorStarter mSurfaceAnimatorStarter;
+
+ private class DimAnimatable implements SurfaceAnimator.Animatable {
+ private SurfaceControl mDimLayer;
+
+ private DimAnimatable(SurfaceControl dimLayer) {
+ mDimLayer = dimLayer;
+ }
+
+ @Override
+ public SurfaceControl.Transaction getSyncTransaction() {
+ return mHost.getSyncTransaction();
+ }
+
+ @Override
+ public SurfaceControl.Transaction getPendingTransaction() {
+ return mHost.getPendingTransaction();
+ }
+
+ @Override
+ public void commitPendingTransaction() {
+ mHost.commitPendingTransaction();
+ }
+
+ @Override
+ public void onAnimationLeashCreated(SurfaceControl.Transaction t, SurfaceControl leash) {
+ }
+
+ @Override
+ public void onAnimationLeashLost(SurfaceControl.Transaction t) {
+ }
+
+ @Override
+ public SurfaceControl.Builder makeAnimationLeash() {
+ return mHost.makeAnimationLeash();
+ }
+
+ @Override
+ public SurfaceControl getAnimationLeashParent() {
+ return mHost.getSurfaceControl();
+ }
+
+ @Override
+ public SurfaceControl getSurfaceControl() {
+ return mDimLayer;
+ }
+
+ @Override
+ public SurfaceControl getParentSurfaceControl() {
+ return mHost.getSurfaceControl();
+ }
+
+ @Override
+ public int getSurfaceWidth() {
+ // This will determine the size of the leash created. This should be the size of the
+ // host and not the dim layer since the dim layer may get bigger during animation. If
+ // that occurs, the leash size cannot change so we need to ensure the leash is big
+ // enough that the dim layer can grow.
+ // This works because the mHost will be a Task which has the display bounds.
+ return mHost.getSurfaceWidth();
+ }
+
+ @Override
+ public int getSurfaceHeight() {
+ // See getSurfaceWidth() above for explanation.
+ return mHost.getSurfaceHeight();
+ }
+
+ void removeSurface() {
+ if (mDimLayer != null && mDimLayer.isValid()) {
+ getSyncTransaction().remove(mDimLayer);
+ }
+ mDimLayer = null;
+ }
+ }
+
+ @VisibleForTesting
+ class DimState {
+ /**
+ * The layer where property changes should be invoked on.
+ */
+ SurfaceControl mDimLayer;
+ boolean mDimming;
+ boolean mIsVisible;
+
+ // TODO(b/64816140): Remove after confirming dimmer layer always matches its container.
+ final Rect mDimBounds = new Rect();
+
+ /**
+ * Determines whether the dim layer should animate before destroying.
+ */
+ boolean mAnimateExit = true;
+
+ /**
+ * Used for Dims not associated with a WindowContainer. See {@link Dimmer#dimAbove} for
+ * details on Dim lifecycle.
+ */
+ boolean mDontReset;
+ SurfaceAnimator mSurfaceAnimator;
+
+ DimState(SurfaceControl dimLayer) {
+ mDimLayer = dimLayer;
+ mDimming = true;
+ final DimAnimatable dimAnimatable = new DimAnimatable(dimLayer);
+ mSurfaceAnimator = new SurfaceAnimator(dimAnimatable, (type, anim) -> {
+ if (!mDimming) {
+ dimAnimatable.removeSurface();
+ }
+ }, mHost.mWmService);
+ }
+ }
+
+ @VisibleForTesting
+ interface SurfaceAnimatorStarter {
+ void startAnimation(SurfaceAnimator surfaceAnimator, SurfaceControl.Transaction t,
+ AnimationAdapter anim, boolean hidden, @SurfaceAnimator.AnimationType int type);
+ }
+
+ protected LegacyDimmer(WindowContainer host) {
+ this(host, SurfaceAnimator::startAnimation);
+ }
+
+ LegacyDimmer(WindowContainer host, SurfaceAnimatorStarter surfaceAnimatorStarter) {
+ super(host);
+ mSurfaceAnimatorStarter = surfaceAnimatorStarter;
+ }
+
+ private DimState obtainDimState(WindowContainer container) {
+ if (mDimState == null) {
+ try {
+ final SurfaceControl ctl = makeDimLayer();
+ mDimState = new DimState(ctl);
+ } catch (Surface.OutOfResourcesException e) {
+ Log.w(TAG, "OutOfResourcesException creating dim surface");
+ }
+ }
+
+ mLastRequestedDimContainer = container;
+ return mDimState;
+ }
+
+ private SurfaceControl makeDimLayer() {
+ return mHost.makeChildSurface(null)
+ .setParent(mHost.getSurfaceControl())
+ .setColorLayer()
+ .setName("Dim Layer for - " + mHost.getName())
+ .setCallsite("Dimmer.makeDimLayer")
+ .build();
+ }
+
+ @Override
+ SurfaceControl getDimLayer() {
+ return mDimState != null ? mDimState.mDimLayer : null;
+ }
+
+ @Override
+ void resetDimStates() {
+ if (mDimState == null) {
+ return;
+ }
+ if (!mDimState.mDontReset) {
+ mDimState.mDimming = false;
+ }
+ }
+
+ @Override
+ Rect getDimBounds() {
+ return mDimState != null ? mDimState.mDimBounds : null;
+ }
+
+ @Override
+ void dontAnimateExit() {
+ if (mDimState != null) {
+ mDimState.mAnimateExit = false;
+ }
+ }
+
+ @Override
+ protected void dim(WindowContainer container, int relativeLayer, float alpha, int blurRadius) {
+ final DimState d = obtainDimState(container);
+
+ if (d == null) {
+ return;
+ }
+
+ // The dim method is called from WindowState.prepareSurfaces(), which is always called
+ // in the correct Z from lowest Z to highest. This ensures that the dim layer is always
+ // relative to the highest Z layer with a dim.
+ SurfaceControl.Transaction t = mHost.getPendingTransaction();
+ t.setRelativeLayer(d.mDimLayer, container.getSurfaceControl(), relativeLayer);
+ t.setAlpha(d.mDimLayer, alpha);
+ t.setBackgroundBlurRadius(d.mDimLayer, blurRadius);
+
+ d.mDimming = true;
+ }
+
+ @Override
+ boolean updateDims(SurfaceControl.Transaction t) {
+ if (mDimState == null) {
+ return false;
+ }
+
+ if (!mDimState.mDimming) {
+ if (!mDimState.mAnimateExit) {
+ if (mDimState.mDimLayer.isValid()) {
+ t.remove(mDimState.mDimLayer);
+ }
+ } else {
+ startDimExit(mLastRequestedDimContainer,
+ mDimState.mSurfaceAnimator, t);
+ }
+ mDimState = null;
+ return false;
+ } else {
+ final Rect bounds = mDimState.mDimBounds;
+ // TODO: Once we use geometry from hierarchy this falls away.
+ t.setPosition(mDimState.mDimLayer, bounds.left, bounds.top);
+ t.setWindowCrop(mDimState.mDimLayer, bounds.width(), bounds.height());
+ if (!mDimState.mIsVisible) {
+ mDimState.mIsVisible = true;
+ t.show(mDimState.mDimLayer);
+ // Skip enter animation while starting window is on top of its activity
+ final WindowState ws = mLastRequestedDimContainer.asWindowState();
+ if (ws == null || ws.mActivityRecord == null
+ || ws.mActivityRecord.mStartingData == null) {
+ startDimEnter(mLastRequestedDimContainer,
+ mDimState.mSurfaceAnimator, t);
+ }
+ }
+ return true;
+ }
+ }
+
+ private long getDimDuration(WindowContainer container) {
+ // Use the same duration as the animation on the WindowContainer
+ AnimationAdapter animationAdapter = container.mSurfaceAnimator.getAnimation();
+ final float durationScale = container.mWmService.getTransitionAnimationScaleLocked();
+ return animationAdapter == null ? (long) (DEFAULT_DIM_ANIM_DURATION * durationScale)
+ : animationAdapter.getDurationHint();
+ }
+
+ private void startDimEnter(WindowContainer container, SurfaceAnimator animator,
+ SurfaceControl.Transaction t) {
+ startAnim(container, animator, t, 0 /* startAlpha */, 1 /* endAlpha */);
+ }
+
+ private void startDimExit(WindowContainer container, SurfaceAnimator animator,
+ SurfaceControl.Transaction t) {
+ startAnim(container, animator, t, 1 /* startAlpha */, 0 /* endAlpha */);
+ }
+
+ private void startAnim(WindowContainer container, SurfaceAnimator animator,
+ SurfaceControl.Transaction t, float startAlpha, float endAlpha) {
+ mSurfaceAnimatorStarter.startAnimation(animator, t, new LocalAnimationAdapter(
+ new AlphaAnimationSpec(startAlpha, endAlpha, getDimDuration(container)),
+ mHost.mWmService.mSurfaceAnimationRunner), false /* hidden */,
+ ANIMATION_TYPE_DIMMER);
+ }
+
+ private static class AlphaAnimationSpec implements LocalAnimationAdapter.AnimationSpec {
+ private final long mDuration;
+ private final float mFromAlpha;
+ private final float mToAlpha;
+
+ AlphaAnimationSpec(float fromAlpha, float toAlpha, long duration) {
+ mFromAlpha = fromAlpha;
+ mToAlpha = toAlpha;
+ mDuration = duration;
+ }
+
+ @Override
+ public long getDuration() {
+ return mDuration;
+ }
+
+ @Override
+ public void apply(SurfaceControl.Transaction t, SurfaceControl sc, long currentPlayTime) {
+ final float fraction = getFraction(currentPlayTime);
+ final float alpha = fraction * (mToAlpha - mFromAlpha) + mFromAlpha;
+ t.setAlpha(sc, alpha);
+ }
+
+ @Override
+ public void dump(PrintWriter pw, String prefix) {
+ pw.print(prefix); pw.print("from="); pw.print(mFromAlpha);
+ pw.print(" to="); pw.print(mToAlpha);
+ pw.print(" duration="); pw.println(mDuration);
+ }
+
+ @Override
+ public void dumpDebugInner(ProtoOutputStream proto) {
+ final long token = proto.start(ALPHA);
+ proto.write(FROM, mFromAlpha);
+ proto.write(TO, mToAlpha);
+ proto.write(DURATION_MS, mDuration);
+ proto.end(token);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/wm/OWNERS b/services/core/java/com/android/server/wm/OWNERS
index 458786f..f6c3640 100644
--- a/services/core/java/com/android/server/wm/OWNERS
+++ b/services/core/java/com/android/server/wm/OWNERS
@@ -18,4 +18,4 @@
yunfanc@google.com
per-file BackgroundActivityStartController.java = set noparent
-per-file BackgroundActivityStartController.java = brufino@google.com, ogunwale@google.com, louischang@google.com, lus@google.com
\ No newline at end of file
+per-file BackgroundActivityStartController.java = brufino@google.com, topjohnwu@google.com, achim@google.com, ogunwale@google.com, louischang@google.com, lus@google.com
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 cf6a1fe..7a442e7 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -2211,6 +2211,10 @@
mService.mTaskFragmentOrganizerController.dispatchPendingInfoChangedEvent(
organizedTf);
}
+
+ if (taskDisplayArea.getFocusedRootTask() == rootTask) {
+ taskDisplayArea.clearPreferredTopFocusableRootTask();
+ }
} finally {
mService.continueWindowLayout();
try {
diff --git a/services/core/java/com/android/server/wm/SmoothDimmer.java b/services/core/java/com/android/server/wm/SmoothDimmer.java
new file mode 100644
index 0000000..6ddbd2c
--- /dev/null
+++ b/services/core/java/com/android/server/wm/SmoothDimmer.java
@@ -0,0 +1,395 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_DIMMER;
+import static com.android.server.wm.AlphaAnimationSpecProto.DURATION_MS;
+import static com.android.server.wm.AlphaAnimationSpecProto.FROM;
+import static com.android.server.wm.AlphaAnimationSpecProto.TO;
+import static com.android.server.wm.AnimationSpecProto.ALPHA;
+import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_DIMMER;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+
+import android.graphics.Rect;
+import android.util.Log;
+import android.util.proto.ProtoOutputStream;
+import android.view.Surface;
+import android.view.SurfaceControl;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.protolog.common.ProtoLog;
+
+import java.io.PrintWriter;
+
+class SmoothDimmer extends Dimmer {
+ private static final String TAG = TAG_WITH_CLASS_NAME ? "Dimmer" : TAG_WM;
+ private static final float EPSILON = 0.0001f;
+ // This is in milliseconds.
+ private static final int DEFAULT_DIM_ANIM_DURATION = 200;
+ DimState mDimState;
+ private WindowContainer mLastRequestedDimContainer;
+ private final AnimationAdapterFactory mAnimationAdapterFactory;
+
+ @VisibleForTesting
+ class DimState {
+ /**
+ * The layer where property changes should be invoked on.
+ */
+ SurfaceControl mDimLayer;
+ boolean mDimming;
+ boolean mIsVisible;
+
+ // TODO(b/64816140): Remove after confirming dimmer layer always matches its container.
+ final Rect mDimBounds = new Rect();
+
+ /**
+ * Determines whether the dim layer should animate before destroying.
+ */
+ boolean mAnimateExit = true;
+
+ /**
+ * Used for Dims not associated with a WindowContainer. See {@link Dimmer#dimAbove} for
+ * details on Dim lifecycle.
+ */
+ boolean mDontReset;
+
+ Change mCurrentProperties;
+ Change mRequestedProperties;
+ private AnimationSpec mAlphaAnimationSpec;
+ private AnimationAdapter mLocalAnimationAdapter;
+
+ static class Change {
+ private float mAlpha = -1f;
+ private int mBlurRadius = -1;
+ private WindowContainer mDimmingContainer = null;
+ private int mRelativeLayer = -1;
+ private boolean mSkipAnimation = false;
+
+ Change() {}
+
+ Change(Change other) {
+ mAlpha = other.mAlpha;
+ mBlurRadius = other.mBlurRadius;
+ mDimmingContainer = other.mDimmingContainer;
+ mRelativeLayer = other.mRelativeLayer;
+ }
+
+ @Override
+ public String toString() {
+ return "Dim state: alpha=" + mAlpha + ", blur=" + mBlurRadius + ", container="
+ + mDimmingContainer + ", relativePosition=" + mRelativeLayer
+ + ", skipAnimation=" + mSkipAnimation;
+ }
+ }
+
+ DimState(SurfaceControl dimLayer) {
+ mDimLayer = dimLayer;
+ mDimming = true;
+ mCurrentProperties = new Change();
+ mRequestedProperties = new Change();
+ }
+
+ void setExitParameters(WindowContainer container) {
+ setRequestedParameters(container, -1, 0, 0);
+ }
+ // Sets a requested change without applying it immediately
+ void setRequestedParameters(WindowContainer container, int relativeLayer, float alpha,
+ int blurRadius) {
+ mRequestedProperties.mDimmingContainer = container;
+ mRequestedProperties.mRelativeLayer = relativeLayer;
+ mRequestedProperties.mAlpha = alpha;
+ mRequestedProperties.mBlurRadius = blurRadius;
+ }
+
+ /**
+ * Commit the last changes we received. Called after
+ * {@link Change#setRequestedParameters(WindowContainer, int, float, int)}
+ */
+ void applyChanges(SurfaceControl.Transaction t) {
+ if (mRequestedProperties.mDimmingContainer.mSurfaceControl == null) {
+ Log.w(TAG, "container " + mRequestedProperties.mDimmingContainer
+ + "does not have a surface");
+ return;
+ }
+ if (!mDimState.mIsVisible) {
+ mDimState.mIsVisible = true;
+ t.show(mDimState.mDimLayer);
+ }
+ t.setRelativeLayer(mDimLayer,
+ mRequestedProperties.mDimmingContainer.getSurfaceControl(),
+ mRequestedProperties.mRelativeLayer);
+
+ if (aspectChanged()) {
+ if (isAnimating()) {
+ mLocalAnimationAdapter.onAnimationCancelled(mDimLayer);
+ }
+ if (mRequestedProperties.mSkipAnimation
+ || (!dimmingContainerChanged() && mDimming)) {
+ // If the dimming container has not changed, then it is running its own
+ // animation, thus we can directly set the values we get requested, unless it's
+ // the exiting animation
+ ProtoLog.d(WM_DEBUG_DIMMER,
+ "Dim %s skipping animation and directly setting alpha=%f, blur=%d",
+ mDimLayer, mRequestedProperties.mAlpha,
+ mRequestedProperties.mBlurRadius);
+ t.setAlpha(mDimLayer, mRequestedProperties.mAlpha);
+ t.setBackgroundBlurRadius(mDimLayer, mRequestedProperties.mBlurRadius);
+ mRequestedProperties.mSkipAnimation = false;
+ } else {
+ startAnimation(t);
+ }
+ }
+ mCurrentProperties = new Change(mRequestedProperties);
+ }
+
+ private void startAnimation(SurfaceControl.Transaction t) {
+ mAlphaAnimationSpec = getRequestedAnimationSpec(mRequestedProperties.mAlpha,
+ mRequestedProperties.mBlurRadius);
+ mLocalAnimationAdapter = mAnimationAdapterFactory.get(mAlphaAnimationSpec,
+ mHost.mWmService.mSurfaceAnimationRunner);
+
+ mLocalAnimationAdapter.startAnimation(mDimLayer, t,
+ ANIMATION_TYPE_DIMMER, (type, animator) -> {
+ t.setAlpha(mDimLayer, mRequestedProperties.mAlpha);
+ t.setBackgroundBlurRadius(mDimLayer, mRequestedProperties.mBlurRadius);
+ if (mRequestedProperties.mAlpha == 0f && !mDimming) {
+ ProtoLog.d(WM_DEBUG_DIMMER,
+ "Removing dim surface %s on transaction %s", mDimLayer, t);
+ t.remove(mDimLayer);
+ }
+ mLocalAnimationAdapter = null;
+ mAlphaAnimationSpec = null;
+ });
+ }
+
+ private boolean isAnimating() {
+ return mAlphaAnimationSpec != null;
+ }
+
+ private boolean aspectChanged() {
+ return Math.abs(mRequestedProperties.mAlpha - mCurrentProperties.mAlpha) > EPSILON
+ || mRequestedProperties.mBlurRadius != mCurrentProperties.mBlurRadius;
+ }
+
+ private boolean dimmingContainerChanged() {
+ return mRequestedProperties.mDimmingContainer != mCurrentProperties.mDimmingContainer;
+ }
+
+ private AnimationSpec getRequestedAnimationSpec(float targetAlpha, int targetBlur) {
+ final float startAlpha;
+ final int startBlur;
+ if (mAlphaAnimationSpec != null) {
+ startAlpha = mAlphaAnimationSpec.mCurrentAlpha;
+ startBlur = mAlphaAnimationSpec.mCurrentBlur;
+ } else {
+ startAlpha = Math.max(mCurrentProperties.mAlpha, 0f);
+ startBlur = Math.max(mCurrentProperties.mBlurRadius, 0);
+ }
+ long duration = (long) (getDimDuration(mRequestedProperties.mDimmingContainer)
+ * Math.abs(targetAlpha - startAlpha));
+
+ ProtoLog.v(WM_DEBUG_DIMMER, "Starting animation on dim layer %s, requested by %s, "
+ + "alpha: %f -> %f, blur: %d -> %d",
+ mDimLayer, mRequestedProperties.mDimmingContainer, startAlpha, targetAlpha,
+ startBlur, targetBlur);
+ return new AnimationSpec(
+ new AnimationExtremes<>(startAlpha, targetAlpha),
+ new AnimationExtremes<>(startBlur, targetBlur),
+ duration
+ );
+ }
+ }
+
+ protected SmoothDimmer(WindowContainer host) {
+ this(host, new AnimationAdapterFactory());
+ }
+
+ @VisibleForTesting
+ SmoothDimmer(WindowContainer host, AnimationAdapterFactory animationFactory) {
+ super(host);
+ mAnimationAdapterFactory = animationFactory;
+ }
+
+ private DimState obtainDimState(WindowContainer container) {
+ if (mDimState == null) {
+ try {
+ final SurfaceControl ctl = makeDimLayer();
+ mDimState = new DimState(ctl);
+ } catch (Surface.OutOfResourcesException e) {
+ Log.w(TAG, "OutOfResourcesException creating dim surface");
+ }
+ }
+
+ mLastRequestedDimContainer = container;
+ return mDimState;
+ }
+
+ private SurfaceControl makeDimLayer() {
+ return mHost.makeChildSurface(null)
+ .setParent(mHost.getSurfaceControl())
+ .setColorLayer()
+ .setName("Dim Layer for - " + mHost.getName())
+ .setCallsite("Dimmer.makeDimLayer")
+ .build();
+ }
+
+ @Override
+ SurfaceControl getDimLayer() {
+ return mDimState != null ? mDimState.mDimLayer : null;
+ }
+
+ @Override
+ void resetDimStates() {
+ if (mDimState == null) {
+ return;
+ }
+ if (!mDimState.mDontReset) {
+ mDimState.mDimming = false;
+ }
+ }
+
+ @Override
+ Rect getDimBounds() {
+ return mDimState != null ? mDimState.mDimBounds : null;
+ }
+
+ @Override
+ void dontAnimateExit() {
+ if (mDimState != null) {
+ mDimState.mAnimateExit = false;
+ }
+ }
+
+ @Override
+ protected void dim(WindowContainer container, int relativeLayer, float alpha, int blurRadius) {
+ final DimState d = obtainDimState(container);
+
+ mDimState.mRequestedProperties.mDimmingContainer = container;
+ mDimState.setRequestedParameters(container, relativeLayer, alpha, blurRadius);
+ d.mDimming = true;
+ }
+
+ boolean updateDims(SurfaceControl.Transaction t) {
+ if (mDimState == null) {
+ return false;
+ }
+
+ if (!mDimState.mDimming) {
+ // No one is dimming anymore, fade out dim and remove
+ if (!mDimState.mAnimateExit) {
+ if (mDimState.mDimLayer.isValid()) {
+ t.remove(mDimState.mDimLayer);
+ }
+ } else {
+ mDimState.setExitParameters(
+ mDimState.mRequestedProperties.mDimmingContainer);
+ mDimState.applyChanges(t);
+ }
+ mDimState = null;
+ return false;
+ }
+ final Rect bounds = mDimState.mDimBounds;
+ // TODO: Once we use geometry from hierarchy this falls away.
+ t.setPosition(mDimState.mDimLayer, bounds.left, bounds.top);
+ t.setWindowCrop(mDimState.mDimLayer, bounds.width(), bounds.height());
+ // Skip enter animation while starting window is on top of its activity
+ final WindowState ws = mLastRequestedDimContainer.asWindowState();
+ if (!mDimState.mIsVisible && ws != null && ws.mActivityRecord != null
+ && ws.mActivityRecord.mStartingData != null) {
+ mDimState.mRequestedProperties.mSkipAnimation = true;
+ }
+ mDimState.applyChanges(t);
+ return true;
+ }
+
+ private long getDimDuration(WindowContainer container) {
+ // Use the same duration as the animation on the WindowContainer
+ AnimationAdapter animationAdapter = container.mSurfaceAnimator.getAnimation();
+ final float durationScale = container.mWmService.getTransitionAnimationScaleLocked();
+ return animationAdapter == null ? (long) (DEFAULT_DIM_ANIM_DURATION * durationScale)
+ : animationAdapter.getDurationHint();
+ }
+
+ private static class AnimationExtremes<T> {
+ final T mStartValue;
+ final T mFinishValue;
+
+ AnimationExtremes(T fromValue, T toValue) {
+ mStartValue = fromValue;
+ mFinishValue = toValue;
+ }
+ }
+
+ private static class AnimationSpec implements LocalAnimationAdapter.AnimationSpec {
+ private final long mDuration;
+ private final AnimationExtremes<Float> mAlpha;
+ private final AnimationExtremes<Integer> mBlur;
+
+ float mCurrentAlpha = 0;
+ int mCurrentBlur = 0;
+
+ AnimationSpec(AnimationExtremes<Float> alpha,
+ AnimationExtremes<Integer> blur, long duration) {
+ mAlpha = alpha;
+ mBlur = blur;
+ mDuration = duration;
+ }
+
+ @Override
+ public long getDuration() {
+ return mDuration;
+ }
+
+ @Override
+ public void apply(SurfaceControl.Transaction t, SurfaceControl sc, long currentPlayTime) {
+ final float fraction = getFraction(currentPlayTime);
+ mCurrentAlpha =
+ fraction * (mAlpha.mFinishValue - mAlpha.mStartValue) + mAlpha.mStartValue;
+ mCurrentBlur =
+ (int) fraction * (mBlur.mFinishValue - mBlur.mStartValue) + mBlur.mStartValue;
+ t.setAlpha(sc, mCurrentAlpha);
+ t.setBackgroundBlurRadius(sc, mCurrentBlur);
+ }
+
+ @Override
+ public void dump(PrintWriter pw, String prefix) {
+ pw.print(prefix); pw.print("from_alpha="); pw.print(mAlpha.mStartValue);
+ pw.print(" to_alpha="); pw.print(mAlpha.mFinishValue);
+ pw.print(prefix); pw.print("from_blur="); pw.print(mBlur.mStartValue);
+ pw.print(" to_blur="); pw.print(mBlur.mFinishValue);
+ pw.print(" duration="); pw.println(mDuration);
+ }
+
+ @Override
+ public void dumpDebugInner(ProtoOutputStream proto) {
+ final long token = proto.start(ALPHA);
+ proto.write(FROM, mAlpha.mStartValue);
+ proto.write(TO, mAlpha.mFinishValue);
+ proto.write(DURATION_MS, mDuration);
+ proto.end(token);
+ }
+ }
+
+ static class AnimationAdapterFactory {
+
+ public AnimationAdapter get(LocalAnimationAdapter.AnimationSpec alphaAnimationSpec,
+ SurfaceAnimationRunner runner) {
+ return new LocalAnimationAdapter(alphaAnimationSpec, runner);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/wm/SurfaceAnimator.java b/services/core/java/com/android/server/wm/SurfaceAnimator.java
index 408ea6e..c3de4d5 100644
--- a/services/core/java/com/android/server/wm/SurfaceAnimator.java
+++ b/services/core/java/com/android/server/wm/SurfaceAnimator.java
@@ -49,7 +49,8 @@
* {@link AnimationAdapter}. When the animation is done animating, our callback to finish the
* animation will be invoked, at which we reparent the children back to the original parent.
*/
-class SurfaceAnimator {
+@VisibleForTesting
+public class SurfaceAnimator {
private static final String TAG = TAG_WITH_CLASS_NAME ? "SurfaceAnimator" : TAG_WM;
@@ -617,7 +618,8 @@
* Callback to be passed into {@link AnimationAdapter#startAnimation} to be invoked by the
* component that is running the animation when the animation is finished.
*/
- interface OnAnimationFinishedCallback {
+ @VisibleForTesting
+ public interface OnAnimationFinishedCallback {
void onAnimationFinished(@AnimationType int type, AnimationAdapter anim);
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 8385615..4922e90 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -199,6 +199,7 @@
import com.android.server.am.ActivityManagerService;
import com.android.server.am.AppTimeTracker;
import com.android.server.uri.NeededUriGrants;
+import com.android.window.flags.Flags;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -488,10 +489,6 @@
private boolean mForceShowForAllUsers;
- /** When set, will force the task to report as invisible. */
- static final int FLAG_FORCE_HIDDEN_FOR_PINNED_TASK = 1;
- static final int FLAG_FORCE_HIDDEN_FOR_TASK_ORG = 1 << 1;
- private int mForceHiddenFlags = 0;
private boolean mForceTranslucent = false;
// The display category name for this task.
@@ -2858,7 +2855,8 @@
}
/** Bounds of the task to be used for dimming, as well as touch related tests. */
- void getDimBounds(Rect out) {
+ @Override
+ void getDimBounds(@NonNull Rect out) {
if (isRootTask()) {
getBounds(out);
return;
@@ -3306,7 +3304,8 @@
// Once at the root task level, we want to check {@link #isTranslucent(ActivityRecord)}.
// If true, we want to get the Dimmer from the level above since we don't want to animate
// the dim with the Task.
- if (!isRootTask() || isTranslucent(null)) {
+ if (!isRootTask() || (Flags.dimmerRefactor() && isTranslucentAndVisible())
+ || isTranslucent(null)) {
return super.getDimmer();
}
@@ -4492,20 +4491,13 @@
* Sets/unsets the forced-hidden state flag for this task depending on {@param set}.
* @return Whether the force hidden state changed
*/
- boolean setForceHidden(int flags, boolean set) {
- int newFlags = mForceHiddenFlags;
- if (set) {
- newFlags |= flags;
- } else {
- newFlags &= ~flags;
- }
- if (mForceHiddenFlags == newFlags) {
- return false;
- }
-
+ @Override
+ boolean setForceHidden(@FlagForceHidden int flags, boolean set) {
final boolean wasHidden = isForceHidden();
final boolean wasVisible = isVisible();
- mForceHiddenFlags = newFlags;
+ if (!super.setForceHidden(flags, set)) {
+ return false;
+ }
final boolean nowHidden = isForceHidden();
if (wasHidden != nowHidden) {
final String reason = "setForceHidden";
@@ -4536,11 +4528,6 @@
return super.isAlwaysOnTop();
}
- @Override
- protected boolean isForceHidden() {
- return mForceHiddenFlags != 0;
- }
-
boolean isForceHiddenForPinnedTask() {
return (mForceHiddenFlags & FLAG_FORCE_HIDDEN_FOR_PINNED_TASK) != 0;
}
@@ -5648,6 +5635,8 @@
if (noAnimation) {
mDisplayContent.prepareAppTransition(TRANSIT_NONE);
mTaskSupervisor.mNoAnimActivities.add(top);
+ mTransitionController.collect(top);
+ mTransitionController.setNoAnimation(top);
ActivityOptions.abort(options);
} else {
updateTransitLocked(TRANSIT_TO_FRONT, options);
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index 50bc825..906b3b5 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -103,6 +103,7 @@
import com.android.internal.protolog.common.ProtoLog;
import com.android.server.am.HostingRecord;
import com.android.server.pm.pkg.AndroidPackage;
+import com.android.window.flags.Flags;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -209,7 +210,26 @@
*/
int mMinHeight;
- Dimmer mDimmer = new Dimmer(this);
+ Dimmer mDimmer = Flags.dimmerRefactor()
+ ? new SmoothDimmer(this) : new LegacyDimmer(this);
+
+ /** Apply the dim layer on the embedded TaskFragment. */
+ static final int EMBEDDED_DIM_AREA_TASK_FRAGMENT = 0;
+
+ /** Apply the dim layer on the parent Task for an embedded TaskFragment. */
+ static final int EMBEDDED_DIM_AREA_PARENT_TASK = 1;
+
+ /**
+ * The type of dim layer area for an embedded TaskFragment.
+ */
+ @IntDef(prefix = {"EMBEDDED_DIM_AREA_"}, value = {
+ EMBEDDED_DIM_AREA_TASK_FRAGMENT,
+ EMBEDDED_DIM_AREA_PARENT_TASK,
+ })
+ @interface EmbeddedDimArea {}
+
+ @EmbeddedDimArea
+ private int mEmbeddedDimArea = EMBEDDED_DIM_AREA_TASK_FRAGMENT;
/** This task fragment will be removed when the cleanup of its children are done. */
private boolean mIsRemovalRequested;
@@ -342,6 +362,19 @@
*/
private boolean mIsolatedNav;
+ /** When set, will force the task to report as invisible. */
+ static final int FLAG_FORCE_HIDDEN_FOR_PINNED_TASK = 1;
+ static final int FLAG_FORCE_HIDDEN_FOR_TASK_ORG = 1 << 1;
+ static final int FLAG_FORCE_HIDDEN_FOR_TASK_FRAGMENT_ORG = 1 << 2;
+
+ @IntDef(prefix = {"FLAG_FORCE_HIDDEN_"}, value = {
+ FLAG_FORCE_HIDDEN_FOR_PINNED_TASK,
+ FLAG_FORCE_HIDDEN_FOR_TASK_ORG,
+ FLAG_FORCE_HIDDEN_FOR_TASK_FRAGMENT_ORG,
+ }, flag = true)
+ @interface FlagForceHidden {}
+ protected int mForceHiddenFlags = 0;
+
final Point mLastSurfaceSize = new Point();
private final Rect mTmpBounds = new Rect();
@@ -825,7 +858,25 @@
* Returns whether this TaskFragment is currently forced to be hidden for any reason.
*/
protected boolean isForceHidden() {
- return false;
+ return mForceHiddenFlags != 0;
+ }
+
+ /**
+ * Sets/unsets the forced-hidden state flag for this task depending on {@param set}.
+ * @return Whether the force hidden state changed
+ */
+ boolean setForceHidden(@FlagForceHidden int flags, boolean set) {
+ int newFlags = mForceHiddenFlags;
+ if (set) {
+ newFlags |= flags;
+ } else {
+ newFlags &= ~flags;
+ }
+ if (mForceHiddenFlags == newFlags) {
+ return false;
+ }
+ mForceHiddenFlags = newFlags;
+ return true;
}
protected boolean isForceTranslucent() {
@@ -969,7 +1020,7 @@
// A TaskFragment isn't translucent if it has at least one visible activity that occludes
// this TaskFragment.
return mTaskSupervisor.mOpaqueActivityHelper.getVisibleOpaqueActivity(this,
- starting) == null;
+ starting, true /* ignoringKeyguard */) == null;
}
/**
@@ -982,7 +1033,20 @@
return true;
}
// Including finishing Activity if the TaskFragment is becoming invisible in the transition.
- return mTaskSupervisor.mOpaqueActivityHelper.getOpaqueActivity(this) == null;
+ return mTaskSupervisor.mOpaqueActivityHelper.getOpaqueActivity(this,
+ true /* ignoringKeyguard */) == null;
+ }
+
+ /**
+ * Like {@link #isTranslucent(ActivityRecord)} but evaluating the actual visibility of the
+ * windows rather than their visibility ignoring keyguard.
+ */
+ boolean isTranslucentAndVisible() {
+ if (!isAttached() || isForceHidden() || isForceTranslucent()) {
+ return true;
+ }
+ return mTaskSupervisor.mOpaqueActivityHelper.getVisibleOpaqueActivity(this, null,
+ false /* ignoringKeyguard */) == null;
}
ActivityRecord getTopNonFinishingActivity() {
@@ -2929,14 +2993,27 @@
@Override
Dimmer getDimmer() {
- // If the window is in an embedded TaskFragment, we want to dim at the TaskFragment.
- if (asTask() == null) {
+ // If this is in an embedded TaskFragment and we want the dim applies on the TaskFragment.
+ if (mIsEmbedded && mEmbeddedDimArea == EMBEDDED_DIM_AREA_TASK_FRAGMENT) {
return mDimmer;
}
return super.getDimmer();
}
+ /** Bounds to be used for dimming, as well as touch related tests. */
+ void getDimBounds(@NonNull Rect out) {
+ if (mIsEmbedded && mEmbeddedDimArea == EMBEDDED_DIM_AREA_PARENT_TASK) {
+ out.set(getTask().getBounds());
+ } else {
+ out.set(getBounds());
+ }
+ }
+
+ void setEmbeddedDimArea(@EmbeddedDimArea int embeddedDimArea) {
+ mEmbeddedDimArea = embeddedDimArea;
+ }
+
@Override
void prepareSurfaces() {
if (asTask() != null) {
diff --git a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
index 04164c2..ff766be 100644
--- a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
@@ -51,7 +51,6 @@
import android.window.ITaskFragmentOrganizerController;
import android.window.TaskFragmentInfo;
import android.window.TaskFragmentOperation;
-import android.window.TaskFragmentOrganizerToken;
import android.window.TaskFragmentParentInfo;
import android.window.TaskFragmentTransaction;
import android.window.WindowContainerTransaction;
@@ -745,9 +744,9 @@
}
}
- boolean isSystemOrganizer(@NonNull TaskFragmentOrganizerToken token) {
+ boolean isSystemOrganizer(@NonNull IBinder organizerToken) {
final TaskFragmentOrganizerState state =
- mTaskFragmentOrganizerState.get(token.asBinder());
+ mTaskFragmentOrganizerState.get(organizerToken);
return state != null && state.mIsSystemOrganizer;
}
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 882104a..7d65c61 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -418,8 +418,8 @@
if (transientRoot == null) continue;
final WindowContainer<?> rootParent = transientRoot.getParent();
if (rootParent == null || rootParent.getTopChild() == transientRoot) continue;
- final ActivityRecord topOpaque = mController.mAtm.mTaskSupervisor
- .mOpaqueActivityHelper.getOpaqueActivity(rootParent);
+ final ActivityRecord topOpaque = mController.mAtm.mTaskSupervisor.mOpaqueActivityHelper
+ .getOpaqueActivity(rootParent, true /* ignoringKeyguard */);
if (transientRoot.compareTo(topOpaque.getRootTask()) < 0) {
occludedCount++;
}
@@ -703,6 +703,7 @@
if (dc == null || mTargetDisplays.contains(dc)) return;
mTargetDisplays.add(dc);
addOnTopTasks(dc, mOnTopTasksStart);
+ mController.startPerfHintForDisplay(dc.mDisplayId);
}
/**
@@ -1391,7 +1392,6 @@
dc.handleCompleteDeferredRemoval();
}
validateKeyguardOcclusion();
- validateVisibility();
mState = STATE_FINISHED;
// Rotation change may be deferred while there is a display change transition, so check
@@ -2766,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
@@ -3344,8 +3321,8 @@
mFrozen.add(wc);
final ChangeInfo changeInfo = Objects.requireNonNull(mChanges.get(wc));
changeInfo.mSnapshot = snapshotSurface;
- if (isDisplayRotation) {
- // This isn't cheap, so only do it for display rotations.
+ if (changeInfo.mRotation != wc.mDisplayContent.getRotation()) {
+ // This isn't cheap, so only do it for rotation change.
changeInfo.mSnapshotLuma = TransitionAnimation.getBorderLuma(
buffer, screenshotBuffer.getColorSpace());
}
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 c9107e8..9663f3a 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -304,6 +304,7 @@
import android.window.ISurfaceSyncGroupCompletedListener;
import android.window.ITaskFpsCallback;
import android.window.ScreenCapture;
+import android.window.SystemPerformanceHinter;
import android.window.TaskSnapshot;
import android.window.WindowContainerToken;
import android.window.WindowContextInfo;
@@ -547,6 +548,8 @@
@VisibleForTesting
WindowManagerPolicy mPolicy;
+ final WindowManagerFlags mFlags;
+
final IActivityManager mActivityManager;
final ActivityManagerInternal mAmInternal;
final UserManagerInternal mUmInternal;
@@ -1036,6 +1039,8 @@
sThreadPriorityBooster.reset();
}
+ SystemPerformanceHinter mSystemPerformanceHinter;
+
void openSurfaceTransaction() {
try {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "openSurfaceTransaction");
@@ -1153,6 +1158,7 @@
mGlobalLock = atm.getGlobalLock();
mAtmService = atm;
mContext = context;
+ mFlags = new WindowManagerFlags();
mIsPc = mContext.getPackageManager().hasSystemFeature(FEATURE_PC);
mAllowBootMessages = showBootMsgs;
mLimitedAlphaCompositing = context.getResources().getBoolean(
@@ -1329,6 +1335,13 @@
mBlurController = new BlurController(mContext, mPowerManager);
mTaskFpsCallbackController = new TaskFpsCallbackController(mContext);
mAccessibilityController = new AccessibilityController(this);
+ mSystemPerformanceHinter = new SystemPerformanceHinter(mContext, displayId -> {
+ synchronized (mGlobalLock) {
+ DisplayContent dc = mRoot.getDisplayContent(displayId);
+ return (dc == null) ? null : dc.getSurfaceControl();
+ }
+
+ }, mTransactionFactory);
}
DisplayAreaPolicy.Provider getDisplayAreaPolicyProvider() {
@@ -2248,6 +2261,7 @@
}
}
+ final boolean wasTrustedOverlay = win.isWindowTrustedOverlay();
flagChanges = win.mAttrs.flags ^ attrs.flags;
privateFlagChanges = win.mAttrs.privateFlags ^ attrs.privateFlags;
attrChanges = win.mAttrs.copyFrom(attrs);
@@ -2260,6 +2274,9 @@
if (layoutChanged && win.providesDisplayDecorInsets()) {
configChanged = displayPolicy.updateDecorInsetsInfo();
}
+ if (wasTrustedOverlay != win.isWindowTrustedOverlay()) {
+ win.updateTrustedOverlay();
+ }
if (win.mActivityRecord != null && ((flagChanges & FLAG_SHOW_WHEN_LOCKED) != 0
|| (flagChanges & FLAG_DISMISS_KEYGUARD) != 0)) {
win.mActivityRecord.checkKeyguardFlagsChanged();
@@ -3101,10 +3118,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);
}
}
@@ -5281,7 +5303,11 @@
public void displayReady() {
synchronized (mGlobalLock) {
if (mMaxUiWidth > 0) {
- mRoot.forAllDisplays(displayContent -> displayContent.setMaxUiWidth(mMaxUiWidth));
+ mRoot.forAllDisplays(dc -> {
+ if (dc.mDisplay.getType() == Display.TYPE_INTERNAL) {
+ dc.setMaxUiWidth(mMaxUiWidth);
+ }
+ });
}
applyForcedPropertiesForDefaultDisplay();
mAnimator.ready();
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index dd9a88f..5ed6caf 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -24,7 +24,9 @@
import static android.window.TaskFragmentOperation.OP_TYPE_CLEAR_ADJACENT_TASK_FRAGMENTS;
import static android.window.TaskFragmentOperation.OP_TYPE_CREATE_TASK_FRAGMENT;
import static android.window.TaskFragmentOperation.OP_TYPE_DELETE_TASK_FRAGMENT;
+import static android.window.TaskFragmentOperation.OP_TYPE_REORDER_TO_BOTTOM_OF_TASK;
import static android.window.TaskFragmentOperation.OP_TYPE_REORDER_TO_FRONT;
+import static android.window.TaskFragmentOperation.OP_TYPE_REORDER_TO_TOP_OF_TASK;
import static android.window.TaskFragmentOperation.OP_TYPE_REPARENT_ACTIVITY_TO_TASK_FRAGMENT;
import static android.window.TaskFragmentOperation.OP_TYPE_REQUEST_FOCUS_ON_TASK_FRAGMENT;
import static android.window.TaskFragmentOperation.OP_TYPE_SET_ADJACENT_TASK_FRAGMENTS;
@@ -34,6 +36,8 @@
import static android.window.TaskFragmentOperation.OP_TYPE_SET_RELATIVE_BOUNDS;
import static android.window.TaskFragmentOperation.OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT;
import static android.window.TaskFragmentOperation.OP_TYPE_UNKNOWN;
+import static android.window.WindowContainerTransaction.Change.CHANGE_FOCUSABLE;
+import static android.window.WindowContainerTransaction.Change.CHANGE_HIDDEN;
import static android.window.WindowContainerTransaction.Change.CHANGE_RELATIVE_BOUNDS;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_ADD_INSETS_FRAME_PROVIDER;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_ADD_TASK_FRAGMENT_OPERATION;
@@ -61,6 +65,7 @@
import static com.android.server.wm.Task.FLAG_FORCE_HIDDEN_FOR_PINNED_TASK;
import static com.android.server.wm.Task.FLAG_FORCE_HIDDEN_FOR_TASK_ORG;
import static com.android.server.wm.TaskFragment.EMBEDDING_ALLOWED;
+import static com.android.server.wm.TaskFragment.FLAG_FORCE_HIDDEN_FOR_TASK_FRAGMENT_ORG;
import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
import static com.android.server.wm.WindowContainer.POSITION_TOP;
@@ -821,6 +826,7 @@
return TRANSACT_EFFECTS_NONE;
}
+ int effects = TRANSACT_EFFECTS_NONE;
// When the TaskFragment is resized, we may want to create a change transition for it, for
// which we want to defer the surface update until we determine whether or not to start
// change transition.
@@ -843,7 +849,14 @@
c.getConfiguration().windowConfiguration.setBounds(absBounds);
taskFragment.setRelativeEmbeddedBounds(relBounds);
}
- final int effects = applyChanges(taskFragment, c);
+ if ((c.getChangeMask() & WindowContainerTransaction.Change.CHANGE_HIDDEN) != 0) {
+ if (taskFragment.setForceHidden(
+ FLAG_FORCE_HIDDEN_FOR_TASK_FRAGMENT_ORG, c.getHidden())) {
+ effects |= TRANSACT_EFFECTS_LIFECYCLE;
+ }
+ }
+ effects |= applyChanges(taskFragment, c);
+
if (taskFragment.shouldStartChangeTransition(mTmpBounds0, mTmpBounds1)) {
taskFragment.initializeChangeTransition(mTmpBounds0);
}
@@ -1393,6 +1406,24 @@
taskFragment.setIsolatedNav(isolatedNav);
break;
}
+ case OP_TYPE_REORDER_TO_BOTTOM_OF_TASK: {
+ final Task task = taskFragment.getTask();
+ if (task != null) {
+ task.mChildren.remove(taskFragment);
+ task.mChildren.add(0, taskFragment);
+ effects |= TRANSACT_EFFECTS_LIFECYCLE;
+ }
+ break;
+ }
+ case OP_TYPE_REORDER_TO_TOP_OF_TASK: {
+ final Task task = taskFragment.getTask();
+ if (task != null) {
+ task.mChildren.remove(taskFragment);
+ task.mChildren.add(taskFragment);
+ effects |= TRANSACT_EFFECTS_LIFECYCLE;
+ }
+ break;
+ }
}
return effects;
}
@@ -1420,6 +1451,18 @@
return false;
}
+ if ((opType == OP_TYPE_REORDER_TO_BOTTOM_OF_TASK
+ || opType == OP_TYPE_REORDER_TO_TOP_OF_TASK)
+ && !mTaskFragmentOrganizerController.isSystemOrganizer(organizer.asBinder())) {
+ final Throwable exception = new SecurityException(
+ "Only a system organizer can perform OP_TYPE_REORDER_TO_BOTTOM_OF_TASK or "
+ + "OP_TYPE_REORDER_TO_TOP_OF_TASK."
+ );
+ sendTaskFragmentOperationFailure(organizer, errorCallbackToken, taskFragment,
+ opType, exception);
+ return false;
+ }
+
final IBinder secondaryFragmentToken = operation.getSecondaryFragmentToken();
return secondaryFragmentToken == null
|| validateTaskFragment(mLaunchTaskFragments.get(secondaryFragmentToken), opType,
@@ -1920,6 +1963,11 @@
* For config change on {@link TaskFragment}, we only support the following operations:
* {@link WindowContainerTransaction#setRelativeBounds(WindowContainerToken, Rect)},
* {@link WindowContainerTransaction#setWindowingMode(WindowContainerToken, int)}.
+ *
+ * For a system organizer, we additionally support
+ * {@link WindowContainerTransaction#setHidden(WindowContainerToken, boolean)}, and
+ * {@link WindowContainerTransaction#setFocusable(WindowContainerToken, boolean)}. See
+ * {@link TaskFragmentOrganizerController#registerOrganizer(ITaskFragmentOrganizer, boolean)}
*/
private void enforceTaskFragmentConfigChangeAllowed(@NonNull String func,
@Nullable WindowContainer wc, @NonNull WindowContainerTransaction.Change change,
@@ -1938,31 +1986,49 @@
throw new SecurityException(msg);
}
- final int changeMask = change.getChangeMask();
- final int configSetMask = change.getConfigSetMask();
- final int windowSetMask = change.getWindowSetMask();
- if (changeMask == 0 && configSetMask == 0 && windowSetMask == 0
- && change.getWindowingMode() >= 0) {
- // The change contains only setWindowingMode, which is allowed.
- return;
+ final int originalChangeMask = change.getChangeMask();
+ final int originalConfigSetMask = change.getConfigSetMask();
+ final int originalWindowSetMask = change.getWindowSetMask();
+
+ int changeMaskToBeChecked = originalChangeMask;
+ int configSetMaskToBeChecked = originalConfigSetMask;
+ int windowSetMaskToBeChecked = originalWindowSetMask;
+
+ if (mTaskFragmentOrganizerController.isSystemOrganizer(organizer.asBinder())) {
+ // System organizer is allowed to update the hidden and focusable state.
+ // We unset the CHANGE_HIDDEN and CHANGE_FOCUSABLE bits because they are checked here.
+ changeMaskToBeChecked &= ~CHANGE_HIDDEN;
+ changeMaskToBeChecked &= ~CHANGE_FOCUSABLE;
}
- if (changeMask != CHANGE_RELATIVE_BOUNDS
- || configSetMask != ActivityInfo.CONFIG_WINDOW_CONFIGURATION
- || windowSetMask != WindowConfiguration.WINDOW_CONFIG_BOUNDS) {
- // None of the change should be requested from a TaskFragment organizer except
- // setRelativeBounds and setWindowingMode.
- // For setRelativeBounds, we don't need to check whether it is outside of the Task
+
+ // setRelativeBounds is allowed.
+ if ((changeMaskToBeChecked & CHANGE_RELATIVE_BOUNDS) != 0
+ && (configSetMaskToBeChecked & ActivityInfo.CONFIG_WINDOW_CONFIGURATION) != 0
+ && (windowSetMaskToBeChecked & WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0) {
+ // For setRelativeBounds, we don't need to check whether it is outside the Task
// bounds, because it is possible that the Task is also resizing, for which we don't
// want to throw an exception. The bounds will be adjusted in
// TaskFragment#translateRelativeBoundsToAbsoluteBounds.
- String msg = "Permission Denial: " + func + " from pid="
- + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
- + " trying to apply changes of changeMask=" + changeMask
- + " configSetMask=" + configSetMask + " windowSetMask=" + windowSetMask
- + " to TaskFragment=" + tf + " TaskFragmentOrganizer=" + organizer;
- Slog.w(TAG, msg);
- throw new SecurityException(msg);
+ changeMaskToBeChecked &= ~CHANGE_RELATIVE_BOUNDS;
+ configSetMaskToBeChecked &= ~ActivityInfo.CONFIG_WINDOW_CONFIGURATION;
+ windowSetMaskToBeChecked &= ~WindowConfiguration.WINDOW_CONFIG_BOUNDS;
}
+
+ if (changeMaskToBeChecked == 0 && configSetMaskToBeChecked == 0
+ && windowSetMaskToBeChecked == 0) {
+ // All the changes have been checked.
+ // Note that setWindowingMode is always allowed, so we don't need to check the mask.
+ return;
+ }
+
+ final String msg = "Permission Denial: " + func + " from pid="
+ + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
+ + " trying to apply changes of changeMask=" + originalChangeMask
+ + " configSetMask=" + originalConfigSetMask
+ + " windowSetMask=" + originalWindowSetMask
+ + " to TaskFragment=" + tf + " TaskFragmentOrganizer=" + organizer;
+ Slog.w(TAG, msg);
+ throw new SecurityException(msg);
}
private void createTaskFragment(@NonNull TaskFragmentCreationParams creationParams,
@@ -2019,7 +2085,7 @@
TaskFragmentOrganizerToken organizerToken = creationParams.getOrganizer();
taskFragment.setTaskFragmentOrganizer(organizerToken,
ownerActivity.getUid(), ownerActivity.info.processName,
- mTaskFragmentOrganizerController.isSystemOrganizer(organizerToken));
+ mTaskFragmentOrganizerController.isSystemOrganizer(organizerToken.asBinder()));
final int position;
if (creationParams.getPairedPrimaryFragmentToken() != null) {
// When there is a paired primary TaskFragment, we want to place the new TaskFragment
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 4beec2b..7f36aec 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -186,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;
@@ -1188,7 +1189,20 @@
}
}
- public boolean isWindowTrustedOverlay() {
+ @Override
+ void setInitialSurfaceControlProperties(SurfaceControl.Builder b) {
+ super.setInitialSurfaceControlProperties(b);
+ if (surfaceTrustedOverlay() && isWindowTrustedOverlay()) {
+ getPendingTransaction().setTrustedOverlay(mSurfaceControl, true);
+ }
+ }
+
+ void updateTrustedOverlay() {
+ mInputWindowHandle.setTrustedOverlay(getPendingTransaction(), mSurfaceControl,
+ isWindowTrustedOverlay());
+ }
+
+ boolean isWindowTrustedOverlay() {
return InputMonitor.isTrustedOverlay(mAttrs.type)
|| ((mAttrs.privateFlags & PRIVATE_FLAG_TRUSTED_OVERLAY) != 0
&& mSession.mCanAddInternalSystemWindow)
@@ -2755,12 +2769,7 @@
// bounds, as they would be used to display the dim layer.
final TaskFragment taskFragment = getTaskFragment();
if (taskFragment != null) {
- final Task task = taskFragment.asTask();
- if (task != null) {
- task.getDimBounds(mTmpRect);
- } else {
- mTmpRect.set(taskFragment.getBounds());
- }
+ taskFragment.getDimBounds(mTmpRect);
} else if (getRootTask() != null) {
getRootTask().getDimBounds(mTmpRect);
}
@@ -3732,30 +3741,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();
}
@@ -5190,9 +5213,6 @@
updateFrameRateSelectionPriorityIfNeeded();
updateScaleIfNeeded();
mWinAnimator.prepareSurfaceLocked(getSyncTransaction());
- if (surfaceTrustedOverlay()) {
- getSyncTransaction().setTrustedOverlay(mSurfaceControl, isWindowTrustedOverlay());
- }
}
super.prepareSurfaces();
}
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index e434f29..7d21dbf 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -569,6 +569,7 @@
&& asActivityRecord() != null && isVisible()) {
// Trigger an activity level rotation transition.
mTransitionController.requestTransitionIfNeeded(WindowManager.TRANSIT_CHANGE, this);
+ mTransitionController.collectVisibleChange(this);
mTransitionController.setReady(this);
}
final int originalRotation = getWindowConfiguration().getRotation();
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index bc70658..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",
diff --git a/services/core/jni/OWNERS b/services/core/jni/OWNERS
index 7e8ce60..0e45f61 100644
--- a/services/core/jni/OWNERS
+++ b/services/core/jni/OWNERS
@@ -20,6 +20,7 @@
per-file com_android_server_location_* = file:/location/java/android/location/OWNERS
per-file com_android_server_locksettings_* = file:/services/core/java/com/android/server/locksettings/OWNERS
per-file com_android_server_net_* = file:/services/core/java/com/android/server/net/OWNERS
+per-file com_android_server_pdb_* = file:/services/core/java/com/android/server/pdb/OWNERS
per-file com_android_server_pm_* = file:/services/core/java/com/android/server/pm/OWNERS
per-file com_android_server_power_* = file:/services/core/java/com/android/server/power/OWNERS
per-file com_android_server_powerstats_* = file:/services/core/java/com/android/server/powerstats/OWNERS
diff --git a/services/core/jni/com_android_server_PersistentDataBlockService.cpp b/services/core/jni/com_android_server_PersistentDataBlockService.cpp
deleted file mode 100644
index 97e69fb..0000000
--- a/services/core/jni/com_android_server_PersistentDataBlockService.cpp
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <android_runtime/AndroidRuntime.h>
-#include <nativehelper/JNIHelp.h>
-#include <jni.h>
-#include <nativehelper/ScopedUtfChars.h>
-
-#include <utils/misc.h>
-#include <sys/ioctl.h>
-#include <sys/mount.h>
-#include <utils/Log.h>
-
-
-#include <inttypes.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <string.h>
-
-namespace android {
-
- uint64_t get_block_device_size(int fd)
- {
- uint64_t size = 0;
- int ret;
-
- ret = ioctl(fd, BLKGETSIZE64, &size);
-
- if (ret)
- return 0;
-
- return size;
- }
-
- int wipe_block_device(int fd)
- {
- uint64_t range[2];
- int ret;
- uint64_t len = get_block_device_size(fd);
-
- range[0] = 0;
- range[1] = len;
-
- if (range[1] == 0)
- return 0;
-
- ret = ioctl(fd, BLKSECDISCARD, &range);
- if (ret < 0) {
- ALOGE("Something went wrong secure discarding block: %s\n", strerror(errno));
- range[0] = 0;
- range[1] = len;
- ret = ioctl(fd, BLKDISCARD, &range);
- if (ret < 0) {
- ALOGE("Discard failed: %s\n", strerror(errno));
- return -1;
- } else {
- ALOGE("Wipe via secure discard failed, used non-secure discard instead\n");
- return 0;
- }
-
- }
-
- return ret;
- }
-
- static jlong com_android_server_PersistentDataBlockService_getBlockDeviceSize(JNIEnv *env, jclass, jstring jpath)
- {
- ScopedUtfChars path(env, jpath);
- int fd = open(path.c_str(), O_RDONLY);
-
- if (fd < 0)
- return 0;
-
- const uint64_t size = get_block_device_size(fd);
-
- close(fd);
-
- return size;
- }
-
- static int com_android_server_PersistentDataBlockService_wipe(JNIEnv *env, jclass, jstring jpath) {
- ScopedUtfChars path(env, jpath);
- int fd = open(path.c_str(), O_WRONLY);
-
- if (fd < 0)
- return 0;
-
- const int ret = wipe_block_device(fd);
-
- close(fd);
-
- return ret;
- }
-
- 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},
- };
-
- int register_android_server_PersistentDataBlockService(JNIEnv* env)
- {
- return jniRegisterNativeMethods(env, "com/android/server/PersistentDataBlockService",
- sMethods, NELEM(sMethods));
- }
-
-} /* namespace android */
\ No newline at end of file
diff --git a/services/core/jni/com_android_server_display_SmallAreaDetectionController.cpp b/services/core/jni/com_android_server_display_SmallAreaDetectionController.cpp
index b256f16..1844d30 100644
--- a/services/core/jni/com_android_server_display_SmallAreaDetectionController.cpp
+++ b/services/core/jni/com_android_server_display_SmallAreaDetectionController.cpp
@@ -24,33 +24,33 @@
#include "utils/Log.h"
namespace android {
-static void nativeUpdateSmallAreaDetection(JNIEnv* env, jclass clazz, jintArray juids,
+static void nativeUpdateSmallAreaDetection(JNIEnv* env, jclass clazz, jintArray jappIds,
jfloatArray jthresholds) {
- if (juids == nullptr || jthresholds == nullptr) return;
+ if (jappIds == nullptr || jthresholds == nullptr) return;
- ScopedIntArrayRO uids(env, juids);
+ ScopedIntArrayRO appIds(env, jappIds);
ScopedFloatArrayRO thresholds(env, jthresholds);
- if (uids.size() != thresholds.size()) {
- ALOGE("uids size exceeds thresholds size!");
+ if (appIds.size() != thresholds.size()) {
+ ALOGE("appIds size exceeds thresholds size!");
return;
}
- std::vector<int32_t> uidVector;
+ std::vector<int32_t> appIdVector;
std::vector<float> thresholdVector;
- size_t size = uids.size();
- uidVector.reserve(size);
+ size_t size = appIds.size();
+ appIdVector.reserve(size);
thresholdVector.reserve(size);
for (int i = 0; i < size; i++) {
- uidVector.push_back(static_cast<int32_t>(uids[i]));
+ appIdVector.push_back(static_cast<int32_t>(appIds[i]));
thresholdVector.push_back(static_cast<float>(thresholds[i]));
}
- SurfaceComposerClient::updateSmallAreaDetection(uidVector, thresholdVector);
+ SurfaceComposerClient::updateSmallAreaDetection(appIdVector, thresholdVector);
}
-static void nativeSetSmallAreaDetectionThreshold(JNIEnv* env, jclass clazz, jint uid,
+static void nativeSetSmallAreaDetectionThreshold(JNIEnv* env, jclass clazz, jint appId,
jfloat threshold) {
- SurfaceComposerClient::setSmallAreaDetectionThreshold(uid, threshold);
+ SurfaceComposerClient::setSmallAreaDetectionThreshold(appId, threshold);
}
static const JNINativeMethod gMethods[] = {
diff --git a/services/core/jni/com_android_server_pdb_PersistentDataBlockService.cpp b/services/core/jni/com_android_server_pdb_PersistentDataBlockService.cpp
new file mode 100644
index 0000000..1e3cfd0
--- /dev/null
+++ b/services/core/jni/com_android_server_pdb_PersistentDataBlockService.cpp
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android_runtime/AndroidRuntime.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <jni.h>
+#include <nativehelper/JNIHelp.h>
+#include <nativehelper/ScopedUtfChars.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/mount.h>
+#include <utils/Log.h>
+#include <utils/misc.h>
+
+namespace android {
+
+uint64_t get_block_device_size(int fd) {
+ uint64_t size = 0;
+ int ret;
+
+ ret = ioctl(fd, BLKGETSIZE64, &size);
+
+ if (ret) return 0;
+
+ return size;
+}
+
+int wipe_block_device(int fd) {
+ uint64_t range[2];
+ int ret;
+ uint64_t len = get_block_device_size(fd);
+
+ range[0] = 0;
+ range[1] = len;
+
+ if (range[1] == 0) return 0;
+
+ ret = ioctl(fd, BLKSECDISCARD, &range);
+ if (ret < 0) {
+ ALOGE("Something went wrong secure discarding block: %s\n", strerror(errno));
+ range[0] = 0;
+ range[1] = len;
+ ret = ioctl(fd, BLKDISCARD, &range);
+ if (ret < 0) {
+ ALOGE("Discard failed: %s\n", strerror(errno));
+ return -1;
+ } else {
+ ALOGE("Wipe via secure discard failed, used non-secure discard instead\n");
+ return 0;
+ }
+ }
+
+ return ret;
+}
+
+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);
+
+ if (fd < 0) return 0;
+
+ const uint64_t size = get_block_device_size(fd);
+
+ close(fd);
+
+ return size;
+}
+
+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);
+
+ if (fd < 0) return 0;
+
+ const int ret = wipe_block_device(fd);
+
+ close(fd);
+
+ return ret;
+}
+
+static const JNINativeMethod sMethods[] = {
+ /* name, signature, funcPtr */
+ {"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_pdb_PersistentDataBlockService(JNIEnv *env) {
+ return jniRegisterNativeMethods(env, "com/android/server/pdb/PersistentDataBlockService",
+ sMethods, NELEM(sMethods));
+}
+
+} /* namespace android */
\ No newline at end of file
diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp
index df44895..11734da 100644
--- a/services/core/jni/onload.cpp
+++ b/services/core/jni/onload.cpp
@@ -47,7 +47,7 @@
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);
@@ -108,7 +108,7 @@
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);
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/credentials/java/com/android/server/credentials/CredentialManagerService.java b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
index 08df651..a4adf58 100644
--- a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
+++ b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
@@ -65,7 +65,6 @@
import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
-import com.android.internal.content.PackageMonitor;
import com.android.server.credentials.metrics.ApiName;
import com.android.server.credentials.metrics.ApiStatus;
import com.android.server.infra.AbstractMasterSystemService;
@@ -90,7 +89,7 @@
*/
public final class CredentialManagerService
extends AbstractMasterSystemService<
- CredentialManagerService, CredentialManagerServiceImpl> {
+ CredentialManagerService, CredentialManagerServiceImpl> {
private static final String TAG = "CredManSysService";
private static final String PERMISSION_DENIED_ERROR = "permission_denied";
@@ -111,7 +110,8 @@
/** Cache of all ongoing request sessions per user id. */
@GuardedBy("mLock")
- private final SparseArray<Map<IBinder, RequestSession>> mRequestSessions = new SparseArray<>();
+ private final SparseArray<Map<IBinder, RequestSession>> mRequestSessions =
+ new SparseArray<>();
private final SessionManager mSessionManager = new SessionManager();
@@ -123,8 +123,6 @@
null,
PACKAGE_UPDATE_POLICY_REFRESH_EAGER);
mContext = context;
-
- mPackageMonitor.register(context, context.getMainLooper(), false);
}
@NonNull
@@ -141,7 +139,8 @@
serviceInfos.forEach(
info -> {
services.add(
- new CredentialManagerServiceImpl(this, mLock, resolvedUserId, info));
+ new CredentialManagerServiceImpl(this, mLock, resolvedUserId,
+ info));
});
return services;
}
@@ -217,8 +216,8 @@
for (CredentialManagerServiceImpl serviceToBeRemoved : servicesToBeRemoved) {
removeServiceFromCache(serviceToBeRemoved, userId);
removeServiceFromSystemServicesCache(serviceToBeRemoved, userId);
- removeServiceFromMultiModeSettings(
- serviceToBeRemoved.getComponentName().flattenToString(), userId);
+ removeServiceFromMultiModeSettings(serviceToBeRemoved.getComponentName()
+ .flattenToString(), userId);
CredentialDescriptionRegistry.forUser(userId)
.evictProviderWithPackageName(serviceToBeRemoved.getServicePackageName());
}
@@ -287,20 +286,13 @@
}
private static Set<ComponentName> getPrimaryProvidersForUserId(Context context, int userId) {
- final int resolvedUserId =
- ActivityManager.handleIncomingUser(
- Binder.getCallingPid(),
- Binder.getCallingUid(),
- userId,
- false,
- false,
- "getPrimaryProvidersForUserId",
- null);
- SecureSettingsServiceNameResolver resolver =
- new SecureSettingsServiceNameResolver(
- context,
- Settings.Secure.CREDENTIAL_SERVICE_PRIMARY,
- /* isMultipleMode= */ true);
+ final int resolvedUserId = ActivityManager.handleIncomingUser(
+ Binder.getCallingPid(), Binder.getCallingUid(),
+ userId, false, false,
+ "getPrimaryProvidersForUserId", null);
+ SecureSettingsServiceNameResolver resolver = new SecureSettingsServiceNameResolver(
+ context, Settings.Secure.CREDENTIAL_SERVICE_PRIMARY,
+ /* isMultipleMode= */ true);
String[] serviceNames = resolver.readServiceNameList(resolvedUserId);
if (serviceNames == null) {
return new HashSet<ComponentName>();
@@ -337,8 +329,7 @@
final long origId = Binder.clearCallingIdentity();
try {
return DeviceConfig.getBoolean(
- DeviceConfig.NAMESPACE_CREDENTIAL,
- DEVICE_CONFIG_ENABLE_CREDENTIAL_DESC_API,
+ DeviceConfig.NAMESPACE_CREDENTIAL, DEVICE_CONFIG_ENABLE_CREDENTIAL_DESC_API,
false);
} finally {
Binder.restoreCallingIdentity(origId);
@@ -354,14 +345,13 @@
List<ProviderSession> providerSessions = new ArrayList<>();
for (Pair<CredentialOption, CredentialDescriptionRegistry.FilterResult> result :
activeCredentialContainers) {
- ProviderSession providerSession =
- ProviderRegistryGetSession.createNewSession(
- mContext,
- UserHandle.getCallingUserId(),
- session,
- session.mClientAppInfo,
- result.second.mPackageName,
- result.first);
+ ProviderSession providerSession = ProviderRegistryGetSession.createNewSession(
+ mContext,
+ UserHandle.getCallingUserId(),
+ session,
+ session.mClientAppInfo,
+ result.second.mPackageName,
+ result.first);
providerSessions.add(providerSession);
session.addProviderSession(providerSession.getComponentName(), providerSession);
}
@@ -377,23 +367,23 @@
List<ProviderSession> providerSessions = new ArrayList<>();
for (Pair<CredentialOption, CredentialDescriptionRegistry.FilterResult> result :
activeCredentialContainers) {
- ProviderSession providerSession =
- ProviderRegistryGetSession.createNewSession(
- mContext,
- UserHandle.getCallingUserId(),
- session,
- session.mClientAppInfo,
- result.second.mPackageName,
- result.first);
+ ProviderSession providerSession = ProviderRegistryGetSession.createNewSession(
+ mContext,
+ UserHandle.getCallingUserId(),
+ session,
+ session.mClientAppInfo,
+ result.second.mPackageName,
+ result.first);
providerSessions.add(providerSession);
session.addProviderSession(providerSession.getComponentName(), providerSession);
}
return providerSessions;
}
+
@NonNull
private Set<Pair<CredentialOption, CredentialDescriptionRegistry.FilterResult>>
- getFilteredResultFromRegistry(List<CredentialOption> options) {
+ getFilteredResultFromRegistry(List<CredentialOption> options) {
// Session for active/provisioned credential descriptions;
CredentialDescriptionRegistry registry =
CredentialDescriptionRegistry.forUser(UserHandle.getCallingUserId());
@@ -403,12 +393,10 @@
options.stream()
.map(
getCredentialOption ->
- new HashSet<>(
- getCredentialOption
- .getCredentialRetrievalData()
- .getStringArrayList(
- CredentialOption
- .SUPPORTED_ELEMENT_KEYS)))
+ new HashSet<>(getCredentialOption
+ .getCredentialRetrievalData()
+ .getStringArrayList(
+ CredentialOption.SUPPORTED_ELEMENT_KEYS)))
.collect(Collectors.toSet());
// All requested credential descriptions based on the given request.
@@ -420,14 +408,12 @@
for (CredentialDescriptionRegistry.FilterResult filterResult : filterResults) {
for (CredentialOption credentialOption : options) {
- Set<String> requestedElementKeys =
- new HashSet<>(
- credentialOption
- .getCredentialRetrievalData()
- .getStringArrayList(
- CredentialOption.SUPPORTED_ELEMENT_KEYS));
- if (CredentialDescriptionRegistry.checkForMatch(
- filterResult.mElementKeys, requestedElementKeys)) {
+ Set<String> requestedElementKeys = new HashSet<>(
+ credentialOption
+ .getCredentialRetrievalData()
+ .getStringArrayList(CredentialOption.SUPPORTED_ELEMENT_KEYS));
+ if (CredentialDescriptionRegistry.checkForMatch(filterResult.mElementKeys,
+ requestedElementKeys)) {
result.add(new Pair<>(credentialOption, filterResult));
}
}
@@ -463,7 +449,9 @@
}
private CallingAppInfo constructCallingAppInfo(
- String realPackageName, int userId, @Nullable String origin) {
+ String realPackageName,
+ int userId,
+ @Nullable String origin) {
final PackageInfo packageInfo;
CallingAppInfo callingAppInfo;
try {
@@ -489,7 +477,8 @@
GetCredentialRequest request,
IGetCandidateCredentialsCallback callback,
final String callingPackage) {
- Slog.i(TAG, "starting getCandidateCredentials with callingPackage: " + callingPackage);
+ Slog.i(TAG, "starting getCandidateCredentials with callingPackage: "
+ + callingPackage);
ICancellationSignal cancelTransport = CancellationSignal.createTransport();
final int userId = UserHandle.getCallingUserId();
@@ -507,7 +496,8 @@
request,
constructCallingAppInfo(callingPackage, userId, request.getOrigin()),
getEnabledProvidersForUser(userId),
- CancellationSignal.fromTransport(cancelTransport));
+ CancellationSignal.fromTransport(cancelTransport)
+ );
addSessionLocked(userId, session);
List<ProviderSession> providerSessions =
@@ -541,7 +531,8 @@
IGetCredentialCallback callback,
final String callingPackage) {
final long timestampBegan = System.nanoTime();
- Slog.i(TAG, "starting executeGetCredential with callingPackage: " + callingPackage);
+ Slog.i(TAG, "starting executeGetCredential with callingPackage: "
+ + callingPackage);
ICancellationSignal cancelTransport = CancellationSignal.createTransport();
final int userId = UserHandle.getCallingUserId();
@@ -566,7 +557,8 @@
timestampBegan);
addSessionLocked(userId, session);
- List<ProviderSession> providerSessions = prepareProviderSessions(request, session);
+ List<ProviderSession> providerSessions =
+ prepareProviderSessions(request, session);
if (providerSessions.isEmpty()) {
try {
@@ -625,17 +617,15 @@
if (providerSessions.isEmpty()) {
try {
prepareGetCredentialCallback.onResponse(
- new PrepareGetCredentialResponseInternal(
- PermissionUtils.hasPermission(
- mContext,
- callingPackage,
- Manifest.permission
- .CREDENTIAL_MANAGER_QUERY_CANDIDATE_CREDENTIALS
- ),
- /* credentialResultTypes= */ null,
- /* hasAuthenticationResults= */ false,
- /* hasRemoteResults= */ false,
- /* pendingIntent= */ null));
+ new PrepareGetCredentialResponseInternal(PermissionUtils.hasPermission(
+ mContext,
+ callingPackage,
+ Manifest.permission
+ .CREDENTIAL_MANAGER_QUERY_CANDIDATE_CREDENTIALS),
+ /*credentialResultTypes=*/null,
+ /*hasAuthenticationResults=*/false,
+ /*hasRemoteResults=*/false,
+ /*pendingIntent=*/null));
} catch (RemoteException e) {
Slog.e(
TAG,
@@ -651,32 +641,27 @@
}
private List<ProviderSession> prepareProviderSessions(
- GetCredentialRequest request, GetRequestSession session) {
+ GetCredentialRequest request,
+ GetRequestSession session) {
List<ProviderSession> providerSessions;
if (isCredentialDescriptionApiEnabled()) {
List<CredentialOption> optionsThatRequireActiveCredentials =
request.getCredentialOptions().stream()
- .filter(
- credentialOption ->
- credentialOption
- .getCredentialRetrievalData()
- .getStringArrayList(
- CredentialOption
- .SUPPORTED_ELEMENT_KEYS)
- != null)
+ .filter(credentialOption -> credentialOption
+ .getCredentialRetrievalData()
+ .getStringArrayList(
+ CredentialOption
+ .SUPPORTED_ELEMENT_KEYS) != null)
.toList();
List<CredentialOption> optionsThatDoNotRequireActiveCredentials =
request.getCredentialOptions().stream()
- .filter(
- credentialOption ->
- credentialOption
- .getCredentialRetrievalData()
- .getStringArrayList(
- CredentialOption
- .SUPPORTED_ELEMENT_KEYS)
- == null)
+ .filter(credentialOption -> credentialOption
+ .getCredentialRetrievalData()
+ .getStringArrayList(
+ CredentialOption
+ .SUPPORTED_ELEMENT_KEYS) == null)
.toList();
List<ProviderSession> sessionsWithoutRemoteService =
@@ -721,7 +706,8 @@
ICreateCredentialCallback callback,
String callingPackage) {
final long timestampBegan = System.nanoTime();
- Slog.i(TAG, "starting executeCreateCredential with callingPackage: " + callingPackage);
+ Slog.i(TAG, "starting executeCreateCredential with callingPackage: "
+ + callingPackage);
ICancellationSignal cancelTransport = CancellationSignal.createTransport();
if (request.getOrigin() != null) {
@@ -770,8 +756,8 @@
} catch (RemoteException e) {
Slog.e(
TAG,
- "Issue invoking onError on ICreateCredentialCallback " + "callback: ",
- e);
+ "Issue invoking onError on ICreateCredentialCallback "
+ + "callback: ", e);
}
}
@@ -784,8 +770,8 @@
try {
var initMetric = session.mRequestSessionMetric.getInitialPhaseMetric();
initMetric.setCredentialServiceBeginQueryTimeNanoseconds(System.nanoTime());
- MetricUtilities.logApiCalledInitialPhase(
- initMetric, session.mRequestSessionMetric.returnIncrementSequence());
+ MetricUtilities.logApiCalledInitialPhase(initMetric,
+ session.mRequestSessionMetric.returnIncrementSequence());
} catch (Exception e) {
Slog.i(TAG, "Unexpected error during metric logging: ", e);
}
@@ -793,32 +779,25 @@
@Override
public void setEnabledProviders(
- List<String> primaryProviders,
- List<String> providers,
- int userId,
+ List<String> primaryProviders, List<String> providers, int userId,
ISetEnabledProvidersCallback callback) {
final int callingUid = Binder.getCallingUid();
if (!hasWriteSecureSettingsPermission()) {
try {
MetricUtilities.logApiCalledSimpleV2(
- ApiName.SET_ENABLED_PROVIDERS, ApiStatus.FAILURE, callingUid);
+ ApiName.SET_ENABLED_PROVIDERS,
+ ApiStatus.FAILURE, callingUid);
callback.onError(
PERMISSION_DENIED_ERROR, PERMISSION_DENIED_WRITE_SECURE_SETTINGS_ERROR);
} catch (RemoteException e) {
MetricUtilities.logApiCalledSimpleV2(
- ApiName.SET_ENABLED_PROVIDERS, ApiStatus.FAILURE, callingUid);
+ ApiName.SET_ENABLED_PROVIDERS,
+ ApiStatus.FAILURE, callingUid);
Slog.e(TAG, "Issue with invoking response: ", e);
}
return;
}
- // If we don't have any primary providers enabled anymore then
- // we should erase all the providers since the feature is
- // now disabled.
- if (primaryProviders.isEmpty()) {
- providers.clear();
- }
-
userId =
ActivityManager.handleIncomingUser(
Binder.getCallingPid(),
@@ -829,19 +808,17 @@
"setEnabledProviders",
null);
- Set<String> enabledProviders = new HashSet<>(providers);
- enabledProviders.addAll(primaryProviders);
+ Set<String> enableProvider = new HashSet<>(providers);
+ enableProvider.addAll(primaryProviders);
boolean writeEnabledStatus =
- Settings.Secure.putStringForUser(
- getContext().getContentResolver(),
+ Settings.Secure.putStringForUser(getContext().getContentResolver(),
Settings.Secure.CREDENTIAL_SERVICE,
- String.join(":", enabledProviders),
+ String.join(":", enableProvider),
userId);
boolean writePrimaryStatus =
- Settings.Secure.putStringForUser(
- getContext().getContentResolver(),
+ Settings.Secure.putStringForUser(getContext().getContentResolver(),
Settings.Secure.CREDENTIAL_SERVICE_PRIMARY,
String.join(":", primaryProviders),
userId);
@@ -850,13 +827,15 @@
Slog.e(TAG, "Failed to store setting containing enabled or primary providers");
try {
MetricUtilities.logApiCalledSimpleV2(
- ApiName.SET_ENABLED_PROVIDERS, ApiStatus.FAILURE, callingUid);
+ ApiName.SET_ENABLED_PROVIDERS,
+ ApiStatus.FAILURE, callingUid);
callback.onError(
"failed_setting_store",
"Failed to store setting containing enabled or primary providers");
} catch (RemoteException e) {
MetricUtilities.logApiCalledSimpleV2(
- ApiName.SET_ENABLED_PROVIDERS, ApiStatus.FAILURE, callingUid);
+ ApiName.SET_ENABLED_PROVIDERS,
+ ApiStatus.FAILURE, callingUid);
Slog.e(TAG, "Issue with invoking error response: ", e);
return;
}
@@ -865,11 +844,13 @@
// Call the callback.
try {
MetricUtilities.logApiCalledSimpleV2(
- ApiName.SET_ENABLED_PROVIDERS, ApiStatus.SUCCESS, callingUid);
+ ApiName.SET_ENABLED_PROVIDERS,
+ ApiStatus.SUCCESS, callingUid);
callback.onResponse();
} catch (RemoteException e) {
MetricUtilities.logApiCalledSimpleV2(
- ApiName.SET_ENABLED_PROVIDERS, ApiStatus.FAILURE, callingUid);
+ ApiName.SET_ENABLED_PROVIDERS,
+ ApiStatus.FAILURE, callingUid);
Slog.e(TAG, "Issue with invoking response: ", e);
// TODO: Propagate failure
}
@@ -878,10 +859,8 @@
@Override
public boolean isEnabledCredentialProviderService(
ComponentName componentName, String callingPackage) {
- Slog.i(
- TAG,
- "isEnabledCredentialProviderService with componentName: "
- + componentName.flattenToString());
+ Slog.i(TAG, "isEnabledCredentialProviderService with componentName: "
+ + componentName.flattenToString());
// TODO(253157366): Check additional set of services.
final int userId = UserHandle.getCallingUserId();
@@ -898,8 +877,7 @@
// The component name and the package name do not match.
MetricUtilities.logApiCalledSimpleV2(
ApiName.IS_ENABLED_CREDENTIAL_PROVIDER_SERVICE,
- ApiStatus.FAILURE,
- callingUid);
+ ApiStatus.FAILURE, callingUid);
Slog.w(
TAG,
"isEnabledCredentialProviderService: Component name does "
@@ -908,8 +886,7 @@
}
MetricUtilities.logApiCalledSimpleV2(
ApiName.IS_ENABLED_CREDENTIAL_PROVIDER_SERVICE,
- ApiStatus.SUCCESS,
- callingUid);
+ ApiStatus.SUCCESS, callingUid);
return true;
}
}
@@ -924,14 +901,13 @@
verifyGetProvidersPermission();
final int callingUid = Binder.getCallingUid();
MetricUtilities.logApiCalledSimpleV2(
- ApiName.GET_CREDENTIAL_PROVIDER_SERVICES, ApiStatus.SUCCESS, callingUid);
+ ApiName.GET_CREDENTIAL_PROVIDER_SERVICES,
+ ApiStatus.SUCCESS, callingUid);
+ return CredentialProviderInfoFactory
+ .getCredentialProviderServices(
+ mContext, userId, providerFilter, getEnabledProvidersForUser(userId),
+ getPrimaryProvidersForUserId(mContext, userId));
- return CredentialProviderInfoFactory.getCredentialProviderServices(
- mContext,
- userId,
- providerFilter,
- getEnabledProvidersForUser(userId),
- getPrimaryProvidersForUserId(mContext, userId));
}
@Override
@@ -941,10 +917,7 @@
final int userId = UserHandle.getCallingUserId();
return CredentialProviderInfoFactory.getCredentialProviderServicesForTesting(
- mContext,
- userId,
- providerFilter,
- getEnabledProvidersForUser(userId),
+ mContext, userId, providerFilter, getEnabledProvidersForUser(userId),
getPrimaryProvidersForUserId(mContext, userId));
}
@@ -962,22 +935,15 @@
}
private Set<ComponentName> getEnabledProvidersForUser(int userId) {
- final int resolvedUserId =
- ActivityManager.handleIncomingUser(
- Binder.getCallingPid(),
- Binder.getCallingUid(),
- userId,
- false,
- false,
- "getEnabledProvidersForUser",
- null);
+ final int resolvedUserId = ActivityManager.handleIncomingUser(
+ Binder.getCallingPid(), Binder.getCallingUid(),
+ userId, false, false,
+ "getEnabledProvidersForUser", null);
Set<ComponentName> enabledProviders = new HashSet<>();
- String directValue =
- Settings.Secure.getStringForUser(
- mContext.getContentResolver(),
- Settings.Secure.CREDENTIAL_SERVICE,
- resolvedUserId);
+ String directValue = Settings.Secure.getStringForUser(
+ mContext.getContentResolver(), Settings.Secure.CREDENTIAL_SERVICE,
+ resolvedUserId);
if (!TextUtils.isEmpty(directValue)) {
String[] components = directValue.split(":");
@@ -998,7 +964,8 @@
IClearCredentialStateCallback callback,
String callingPackage) {
final long timestampBegan = System.nanoTime();
- Slog.i(TAG, "starting clearCredentialState with callingPackage: " + callingPackage);
+ Slog.i(TAG, "starting clearCredentialState with callingPackage: "
+ + callingPackage);
final int userId = UserHandle.getCallingUserId();
int callingUid = Binder.getCallingUid();
enforceCallingPackage(callingPackage, callingUid);
@@ -1029,13 +996,13 @@
if (providerSessions.isEmpty()) {
try {
// TODO("Replace with properly defined error type")
- callback.onError("UNKNOWN", "No credentials available on " + "this device");
+ callback.onError("UNKNOWN", "No credentials available on "
+ + "this device");
} catch (RemoteException e) {
Slog.e(
TAG,
"Issue invoking onError on IClearCredentialStateCallback "
- + "callback: ",
- e);
+ + "callback: ", e);
}
}
@@ -1068,7 +1035,9 @@
public void unregisterCredentialDescription(
UnregisterCredentialDescriptionRequest request, String callingPackage)
throws IllegalArgumentException {
- Slog.i(TAG, "unregisterCredentialDescription with callingPackage: " + callingPackage);
+ Slog.i(TAG, "unregisterCredentialDescription with callingPackage: "
+ + callingPackage);
+
if (!isCredentialDescriptionApiEnabled()) {
throw new UnsupportedOperationException("Feature not supported");
@@ -1092,18 +1061,18 @@
}
private void enforcePermissionForAllowedProviders(GetCredentialRequest request) {
- boolean containsAllowedProviders =
- request.getCredentialOptions().stream()
- .anyMatch(
- option ->
- option.getAllowedProviders() != null
- && !option.getAllowedProviders().isEmpty());
+ boolean containsAllowedProviders = request.getCredentialOptions()
+ .stream()
+ .anyMatch(option -> option.getAllowedProviders() != null
+ && !option.getAllowedProviders().isEmpty());
if (containsAllowedProviders) {
- mContext.enforceCallingPermission(CREDENTIAL_MANAGER_SET_ALLOWED_PROVIDERS, null);
+ mContext.enforceCallingPermission(CREDENTIAL_MANAGER_SET_ALLOWED_PROVIDERS,
+ null);
}
}
- private void addSessionLocked(@UserIdInt int userId, RequestSession requestSession) {
+ private void addSessionLocked(@UserIdInt int userId,
+ RequestSession requestSession) {
synchronized (mLock) {
mSessionManager.addSession(userId, requestSession.mRequestId, requestSession);
}
@@ -1111,11 +1080,11 @@
private void enforceCallingPackage(String callingPackage, int callingUid) {
int packageUid;
- PackageManager pm =
- mContext.createContextAsUser(UserHandle.getUserHandleForUid(callingUid), 0)
- .getPackageManager();
+ PackageManager pm = mContext.createContextAsUser(
+ UserHandle.getUserHandleForUid(callingUid), 0).getPackageManager();
try {
- packageUid = pm.getPackageUid(callingPackage, PackageManager.PackageInfoFlags.of(0));
+ packageUid = pm.getPackageUid(callingPackage,
+ PackageManager.PackageInfoFlags.of(0));
} catch (PackageManager.NameNotFoundException e) {
throw new SecurityException(callingPackage + " not found");
}
@@ -1141,72 +1110,4 @@
mRequestSessions.get(userId).put(token, requestSession);
}
}
-
- /** Updates settings when packages are removed. */
- private final PackageMonitor mPackageMonitor =
- new PackageMonitor() {
-
- @Override
- public void onPackageRemoved(String packageName, int uid) {
- Slog.d(TAG, "onPackageRemoved: " + packageName);
-
- // Remove any providers from the primary setting that contain the package name
- // being removed.
- Set<String> primaryProviders =
- getStoredProviders(
- Settings.Secure.CREDENTIAL_SERVICE_PRIMARY, packageName);
- if (!Settings.Secure.putString(
- getContext().getContentResolver(),
- Settings.Secure.CREDENTIAL_SERVICE_PRIMARY,
- String.join(":", primaryProviders))) {
- Slog.w(TAG, "Failed to remove primary package: " + packageName);
- return;
- }
-
- // Get the secondary providers and if there are no primary providers then
- // we should erase all the providers from the secondary list because the
- // feature is now disabled.
- if (!primaryProviders.isEmpty()) {
- return;
- }
-
- if (!Settings.Secure.putString(
- getContext().getContentResolver(),
- Settings.Secure.CREDENTIAL_SERVICE,
- "")) {
- Slog.w(TAG, "Failed to remove secondary package: " + packageName);
- return;
- }
- }
-
- private Set<String> getStoredProviders(String key, String packageName) {
- // Get the current providers.
- String rawProviders =
- Settings.Secure.getStringForUser(
- getContext().getContentResolver(), key,
- UserHandle.myUserId());
- if (rawProviders == null) {
- Slog.w(TAG, "settings key is null: " + key);
- return new HashSet<>();
- }
-
- // If the app being removed matches any of the package names from
- // this list then don't add it in the output.
- Set<String> providers = new HashSet<>();
- for (String rawComponentName : rawProviders.split(":")) {
- if (TextUtils.isEmpty(rawComponentName)
- || rawComponentName.equals("null")) {
- Slog.d(TAG, "provider component name is empty or null");
- continue;
- }
-
- ComponentName cn = ComponentName.unflattenFromString(rawComponentName);
- if (cn != null && !cn.getPackageName().equals(packageName)) {
- providers.add(cn.flattenToString());
- }
- }
-
- return providers;
- }
- };
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 43e47d7..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/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 49ad84a..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;
@@ -916,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".
diff --git a/services/midi/Android.bp b/services/midi/Android.bp
index 5adcfba..4b5f8a7 100644
--- a/services/midi/Android.bp
+++ b/services/midi/Android.bp
@@ -19,4 +19,7 @@
defaults: ["platform_service_defaults"],
srcs: [":services.midi-sources"],
libs: ["services.core"],
+ static_libs: [
+ "aconfig_midi_flags_java_lib",
+ ],
}
diff --git a/services/midi/java/com/android/server/midi/MidiService.java b/services/midi/java/com/android/server/midi/MidiService.java
index 486ddb4..2f47cc7 100644
--- a/services/midi/java/com/android/server/midi/MidiService.java
+++ b/services/midi/java/com/android/server/midi/MidiService.java
@@ -16,6 +16,8 @@
package com.android.server.midi;
+import static com.android.media.midi.flags.Flags.virtualUmp;
+
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
@@ -1391,7 +1393,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 +1530,6 @@
@RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS)
private void addUmpPackageDeviceServer(ServiceInfo serviceInfo, int userId) {
- Log.d(TAG, "addUmpPackageDeviceServer()" + userId);
XmlResourceParser parser = null;
try {
@@ -1551,6 +1551,12 @@
return;
}
+ if (!virtualUmp()) {
+ Log.w(TAG, "Skipping MIDI device service " + serviceInfo.packageName
+ + ": virtual UMP flag not enabled");
+ return;
+ }
+
Bundle properties = null;
int numPorts = 0;
boolean isPrivate = false;
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..3f26517 100644
--- a/services/permission/java/com/android/server/permission/access/immutable/IntReferenceMap.kt
+++ b/services/permission/java/com/android/server/permission/access/immutable/IntReferenceMap.kt
@@ -33,8 +33,7 @@
operator fun contains(key: Int): Boolean = array.contains(key)
- @Suppress("ReplaceGetOrSet")
- operator fun get(key: Int): I? = array.get(key)?.get()
+ @Suppress("ReplaceGetOrSet") operator fun get(key: Int): I? = array.get(key)?.get()
fun indexOfKey(key: Int): Int = array.indexOfKey(key)
@@ -55,7 +54,9 @@
class MutableIntReferenceMap<I : Immutable<M>, M : I>(
array: SparseArray<MutableReference<I, M>> = SparseArray()
) : IntReferenceMap<I, M>(array) {
- constructor(intReferenceMap: IntReferenceMap<I, M>) : this(
+ constructor(
+ intReferenceMap: IntReferenceMap<I, M>
+ ) : this(
intReferenceMap.array.clone().apply {
for (i in 0 until size()) {
setValueAt(i, valueAt(i).toImmutable())
@@ -63,8 +64,7 @@
}
)
- @Suppress("ReplaceGetOrSet")
- fun mutate(key: Int): M? = array.get(key)?.mutate()
+ @Suppress("ReplaceGetOrSet") fun mutate(key: Int): M? = array.get(key)?.mutate()
fun put(key: Int, value: M): I? = array.putReturnOld(key, MutableReference(value))?.get()
diff --git a/services/permission/java/com/android/server/permission/access/immutable/IntReferenceMapExtensions.kt b/services/permission/java/com/android/server/permission/access/immutable/IntReferenceMapExtensions.kt
index 1ed4f8a..a1bab95 100644
--- a/services/permission/java/com/android/server/permission/access/immutable/IntReferenceMapExtensions.kt
+++ b/services/permission/java/com/android/server/permission/access/immutable/IntReferenceMapExtensions.kt
@@ -72,7 +72,9 @@
key: Int,
defaultValue: () -> M
): M {
- mutate(key)?.let { return it }
+ mutate(key)?.let {
+ return it
+ }
return defaultValue().also { put(key, it) }
}
diff --git a/services/permission/java/com/android/server/permission/access/immutable/IntSet.kt b/services/permission/java/com/android/server/permission/access/immutable/IntSet.kt
index 21f2af2..1254797 100644
--- a/services/permission/java/com/android/server/permission/access/immutable/IntSet.kt
+++ b/services/permission/java/com/android/server/permission/access/immutable/IntSet.kt
@@ -18,12 +18,8 @@
import android.util.SparseBooleanArray
-/**
- * Immutable set with index-based access and [Int] elements.
- */
-sealed class IntSet(
- internal val array: SparseBooleanArray
-) : Immutable<MutableIntSet> {
+/** Immutable set with index-based access and [Int] elements. */
+sealed class IntSet(internal val array: SparseBooleanArray) : Immutable<MutableIntSet> {
val size: Int
get() = array.size()
@@ -40,12 +36,8 @@
override fun toString(): String = array.toString()
}
-/**
- * Mutable set with index-based access and [Int] elements.
- */
-class MutableIntSet(
- array: SparseBooleanArray = SparseBooleanArray()
-) : IntSet(array) {
+/** Mutable set with index-based access and [Int] elements. */
+class MutableIntSet(array: SparseBooleanArray = SparseBooleanArray()) : IntSet(array) {
constructor(intSet: IntSet) : this(intSet.array.clone())
fun add(element: Int): Boolean =
diff --git a/services/permission/java/com/android/server/permission/access/immutable/IntSetExtensions.kt b/services/permission/java/com/android/server/permission/access/immutable/IntSetExtensions.kt
index 163ebbf..9d0d14f 100644
--- a/services/permission/java/com/android/server/permission/access/immutable/IntSetExtensions.kt
+++ b/services/permission/java/com/android/server/permission/access/immutable/IntSetExtensions.kt
@@ -66,7 +66,7 @@
operator fun IntSet.plus(element: Int): MutableIntSet = toMutable().apply { this += element }
-fun MutableIntSet(values: IntArray): MutableIntSet = MutableIntSet().apply{ this += values }
+fun MutableIntSet(values: IntArray): MutableIntSet = MutableIntSet().apply { this += values }
operator fun MutableIntSet.plusAssign(element: Int) {
array.put(element, true)
diff --git a/services/permission/java/com/android/server/permission/access/immutable/MutableReference.kt b/services/permission/java/com/android/server/permission/access/immutable/MutableReference.kt
index 171cfeb..471a71b 100644
--- a/services/permission/java/com/android/server/permission/access/immutable/MutableReference.kt
+++ b/services/permission/java/com/android/server/permission/access/immutable/MutableReference.kt
@@ -27,21 +27,17 @@
* exposed on the immutable interface of the data structure as a `getFoo` method, and the [mutate]
* method exposed on the mutable interface of the data structure as a `mutateFoo` method. When the
* data structure is mutated/copied, a new instance of this class should be obtained with
- * [toImmutable], which makes the wrapped reference immutable-only again and thus prevents
- * further modifications to a data structure accessed with its immutable interface.
+ * [toImmutable], which makes the wrapped reference immutable-only again and thus prevents further
+ * modifications to a data structure accessed with its immutable interface.
*
* @see MutableIndexedReferenceMap
* @see MutableIntReferenceMap
*/
-class MutableReference<I : Immutable<M>, M : I> private constructor(
- private var immutable: I,
- private var mutable: M?
-) {
+class MutableReference<I : Immutable<M>, M : I>
+private constructor(private var immutable: I, private var mutable: M?) {
constructor(mutable: M) : this(mutable, mutable)
- /**
- * Return an immutable reference to the wrapped mutable data structure.
- */
+ /** Return an immutable reference to the wrapped mutable data structure. */
fun get(): I = immutable
/**
@@ -50,7 +46,9 @@
* already mutable.
*/
fun mutate(): M {
- mutable?.let { return it }
+ mutable?.let {
+ return it
+ }
return immutable.toMutable().also {
immutable = it
mutable = it
diff --git a/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionMigration.kt b/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionMigration.kt
index 691ed8f..2983895 100644
--- a/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionMigration.kt
+++ b/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionMigration.kt
@@ -23,9 +23,7 @@
import com.android.server.permission.access.util.PackageVersionMigration
import com.android.server.pm.permission.PermissionMigrationHelper
-/**
- * This class migrate legacy permissions to unified permission subsystem
- */
+/** This class migrate legacy permissions to unified permission subsystem */
class AppIdPermissionMigration {
internal fun migrateSystemState(state: MutableAccessState) {
val legacyPermissionsManager =
@@ -34,10 +32,15 @@
return
}
- migratePermissions(state.mutateSystemState().mutatePermissions(),
- legacyPermissionsManager.legacyPermissions)
- migratePermissions(state.mutateSystemState().mutatePermissionTrees(),
- legacyPermissionsManager.legacyPermissionTrees, true)
+ migratePermissions(
+ state.mutateSystemState().mutatePermissions(),
+ legacyPermissionsManager.legacyPermissions
+ )
+ migratePermissions(
+ state.mutateSystemState().mutatePermissionTrees(),
+ legacyPermissionsManager.legacyPermissionTrees,
+ true
+ )
}
private fun migratePermissions(
@@ -46,14 +49,15 @@
isPermissionTree: Boolean = false
) {
legacyPermissions.forEach { (_, legacyPermission) ->
- val permission = Permission(
- legacyPermission.permissionInfo, false, legacyPermission.type, 0
- )
+ val permission =
+ Permission(legacyPermission.permissionInfo, false, legacyPermission.type, 0)
permissions[permission.name] = permission
if (DEBUG_MIGRATION) {
- Slog.v(LOG_TAG, "Migrated permission: ${permission.name}, type: " +
- "${permission.type}, appId: ${permission.appId}, protectionLevel: " +
- "${permission.protectionLevel}, tree: $isPermissionTree"
+ Slog.v(
+ LOG_TAG,
+ "Migrated permission: ${permission.name}, type: " +
+ "${permission.type}, appId: ${permission.appId}, protectionLevel: " +
+ "${permission.protectionLevel}, tree: $isPermissionTree"
)
}
}
@@ -81,25 +85,23 @@
val permissionFlags = MutableIndexedMap<String, Int>()
appIdPermissionFlags[appId] = permissionFlags
- legacyPermissionStates.forEach forEachPermission@ {
+ legacyPermissionStates.forEach forEachPermission@{
(permissionName, legacyPermissionState) ->
val permission = state.systemState.permissions[permissionName]
if (permission == null) {
Slog.w(
- LOG_TAG, "Dropping unknown permission $permissionName for app ID $appId" +
+ LOG_TAG,
+ "Dropping unknown permission $permissionName for app ID $appId" +
" when migrating permission state"
)
return@forEachPermission
}
- permissionFlags[permissionName] = migratePermissionFlags(
- permission, legacyPermissionState, appId, userId
- )
+ permissionFlags[permissionName] =
+ migratePermissionFlags(permission, legacyPermissionState, appId, userId)
}
val packageVersions = userState.mutatePackageVersions()
- packageNames.forEachIndexed { _, packageName ->
- packageVersions[packageName] = version
- }
+ packageNames.forEachIndexed { _, packageName -> packageVersions[packageName] = version }
}
}
@@ -109,29 +111,35 @@
appId: Int,
userId: Int
): Int {
- var flags = when {
- permission.isNormal -> if (legacyPermissionState.isGranted) {
- PermissionFlags.INSTALL_GRANTED
- } else {
- PermissionFlags.INSTALL_REVOKED
- }
- permission.isSignature || permission.isInternal ->
- if (legacyPermissionState.isGranted) {
- if (permission.isDevelopment || permission.isRole) {
- PermissionFlags.PROTECTION_GRANTED or PermissionFlags.RUNTIME_GRANTED
+ var flags =
+ when {
+ permission.isNormal ->
+ if (legacyPermissionState.isGranted) {
+ PermissionFlags.INSTALL_GRANTED
} else {
- PermissionFlags.PROTECTION_GRANTED
+ PermissionFlags.INSTALL_REVOKED
}
- } else {
- 0
- }
- permission.isRuntime ->
- if (legacyPermissionState.isGranted) PermissionFlags.RUNTIME_GRANTED else 0
- else -> 0
- }
- flags = PermissionFlags.updateFlags(
- permission, flags, legacyPermissionState.flags, legacyPermissionState.flags
- )
+ permission.isSignature || permission.isInternal ->
+ if (legacyPermissionState.isGranted) {
+ if (permission.isDevelopment || permission.isRole) {
+ PermissionFlags.PROTECTION_GRANTED or PermissionFlags.RUNTIME_GRANTED
+ } else {
+ PermissionFlags.PROTECTION_GRANTED
+ }
+ } else {
+ 0
+ }
+ permission.isRuntime ->
+ if (legacyPermissionState.isGranted) PermissionFlags.RUNTIME_GRANTED else 0
+ else -> 0
+ }
+ flags =
+ PermissionFlags.updateFlags(
+ permission,
+ flags,
+ legacyPermissionState.flags,
+ legacyPermissionState.flags
+ )
if (DEBUG_MIGRATION) {
val oldFlagString = PermissionFlags.apiFlagsToString(legacyPermissionState.flags)
val newFlagString = PermissionFlags.toString(flags)
@@ -139,7 +147,8 @@
val newGrantState = PermissionFlags.isPermissionGranted(flags)
val flagsMismatch = legacyPermissionState.flags != PermissionFlags.toApiFlags(flags)
Slog.v(
- LOG_TAG, "Migrated appId: $appId, permission: " +
+ LOG_TAG,
+ "Migrated appId: $appId, permission: " +
"${permission.name}, user: $userId, oldGrantState: $oldGrantState" +
", oldFlags: $oldFlagString, newFlags: $newFlagString, grantMismatch: " +
"${oldGrantState != newGrantState}, flagsMismatch: $flagsMismatch"
diff --git a/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPersistence.kt b/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPersistence.kt
index 2c8175b..1f40f01 100644
--- a/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPersistence.kt
+++ b/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPersistence.kt
@@ -57,11 +57,12 @@
isPermissionTree: Boolean
) {
val systemState = state.mutateSystemState(WriteMode.NONE)
- val permissions = if (isPermissionTree) {
- systemState.mutatePermissionTrees()
- } else {
- systemState.mutatePermissions()
- }
+ val permissions =
+ if (isPermissionTree) {
+ systemState.mutatePermissionTrees()
+ } else {
+ systemState.mutatePermissions()
+ }
forEachTag {
when (val tagName = tagName) {
TAG_PERMISSION -> parsePermission(permissions)
@@ -71,10 +72,13 @@
permissions.forEachReversedIndexed { permissionIndex, _, permission ->
val packageName = permission.packageName
val externalState = state.externalState
- if (packageName !in externalState.packageStates &&
- packageName !in externalState.disabledSystemPackageStates) {
+ if (
+ packageName !in externalState.packageStates &&
+ packageName !in externalState.disabledSystemPackageStates
+ ) {
Slog.w(
- LOG_TAG, "Dropping permission ${permission.name} from unknown package" +
+ LOG_TAG,
+ "Dropping permission ${permission.name} from unknown package" +
" $packageName when parsing permissions"
)
permissions.removeAt(permissionIndex)
@@ -88,11 +92,12 @@
) {
val name = getAttributeValueOrThrow(ATTR_NAME).intern()
@Suppress("DEPRECATION")
- val permissionInfo = PermissionInfo().apply {
- this.name = name
- packageName = getAttributeValueOrThrow(ATTR_PACKAGE_NAME).intern()
- protectionLevel = getAttributeIntHexOrThrow(ATTR_PROTECTION_LEVEL)
- }
+ val permissionInfo =
+ PermissionInfo().apply {
+ this.name = name
+ packageName = getAttributeValueOrThrow(ATTR_PACKAGE_NAME).intern()
+ protectionLevel = getAttributeIntHexOrThrow(ATTR_PROTECTION_LEVEL)
+ }
val type = getAttributeIntOrThrow(ATTR_TYPE)
when (type) {
Permission.TYPE_MANIFEST -> {}
@@ -125,15 +130,14 @@
tagName: String,
permissions: IndexedMap<String, Permission>
) {
- tag(tagName) {
- permissions.forEachIndexed { _, _, it -> serializePermission(it) }
- }
+ tag(tagName) { permissions.forEachIndexed { _, _, it -> serializePermission(it) } }
}
private fun BinaryXmlSerializer.serializePermission(permission: Permission) {
val type = permission.type
when (type) {
- Permission.TYPE_MANIFEST, Permission.TYPE_DYNAMIC -> {}
+ Permission.TYPE_MANIFEST,
+ Permission.TYPE_DYNAMIC -> {}
Permission.TYPE_CONFIG -> return
else -> {
Slog.w(LOG_TAG, "Skipping serializing permission $name with unknown type $type")
@@ -228,11 +232,12 @@
tag(TAG_PERMISSION) {
attributeInterned(ATTR_NAME, name)
// Never serialize one-time permissions as granted.
- val serializedFlags = if (flags.hasBits(PermissionFlags.ONE_TIME)) {
- flags andInv PermissionFlags.RUNTIME_GRANTED
- } else {
- flags
- }
+ val serializedFlags =
+ if (flags.hasBits(PermissionFlags.ONE_TIME)) {
+ flags andInv PermissionFlags.RUNTIME_GRANTED
+ } else {
+ flags
+ }
attributeInt(ATTR_FLAGS, serializedFlags)
}
}
diff --git a/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt b/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt
index 345f101..08ba753 100644
--- a/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt
+++ b/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt
@@ -55,7 +55,8 @@
@Volatile
private var onPermissionFlagsChangedListeners:
- IndexedListSet<OnPermissionFlagsChangedListener> = MutableIndexedListSet()
+ IndexedListSet<OnPermissionFlagsChangedListener> =
+ MutableIndexedListSet()
private val onPermissionFlagsChangedListenersLock = Any()
private val privilegedPermissionAllowlistViolations = MutableIndexedSet<String>()
@@ -73,30 +74,37 @@
override fun MutateStateScope.onInitialized() {
newState.externalState.configPermissions.forEach { (permissionName, permissionEntry) ->
val oldPermission = newState.systemState.permissions[permissionName]
- val newPermission = if (oldPermission != null) {
- if (permissionEntry.gids != null) {
- oldPermission.copy(
- gids = permissionEntry.gids, areGidsPerUser = permissionEntry.perUser
- )
+ val newPermission =
+ if (oldPermission != null) {
+ if (permissionEntry.gids != null) {
+ oldPermission.copy(
+ gids = permissionEntry.gids,
+ areGidsPerUser = permissionEntry.perUser
+ )
+ } else {
+ return@forEach
+ }
} else {
- return@forEach
+ @Suppress("DEPRECATION")
+ val permissionInfo =
+ PermissionInfo().apply {
+ name = permissionName
+ packageName = PLATFORM_PACKAGE_NAME
+ protectionLevel = PermissionInfo.PROTECTION_SIGNATURE
+ }
+ if (permissionEntry.gids != null) {
+ Permission(
+ permissionInfo,
+ false,
+ Permission.TYPE_CONFIG,
+ 0,
+ permissionEntry.gids,
+ permissionEntry.perUser
+ )
+ } else {
+ Permission(permissionInfo, false, Permission.TYPE_CONFIG, 0)
+ }
}
- } else {
- @Suppress("DEPRECATION")
- val permissionInfo = PermissionInfo().apply {
- name = permissionName
- packageName = PLATFORM_PACKAGE_NAME
- protectionLevel = PermissionInfo.PROTECTION_SIGNATURE
- }
- if (permissionEntry.gids != null) {
- Permission(
- permissionInfo, false, Permission.TYPE_CONFIG, 0, permissionEntry.gids,
- permissionEntry.perUser
- )
- } else {
- Permission(permissionInfo, false, Permission.TYPE_CONFIG, 0)
- }
- }
newState.mutateSystemState().mutatePermissions()[permissionName] = newPermission
}
}
@@ -200,30 +208,32 @@
val androidPackage = packageState.androidPackage ?: return
val appId = packageState.appId
androidPackage.requestedPermissions.forEach { permissionName ->
- val permission = newState.systemState.permissions[permissionName]
- ?: return@forEach
+ val permission = newState.systemState.permissions[permissionName] ?: return@forEach
if (!permission.isHardOrSoftRestricted) {
return@forEach
}
- val isRequestedBySystemPackage = anyPackageInAppId(appId) {
- it.isSystem && permissionName in it.androidPackage!!.requestedPermissions
- }
+ val isRequestedBySystemPackage =
+ anyPackageInAppId(appId) {
+ it.isSystem && permissionName in it.androidPackage!!.requestedPermissions
+ }
if (isRequestedBySystemPackage) {
return@forEach
}
val oldFlags = getPermissionFlags(appId, userId, permissionName)
var newFlags = oldFlags andInv PermissionFlags.UPGRADE_EXEMPT
val isExempt = newFlags.hasAnyBit(PermissionFlags.MASK_EXEMPT)
- newFlags = if (permission.isHardRestricted && !isExempt) {
- newFlags or PermissionFlags.RESTRICTION_REVOKED
- } else {
- newFlags andInv PermissionFlags.RESTRICTION_REVOKED
- }
- newFlags = if (permission.isSoftRestricted && !isExempt) {
- newFlags or PermissionFlags.SOFT_RESTRICTED
- } else {
- newFlags andInv PermissionFlags.SOFT_RESTRICTED
- }
+ newFlags =
+ if (permission.isHardRestricted && !isExempt) {
+ newFlags or PermissionFlags.RESTRICTION_REVOKED
+ } else {
+ newFlags andInv PermissionFlags.RESTRICTION_REVOKED
+ }
+ newFlags =
+ if (permission.isSoftRestricted && !isExempt) {
+ newFlags or PermissionFlags.SOFT_RESTRICTED
+ } else {
+ newFlags andInv PermissionFlags.SOFT_RESTRICTED
+ }
setPermissionFlags(appId, userId, permissionName, newFlags)
}
}
@@ -243,15 +253,15 @@
val androidPackage = packageState.androidPackage ?: return
val appId = packageState.appId
androidPackage.requestedPermissions.forEach { permissionName ->
- val permission = newState.systemState.permissions[permissionName]
- ?: return@forEach
+ val permission = newState.systemState.permissions[permissionName] ?: return@forEach
if (!permission.isRuntime || permission.isRemoved) {
return@forEach
}
- val isRequestedByOtherPackages = anyPackageInAppId(appId) {
- it.packageName != packageName &&
- permissionName in it.androidPackage!!.requestedPermissions
- }
+ val isRequestedByOtherPackages =
+ anyPackageInAppId(appId) {
+ it.packageName != packageName &&
+ permissionName in it.androidPackage!!.requestedPermissions
+ }
if (isRequestedByOtherPackages) {
return@forEach
}
@@ -260,13 +270,15 @@
return@forEach
}
var newFlags = oldFlags
- newFlags = if (
- newFlags.hasBits(PermissionFlags.ROLE) || newFlags.hasBits(PermissionFlags.PREGRANT)
- ) {
- newFlags or PermissionFlags.RUNTIME_GRANTED
- } else {
- newFlags andInv PermissionFlags.RUNTIME_GRANTED
- }
+ newFlags =
+ if (
+ newFlags.hasBits(PermissionFlags.ROLE) ||
+ newFlags.hasBits(PermissionFlags.PREGRANT)
+ ) {
+ newFlags or PermissionFlags.RUNTIME_GRANTED
+ } else {
+ newFlags andInv PermissionFlags.RUNTIME_GRANTED
+ }
newFlags = newFlags andInv USER_SETTABLE_MASK
if (newFlags.hasBits(PermissionFlags.LEGACY_GRANTED)) {
newFlags = newFlags or PermissionFlags.IMPLICIT
@@ -285,24 +297,32 @@
if (!canAdoptPermissions(packageName, originalPackageName)) {
return@forEachIndexed
}
- newState.systemState.permissions.forEachIndexed permissions@ {
- permissionIndex, permissionName, oldPermission ->
+ newState.systemState.permissions.forEachIndexed permissions@{
+ permissionIndex,
+ permissionName,
+ oldPermission ->
if (oldPermission.packageName != originalPackageName) {
return@permissions
}
@Suppress("DEPRECATION")
- val newPermissionInfo = PermissionInfo().apply {
- name = oldPermission.permissionInfo.name
- this.packageName = packageName
- protectionLevel = oldPermission.permissionInfo.protectionLevel
- }
+ val newPermissionInfo =
+ PermissionInfo().apply {
+ name = oldPermission.permissionInfo.name
+ this.packageName = packageName
+ protectionLevel = oldPermission.permissionInfo.protectionLevel
+ }
// Different from the old implementation, which removes the GIDs upon permission
// adoption, but adds them back on the next boot, we now just consistently keep the
// GIDs.
- val newPermission = oldPermission.copy(
- permissionInfo = newPermissionInfo, isReconciled = false, appId = 0
- )
- newState.mutateSystemState().mutatePermissions()
+ val newPermission =
+ oldPermission.copy(
+ permissionInfo = newPermissionInfo,
+ isReconciled = false,
+ appId = 0
+ )
+ newState
+ .mutateSystemState()
+ .mutatePermissions()
.putAt(permissionIndex, newPermission)
changedPermissionNames += permissionName
}
@@ -313,18 +333,20 @@
packageName: String,
originalPackageName: String
): Boolean {
- val originalPackageState = newState.externalState.packageStates[originalPackageName]
- ?: return false
+ val originalPackageState =
+ newState.externalState.packageStates[originalPackageName] ?: return false
if (!originalPackageState.isSystem) {
Slog.w(
- LOG_TAG, "Unable to adopt permissions from $originalPackageName to $packageName:" +
+ LOG_TAG,
+ "Unable to adopt permissions from $originalPackageName to $packageName:" +
" original package not in system partition"
)
return false
}
if (originalPackageState.androidPackage != null) {
Slog.w(
- LOG_TAG, "Unable to adopt permissions from $originalPackageName to $packageName:" +
+ LOG_TAG,
+ "Unable to adopt permissions from $originalPackageName to $packageName:" +
" original package still exists"
)
return false
@@ -339,20 +361,25 @@
val isInstantApp = packageState.userStates.allIndexed { _, _, it -> it.isInstantApp }
if (isInstantApp) {
Slog.w(
- LOG_TAG, "Ignoring permission groups declared in package" +
+ LOG_TAG,
+ "Ignoring permission groups declared in package" +
" ${packageState.packageName}: instant apps cannot declare permission groups"
)
return
}
packageState.androidPackage!!.permissionGroups.forEachIndexed { _, parsedPermissionGroup ->
- val newPermissionGroup = PackageInfoUtils.generatePermissionGroupInfo(
- parsedPermissionGroup, PackageManager.GET_META_DATA.toLong()
- )!!
+ val newPermissionGroup =
+ PackageInfoUtils.generatePermissionGroupInfo(
+ parsedPermissionGroup,
+ PackageManager.GET_META_DATA.toLong()
+ )!!
// TODO: Clear permission state on group take-over?
val permissionGroupName = newPermissionGroup.name
val oldPermissionGroup = newState.systemState.permissionGroups[permissionGroupName]
- if (oldPermissionGroup != null &&
- newPermissionGroup.packageName != oldPermissionGroup.packageName) {
+ if (
+ oldPermissionGroup != null &&
+ newPermissionGroup.packageName != oldPermissionGroup.packageName
+ ) {
val newPackageName = newPermissionGroup.packageName
val oldPackageName = oldPermissionGroup.packageName
// Different from the old implementation, which defines permission group on
@@ -361,7 +388,8 @@
// to permissions so that we no longer need to rely on the scan order.
if (!packageState.isSystem) {
Slog.w(
- LOG_TAG, "Ignoring permission group $permissionGroupName declared in" +
+ LOG_TAG,
+ "Ignoring permission group $permissionGroupName declared in" +
" package $newPackageName: already declared in another" +
" package $oldPackageName"
)
@@ -369,14 +397,16 @@
}
if (newState.externalState.packageStates[oldPackageName]?.isSystem == true) {
Slog.w(
- LOG_TAG, "Ignoring permission group $permissionGroupName declared in" +
+ LOG_TAG,
+ "Ignoring permission group $permissionGroupName declared in" +
" system package $newPackageName: already declared in another" +
" system package $oldPackageName"
)
return@forEachIndexed
}
Slog.w(
- LOG_TAG, "Overriding permission group $permissionGroupName with" +
+ LOG_TAG,
+ "Overriding permission group $permissionGroupName with" +
" new declaration in system package $newPackageName: originally" +
" declared in another package $oldPackageName"
)
@@ -393,20 +423,23 @@
val androidPackage = packageState.androidPackage!!
// This may not be the same package as the old permission because the old permission owner
// can be different, hence using this somewhat strange name to prevent misuse.
- val oldNewPackage = oldState.externalState.packageStates[packageState.packageName]
- ?.androidPackage
- val isPackageSigningChanged = oldNewPackage != null &&
- androidPackage.signingDetails != oldNewPackage.signingDetails
+ val oldNewPackage =
+ oldState.externalState.packageStates[packageState.packageName]?.androidPackage
+ val isPackageSigningChanged =
+ oldNewPackage != null && androidPackage.signingDetails != oldNewPackage.signingDetails
androidPackage.permissions.forEachIndexed { _, parsedPermission ->
- val newPermissionInfo = PackageInfoUtils.generatePermissionInfo(
- parsedPermission, PackageManager.GET_META_DATA.toLong()
- )!!
+ val newPermissionInfo =
+ PackageInfoUtils.generatePermissionInfo(
+ parsedPermission,
+ PackageManager.GET_META_DATA.toLong()
+ )!!
val permissionName = newPermissionInfo.name
- val oldPermission = if (parsedPermission.isTree) {
- newState.systemState.permissionTrees[permissionName]
- } else {
- newState.systemState.permissions[permissionName]
- }
+ val oldPermission =
+ if (parsedPermission.isTree) {
+ newState.systemState.permissionTrees[permissionName]
+ } else {
+ newState.systemState.permissions[permissionName]
+ }
// Different from the old implementation, which may add an (incomplete) signature
// permission inside another package's permission tree, we now consistently ignore such
// permissions.
@@ -414,128 +447,152 @@
val newPackageName = newPermissionInfo.packageName
if (permissionTree != null && newPackageName != permissionTree.packageName) {
Slog.w(
- LOG_TAG, "Ignoring permission $permissionName declared in package" +
+ LOG_TAG,
+ "Ignoring permission $permissionName declared in package" +
" $newPackageName: base permission tree ${permissionTree.name} is" +
" declared in another package ${permissionTree.packageName}"
)
return@forEachIndexed
}
- val newPermission = if (oldPermission != null &&
- newPackageName != oldPermission.packageName) {
- val oldPackageName = oldPermission.packageName
- // Only allow system apps to redefine non-system permissions.
- if (!packageState.isSystem) {
- Slog.w(
- LOG_TAG, "Ignoring permission $permissionName declared in package" +
- " $newPackageName: already declared in another package" +
- " $oldPackageName"
- )
- return@forEachIndexed
- }
- if (oldPermission.type == Permission.TYPE_CONFIG && !oldPermission.isReconciled) {
- // It's a config permission and has no owner, take ownership now.
- oldPermission.copy(
- permissionInfo = newPermissionInfo, isReconciled = true,
- type = Permission.TYPE_MANIFEST, appId = packageState.appId
- )
- } else if (newState.externalState.packageStates[oldPackageName]?.isSystem != true) {
- Slog.w(
- LOG_TAG, "Overriding permission $permissionName with new declaration in" +
- " system package $newPackageName: originally declared in another" +
- " package $oldPackageName"
- )
- // Remove permission state on owner change.
- newState.externalState.userIds.forEachIndexed { _, userId ->
- newState.externalState.appIdPackageNames.forEachIndexed { _, appId, _ ->
- setPermissionFlags(appId, userId, permissionName, 0)
- }
- }
- // Different from the old implementation, which removes the GIDs upon permission
- // override, but adds them back on the next boot, we now just consistently keep
- // the GIDs.
- Permission(
- newPermissionInfo, true, Permission.TYPE_MANIFEST, packageState.appId,
- oldPermission.gids, oldPermission.areGidsPerUser
- )
- } else {
- Slog.w(
- LOG_TAG, "Ignoring permission $permissionName declared in system package" +
- " $newPackageName: already declared in another system package" +
- " $oldPackageName"
- )
- return@forEachIndexed
- }
- } else {
- if (oldPermission != null && oldPermission.isReconciled) {
- val isPermissionGroupChanged = newPermissionInfo.isRuntime &&
- newPermissionInfo.group != null &&
- newPermissionInfo.group != oldPermission.groupName
- val isPermissionProtectionChanged =
- oldPermission.type != Permission.TYPE_CONFIG && (
- (newPermissionInfo.isRuntime && !oldPermission.isRuntime) ||
- (newPermissionInfo.isInternal && !oldPermission.isInternal)
+ val newPermission =
+ if (oldPermission != null && newPackageName != oldPermission.packageName) {
+ val oldPackageName = oldPermission.packageName
+ // Only allow system apps to redefine non-system permissions.
+ if (!packageState.isSystem) {
+ Slog.w(
+ LOG_TAG,
+ "Ignoring permission $permissionName declared in package" +
+ " $newPackageName: already declared in another package" +
+ " $oldPackageName"
)
- if (isPermissionGroupChanged || isPermissionProtectionChanged) {
+ return@forEachIndexed
+ }
+ if (
+ oldPermission.type == Permission.TYPE_CONFIG && !oldPermission.isReconciled
+ ) {
+ // It's a config permission and has no owner, take ownership now.
+ oldPermission.copy(
+ permissionInfo = newPermissionInfo,
+ isReconciled = true,
+ type = Permission.TYPE_MANIFEST,
+ appId = packageState.appId
+ )
+ } else if (
+ newState.externalState.packageStates[oldPackageName]?.isSystem != true
+ ) {
+ Slog.w(
+ LOG_TAG,
+ "Overriding permission $permissionName with new declaration in" +
+ " system package $newPackageName: originally declared in another" +
+ " package $oldPackageName"
+ )
+ // Remove permission state on owner change.
newState.externalState.userIds.forEachIndexed { _, userId ->
newState.externalState.appIdPackageNames.forEachIndexed { _, appId, _ ->
- if (isPermissionGroupChanged) {
- // We might auto-grant permissions if any permission of
- // the group is already granted. Hence if the group of
- // a granted permission changes we need to revoke it to
- // avoid having permissions of the new group auto-granted.
- Slog.w(
- LOG_TAG, "Revoking runtime permission $permissionName for" +
- " appId $appId and userId $userId as the permission" +
- " group changed from ${oldPermission.groupName}" +
- " to ${newPermissionInfo.group}"
- )
- }
- if (isPermissionProtectionChanged) {
- Slog.w(
- LOG_TAG, "Revoking permission $permissionName for" +
- " appId $appId and userId $userId as the permission" +
- " protection changed."
- )
- }
setPermissionFlags(appId, userId, permissionName, 0)
}
}
+ // Different from the old implementation, which removes the GIDs upon
+ // permission
+ // override, but adds them back on the next boot, we now just consistently
+ // keep
+ // the GIDs.
+ Permission(
+ newPermissionInfo,
+ true,
+ Permission.TYPE_MANIFEST,
+ packageState.appId,
+ oldPermission.gids,
+ oldPermission.areGidsPerUser
+ )
+ } else {
+ Slog.w(
+ LOG_TAG,
+ "Ignoring permission $permissionName declared in system package" +
+ " $newPackageName: already declared in another system package" +
+ " $oldPackageName"
+ )
+ return@forEachIndexed
+ }
+ } else {
+ if (oldPermission != null && oldPermission.isReconciled) {
+ val isPermissionGroupChanged =
+ newPermissionInfo.isRuntime &&
+ newPermissionInfo.group != null &&
+ newPermissionInfo.group != oldPermission.groupName
+ val isPermissionProtectionChanged =
+ oldPermission.type != Permission.TYPE_CONFIG &&
+ ((newPermissionInfo.isRuntime && !oldPermission.isRuntime) ||
+ (newPermissionInfo.isInternal && !oldPermission.isInternal))
+ if (isPermissionGroupChanged || isPermissionProtectionChanged) {
+ newState.externalState.userIds.forEachIndexed { _, userId ->
+ newState.externalState.appIdPackageNames.forEachIndexed {
+ _,
+ appId,
+ _ ->
+ if (isPermissionGroupChanged) {
+ // We might auto-grant permissions if any permission of
+ // the group is already granted. Hence if the group of
+ // a granted permission changes we need to revoke it to
+ // avoid having permissions of the new group auto-granted.
+ Slog.w(
+ LOG_TAG,
+ "Revoking runtime permission $permissionName for" +
+ " appId $appId and userId $userId as the permission" +
+ " group changed from ${oldPermission.groupName}" +
+ " to ${newPermissionInfo.group}"
+ )
+ }
+ if (isPermissionProtectionChanged) {
+ Slog.w(
+ LOG_TAG,
+ "Revoking permission $permissionName for" +
+ " appId $appId and userId $userId as the permission" +
+ " protection changed."
+ )
+ }
+ setPermissionFlags(appId, userId, permissionName, 0)
+ }
+ }
+ }
+ }
+
+ // Different from the old implementation, which doesn't update the permission
+ // definition upon app update, but does update it on the next boot, we now
+ // consistently update the permission definition upon app update.
+ @Suppress("IfThenToElvis")
+ if (oldPermission != null) {
+ oldPermission.copy(
+ permissionInfo = newPermissionInfo,
+ isReconciled = true,
+ type = Permission.TYPE_MANIFEST,
+ appId = packageState.appId
+ )
+ } else {
+ Permission(
+ newPermissionInfo,
+ true,
+ Permission.TYPE_MANIFEST,
+ packageState.appId
+ )
}
}
- // Different from the old implementation, which doesn't update the permission
- // definition upon app update, but does update it on the next boot, we now
- // consistently update the permission definition upon app update.
- @Suppress("IfThenToElvis")
- if (oldPermission != null) {
- oldPermission.copy(
- permissionInfo = newPermissionInfo, isReconciled = true,
- type = Permission.TYPE_MANIFEST, appId = packageState.appId
- )
- } else {
- Permission(
- newPermissionInfo, true, Permission.TYPE_MANIFEST, packageState.appId
- )
- }
- }
-
if (parsedPermission.isTree) {
newState.mutateSystemState().mutatePermissionTrees()[permissionName] = newPermission
} else {
newState.mutateSystemState().mutatePermissions()[permissionName] = newPermission
- val isPermissionChanged = oldPermission == null ||
- newPackageName != oldPermission.packageName ||
- newPermission.protectionLevel != oldPermission.protectionLevel || (
- oldPermission.isReconciled && (
- (newPermission.isSignature && isPackageSigningChanged) || (
- newPermission.isKnownSigner &&
- newPermission.knownCerts != oldPermission.knownCerts
- ) || (
- newPermission.isRuntime && newPermission.groupName != null &&
- newPermission.groupName != oldPermission.groupName
- )
- )
- )
+ val isPermissionChanged =
+ oldPermission == null ||
+ newPackageName != oldPermission.packageName ||
+ newPermission.protectionLevel != oldPermission.protectionLevel ||
+ (oldPermission.isReconciled &&
+ ((newPermission.isSignature && isPackageSigningChanged) ||
+ (newPermission.isKnownSigner &&
+ newPermission.knownCerts != oldPermission.knownCerts) ||
+ (newPermission.isRuntime &&
+ newPermission.groupName != null &&
+ newPermission.groupName != oldPermission.groupName)))
if (isPermissionChanged) {
changedPermissionNames += permissionName
}
@@ -552,39 +609,47 @@
if (packageState != null && androidPackage == null) {
return
}
- val disabledSystemPackage = newState.externalState.disabledSystemPackageStates[packageName]
- ?.androidPackage
+ val disabledSystemPackage =
+ newState.externalState.disabledSystemPackageStates[packageName]?.androidPackage
// Unlike in the previous implementation, we now also retain permission trees defined by
// disabled system packages for consistency with permissions.
newState.systemState.permissionTrees.forEachReversedIndexed {
- permissionTreeIndex, permissionTreeName, permissionTree ->
- if (permissionTree.packageName == packageName && (
- packageState == null || androidPackage!!.permissions.noneIndexed { _, it ->
- it.isTree && it.name == permissionTreeName
- }
- ) && (
- disabledSystemPackage?.permissions?.anyIndexed { _, it ->
- it.isTree && it.name == permissionTreeName
- } != true
- )) {
+ permissionTreeIndex,
+ permissionTreeName,
+ permissionTree ->
+ if (
+ permissionTree.packageName == packageName &&
+ (packageState == null ||
+ androidPackage!!.permissions.noneIndexed { _, it ->
+ it.isTree && it.name == permissionTreeName
+ }) &&
+ (disabledSystemPackage?.permissions?.anyIndexed { _, it ->
+ it.isTree && it.name == permissionTreeName
+ } != true)
+ ) {
newState.mutateSystemState().mutatePermissionTrees().removeAt(permissionTreeIndex)
}
}
newState.systemState.permissions.forEachReversedIndexed {
- permissionIndex, permissionName, permission ->
+ permissionIndex,
+ permissionName,
+ permission ->
val updatedPermission = updatePermissionIfDynamic(permission)
- newState.mutateSystemState().mutatePermissions()
+ newState
+ .mutateSystemState()
+ .mutatePermissions()
.putAt(permissionIndex, updatedPermission)
- if (updatedPermission.packageName == packageName && (
- packageState == null || androidPackage!!.permissions.noneIndexed { _, it ->
- !it.isTree && it.name == permissionName
- }
- ) && (
- disabledSystemPackage?.permissions?.anyIndexed { _, it ->
- !it.isTree && it.name == permissionName
- } != true
- )) {
+ if (
+ updatedPermission.packageName == packageName &&
+ (packageState == null ||
+ androidPackage!!.permissions.noneIndexed { _, it ->
+ !it.isTree && it.name == permissionName
+ }) &&
+ (disabledSystemPackage?.permissions?.anyIndexed { _, it ->
+ !it.isTree && it.name == permissionName
+ } != true)
+ ) {
// Different from the old implementation where we keep the permission state if the
// permission is declared by a disabled system package (ag/15189282), we now
// shouldn't be notified when the updated system package is removed but the disabled
@@ -608,9 +673,12 @@
val permissionTree = findPermissionTree(permission.name) ?: return permission
@Suppress("DEPRECATION")
return permission.copy(
- permissionInfo = PermissionInfo(permission.permissionInfo).apply {
- packageName = permissionTree.packageName
- }, appId = permissionTree.appId, isReconciled = true
+ permissionInfo =
+ PermissionInfo(permission.permissionInfo).apply {
+ packageName = permissionTree.packageName
+ },
+ appId = permissionTree.appId,
+ isReconciled = true
)
}
@@ -636,8 +704,9 @@
}
private fun MutateStateScope.revokePermissionsOnPackageUpdate(appId: Int) {
- val hasOldPackage = appId in oldState.externalState.appIdPackageNames &&
- anyPackageInAppId(appId, oldState) { true }
+ val hasOldPackage =
+ appId in oldState.externalState.appIdPackageNames &&
+ anyPackageInAppId(appId, oldState) { true }
if (!hasOldPackage) {
// Don't revoke anything if this isn't a package update, i.e. if information about the
// old package isn't available. Notably, this also means skipping packages changed via
@@ -650,46 +719,58 @@
// app updated in an attempt to get unscoped storage. If so, revoke all storage permissions.
val oldTargetSdkVersion =
reducePackageInAppId(appId, Build.VERSION_CODES.CUR_DEVELOPMENT, oldState) {
- targetSdkVersion, packageState ->
+ targetSdkVersion,
+ packageState ->
targetSdkVersion.coerceAtMost(packageState.androidPackage!!.targetSdkVersion)
}
val newTargetSdkVersion =
reducePackageInAppId(appId, Build.VERSION_CODES.CUR_DEVELOPMENT, newState) {
- targetSdkVersion, packageState ->
+ targetSdkVersion,
+ packageState ->
targetSdkVersion.coerceAtMost(packageState.androidPackage!!.targetSdkVersion)
}
@Suppress("ConvertTwoComparisonsToRangeCheck")
- val isTargetSdkVersionDowngraded = oldTargetSdkVersion >= Build.VERSION_CODES.Q &&
- newTargetSdkVersion < Build.VERSION_CODES.Q
+ val isTargetSdkVersionDowngraded =
+ oldTargetSdkVersion >= Build.VERSION_CODES.Q &&
+ newTargetSdkVersion < Build.VERSION_CODES.Q
@Suppress("ConvertTwoComparisonsToRangeCheck")
- val isTargetSdkVersionUpgraded = oldTargetSdkVersion < Build.VERSION_CODES.Q &&
- newTargetSdkVersion >= Build.VERSION_CODES.Q
- val oldIsRequestLegacyExternalStorage = anyPackageInAppId(appId, oldState) {
- it.androidPackage!!.isRequestLegacyExternalStorage
- }
- val newIsRequestLegacyExternalStorage = anyPackageInAppId(appId, newState) {
- it.androidPackage!!.isRequestLegacyExternalStorage
- }
- val isNewlyRequestingLegacyExternalStorage = !isTargetSdkVersionUpgraded &&
- !oldIsRequestLegacyExternalStorage && newIsRequestLegacyExternalStorage
- val shouldRevokeStorageAndMediaPermissions = isNewlyRequestingLegacyExternalStorage ||
- isTargetSdkVersionDowngraded
+ val isTargetSdkVersionUpgraded =
+ oldTargetSdkVersion < Build.VERSION_CODES.Q &&
+ newTargetSdkVersion >= Build.VERSION_CODES.Q
+ val oldIsRequestLegacyExternalStorage =
+ anyPackageInAppId(appId, oldState) {
+ it.androidPackage!!.isRequestLegacyExternalStorage
+ }
+ val newIsRequestLegacyExternalStorage =
+ anyPackageInAppId(appId, newState) {
+ it.androidPackage!!.isRequestLegacyExternalStorage
+ }
+ val isNewlyRequestingLegacyExternalStorage =
+ !isTargetSdkVersionUpgraded &&
+ !oldIsRequestLegacyExternalStorage &&
+ newIsRequestLegacyExternalStorage
+ val shouldRevokeStorageAndMediaPermissions =
+ isNewlyRequestingLegacyExternalStorage || isTargetSdkVersionDowngraded
if (shouldRevokeStorageAndMediaPermissions) {
newState.userStates.forEachIndexed { _, userId, userState ->
userState.appIdPermissionFlags[appId]?.forEachReversedIndexed {
- _, permissionName, oldFlags ->
+ _,
+ permissionName,
+ oldFlags ->
// Do not revoke the permission during an upgrade if it's POLICY_FIXED or
// SYSTEM_FIXED. Otherwise the user cannot grant back the permission.
- if (permissionName in STORAGE_AND_MEDIA_PERMISSIONS &&
- oldFlags.hasBits(PermissionFlags.RUNTIME_GRANTED) &&
- !oldFlags.hasAnyBit(SYSTEM_OR_POLICY_FIXED_MASK)) {
+ if (
+ permissionName in STORAGE_AND_MEDIA_PERMISSIONS &&
+ oldFlags.hasBits(PermissionFlags.RUNTIME_GRANTED) &&
+ !oldFlags.hasAnyBit(SYSTEM_OR_POLICY_FIXED_MASK)
+ ) {
Slog.v(
- LOG_TAG, "Revoking storage permission: $permissionName for appId: " +
+ LOG_TAG,
+ "Revoking storage permission: $permissionName for appId: " +
" $appId and user: $userId"
)
- val newFlags = oldFlags andInv (
- PermissionFlags.RUNTIME_GRANTED or USER_SETTABLE_MASK
- )
+ val newFlags =
+ oldFlags andInv (PermissionFlags.RUNTIME_GRANTED or USER_SETTABLE_MASK)
setPermissionFlags(appId, userId, permissionName, newFlags)
}
}
@@ -704,9 +785,10 @@
val externalState = newState.externalState
externalState.userIds.forEachIndexed { _, userId ->
externalState.appIdPackageNames.forEachIndexed { _, appId, _ ->
- val isPermissionRequested = anyPackageInAppId(appId) {
- permissionName in it.androidPackage!!.requestedPermissions
- }
+ val isPermissionRequested =
+ anyPackageInAppId(appId) {
+ permissionName in it.androidPackage!!.requestedPermissions
+ }
if (isPermissionRequested) {
evaluatePermissionState(appId, userId, permissionName, installedPackageState)
}
@@ -720,7 +802,9 @@
) {
newState.externalState.userIds.forEachIndexed { _, userId ->
evaluateAllPermissionStatesForPackageAndUser(
- packageState, userId, installedPackageState
+ packageState,
+ userId,
+ installedPackageState
)
}
}
@@ -732,7 +816,10 @@
) {
packageState.androidPackage?.requestedPermissions?.forEach { permissionName ->
evaluatePermissionState(
- packageState.appId, userId, permissionName, installedPackageState
+ packageState.appId,
+ userId,
+ permissionName,
+ installedPackageState
)
}
}
@@ -779,57 +866,71 @@
val wasGranted = oldFlags.hasBits(PermissionFlags.INSTALL_GRANTED)
if (!wasGranted) {
val wasRevoked = oldFlags.hasBits(PermissionFlags.INSTALL_REVOKED)
- val isRequestedByInstalledPackage = installedPackageState != null &&
- permissionName in installedPackageState.androidPackage!!.requestedPermissions
+ val isRequestedByInstalledPackage =
+ installedPackageState != null &&
+ permissionName in
+ installedPackageState.androidPackage!!.requestedPermissions
val isRequestedBySystemPackage =
requestingPackageStates.anyIndexed { _, it -> it.isSystem }
- val isCompatibilityPermission = requestingPackageStates.anyIndexed { _, it ->
- isCompatibilityPermissionForPackage(it.androidPackage!!, permissionName)
- }
+ val isCompatibilityPermission =
+ requestingPackageStates.anyIndexed { _, it ->
+ isCompatibilityPermissionForPackage(it.androidPackage!!, permissionName)
+ }
// If this is an existing, non-system package,
// then we can't add any new permissions to it.
// Except if this is a permission that was added to the platform
- var newFlags = if (!wasRevoked || isRequestedByInstalledPackage ||
- isRequestedBySystemPackage || isCompatibilityPermission) {
- PermissionFlags.INSTALL_GRANTED
- } else {
- PermissionFlags.INSTALL_REVOKED
- }
+ var newFlags =
+ if (
+ !wasRevoked ||
+ isRequestedByInstalledPackage ||
+ isRequestedBySystemPackage ||
+ isCompatibilityPermission
+ ) {
+ PermissionFlags.INSTALL_GRANTED
+ } else {
+ PermissionFlags.INSTALL_REVOKED
+ }
if (permission.isAppOp) {
- newFlags = newFlags or (
- oldFlags and (PermissionFlags.ROLE or PermissionFlags.USER_SET)
- )
+ newFlags =
+ newFlags or
+ (oldFlags and (PermissionFlags.ROLE or PermissionFlags.USER_SET))
}
setPermissionFlags(appId, userId, permissionName, newFlags)
}
} else if (permission.isSignature || permission.isInternal) {
val wasProtectionGranted = oldFlags.hasBits(PermissionFlags.PROTECTION_GRANTED)
- var newFlags = if (hasMissingPackage && wasProtectionGranted) {
- // Keep the non-runtime permission grants for shared UID with missing androidPackage
- PermissionFlags.PROTECTION_GRANTED
- } else {
- val mayGrantByPrivileged = !permission.isPrivileged ||
- requestingPackageStates.anyIndexed { _, it ->
- checkPrivilegedPermissionAllowlist(it, permission)
- }
- val shouldGrantBySignature = permission.isSignature &&
- requestingPackageStates.anyIndexed { _, it ->
- shouldGrantPermissionBySignature(it, permission)
- }
- val shouldGrantByProtectionFlags = requestingPackageStates.anyIndexed { _, it ->
- shouldGrantPermissionByProtectionFlags(it, permission)
- }
- if (mayGrantByPrivileged &&
- (shouldGrantBySignature || shouldGrantByProtectionFlags)) {
+ var newFlags =
+ if (hasMissingPackage && wasProtectionGranted) {
+ // Keep the non-runtime permission grants for shared UID with missing
+ // androidPackage
PermissionFlags.PROTECTION_GRANTED
} else {
- 0
+ val mayGrantByPrivileged =
+ !permission.isPrivileged ||
+ requestingPackageStates.anyIndexed { _, it ->
+ checkPrivilegedPermissionAllowlist(it, permission)
+ }
+ val shouldGrantBySignature =
+ permission.isSignature &&
+ requestingPackageStates.anyIndexed { _, it ->
+ shouldGrantPermissionBySignature(it, permission)
+ }
+ val shouldGrantByProtectionFlags =
+ requestingPackageStates.anyIndexed { _, it ->
+ shouldGrantPermissionByProtectionFlags(it, permission)
+ }
+ if (
+ mayGrantByPrivileged &&
+ (shouldGrantBySignature || shouldGrantByProtectionFlags)
+ ) {
+ PermissionFlags.PROTECTION_GRANTED
+ } else {
+ 0
+ }
}
- }
if (permission.isAppOp) {
- newFlags = newFlags or (
- oldFlags and (PermissionFlags.ROLE or PermissionFlags.USER_SET)
- )
+ newFlags =
+ newFlags or (oldFlags and (PermissionFlags.ROLE or PermissionFlags.USER_SET))
}
// Different from the old implementation, which seemingly allows granting an
// unallowlisted privileged permission via development or role but revokes it upon next
@@ -840,9 +941,9 @@
newFlags = newFlags or (oldFlags and PermissionFlags.RUNTIME_GRANTED)
}
if (permission.isRole) {
- newFlags = newFlags or (
- oldFlags and (PermissionFlags.ROLE or PermissionFlags.RUNTIME_GRANTED)
- )
+ newFlags =
+ newFlags or
+ (oldFlags and (PermissionFlags.ROLE or PermissionFlags.RUNTIME_GRANTED))
}
setPermissionFlags(appId, userId, permissionName, newFlags)
} else if (permission.isRuntime) {
@@ -850,7 +951,9 @@
val wasRevoked = newFlags != 0 && !PermissionFlags.isPermissionGranted(newFlags)
val targetSdkVersion =
requestingPackageStates.reduceIndexed(Build.VERSION_CODES.CUR_DEVELOPMENT) {
- targetSdkVersion, _, packageState ->
+ targetSdkVersion,
+ _,
+ packageState ->
targetSdkVersion.coerceAtMost(packageState.androidPackage!!.targetSdkVersion)
}
if (targetSdkVersion < Build.VERSION_CODES.M) {
@@ -883,23 +986,27 @@
}
}
val wasGrantedByImplicit = newFlags.hasBits(PermissionFlags.IMPLICIT_GRANTED)
- val isLeanbackNotificationsPermission = newState.externalState.isLeanback &&
- permissionName in NOTIFICATIONS_PERMISSIONS
- val isImplicitPermission = requestingPackageStates.anyIndexed { _, it ->
- permissionName in it.androidPackage!!.implicitPermissions
- }
- val sourcePermissions = newState.externalState
- .implicitToSourcePermissions[permissionName]
- val isAnySourcePermissionNonRuntime = sourcePermissions?.anyIndexed {
- _, sourcePermissionName ->
- val sourcePermission = newState.systemState.permissions[sourcePermissionName]
- checkNotNull(sourcePermission) {
- "Unknown source permission $sourcePermissionName in split permissions"
+ val isLeanbackNotificationsPermission =
+ newState.externalState.isLeanback && permissionName in NOTIFICATIONS_PERMISSIONS
+ val isImplicitPermission =
+ requestingPackageStates.anyIndexed { _, it ->
+ permissionName in it.androidPackage!!.implicitPermissions
}
- !sourcePermission.isRuntime
- } ?: false
- val shouldGrantByImplicit = isLeanbackNotificationsPermission ||
- (isImplicitPermission && isAnySourcePermissionNonRuntime)
+ val sourcePermissions =
+ newState.externalState.implicitToSourcePermissions[permissionName]
+ val isAnySourcePermissionNonRuntime =
+ sourcePermissions?.anyIndexed { _, sourcePermissionName ->
+ val sourcePermission =
+ newState.systemState.permissions[sourcePermissionName]
+ checkNotNull(sourcePermission) {
+ "Unknown source permission $sourcePermissionName in split permissions"
+ }
+ !sourcePermission.isRuntime
+ }
+ ?: false
+ val shouldGrantByImplicit =
+ isLeanbackNotificationsPermission ||
+ (isImplicitPermission && isAnySourcePermissionNonRuntime)
if (shouldGrantByImplicit) {
newFlags = newFlags or PermissionFlags.IMPLICIT_GRANTED
if (wasRevoked) {
@@ -907,26 +1014,31 @@
}
} else {
newFlags = newFlags andInv PermissionFlags.IMPLICIT_GRANTED
- if ((wasGrantedByLegacy || wasGrantedByImplicit) &&
- newFlags.hasBits(PermissionFlags.APP_OP_REVOKED)) {
+ if (
+ (wasGrantedByLegacy || wasGrantedByImplicit) &&
+ newFlags.hasBits(PermissionFlags.APP_OP_REVOKED)
+ ) {
// The permission was granted from a compatibility grant or an implicit
// grant, however this flag might still be set if the user denied this
// permission in the settings. Hence upon app upgrade and when this
// permission is no longer LEGACY_GRANTED or IMPLICIT_GRANTED and we revoke
// the permission, we want to remove this flag so that the app can request
// the permission again.
- newFlags = newFlags andInv (
- PermissionFlags.RUNTIME_GRANTED or PermissionFlags.APP_OP_REVOKED
- )
+ newFlags =
+ newFlags andInv
+ (PermissionFlags.RUNTIME_GRANTED or PermissionFlags.APP_OP_REVOKED)
}
}
if (!isImplicitPermission && hasImplicitFlag) {
newFlags = newFlags andInv PermissionFlags.IMPLICIT
var shouldRetainAsNearbyDevices = false
if (permissionName in NEARBY_DEVICES_PERMISSIONS) {
- val accessBackgroundLocationFlags = getPermissionFlags(
- appId, userId, Manifest.permission.ACCESS_BACKGROUND_LOCATION
- )
+ val accessBackgroundLocationFlags =
+ getPermissionFlags(
+ appId,
+ userId,
+ Manifest.permission.ACCESS_BACKGROUND_LOCATION
+ )
shouldRetainAsNearbyDevices =
PermissionFlags.isAppOpGranted(accessBackgroundLocationFlags) &&
!accessBackgroundLocationFlags.hasBits(PermissionFlags.IMPLICIT)
@@ -937,46 +1049,57 @@
newFlags = newFlags or PermissionFlags.RUNTIME_GRANTED
}
} else {
- newFlags = newFlags andInv (
- PermissionFlags.RUNTIME_GRANTED or PermissionFlags.USER_SET or
- PermissionFlags.USER_FIXED
- )
+ newFlags =
+ newFlags andInv
+ (PermissionFlags.RUNTIME_GRANTED or
+ PermissionFlags.USER_SET or
+ PermissionFlags.USER_FIXED)
}
}
}
val wasExempt = newFlags.hasAnyBit(PermissionFlags.MASK_EXEMPT)
val wasRestricted = newFlags.hasAnyBit(PermissionFlags.MASK_RESTRICTED)
- val isExempt = if (permission.isHardOrSoftRestricted && !wasExempt && !wasRestricted) {
- // All restricted permissions start as exempt. If there's an installer for the
- // package, we will drop this UPGRADE_EXEMPT flag when we receive the
- // onPackageInstalled() callback and set up the INSTALLER_EXEMPT flags.
- // UPGRADE_EXEMPT is chosen instead of other flags because it is the same flag that
- // was assigned to pre-installed apps in RuntimePermissionsUpgradeController, and to
- // apps with missing permission state.
- // This way we make sure both pre-installed apps, and apps updated/installed after
- // a rollback snapshot is taken, can get the allowlist for permissions that won't be
- // allowlisted otherwise.
- newFlags = newFlags or PermissionFlags.UPGRADE_EXEMPT
- true
- } else {
- wasExempt
- }
- newFlags = if (permission.isHardRestricted && !isExempt) {
- newFlags or PermissionFlags.RESTRICTION_REVOKED
- } else {
- newFlags andInv PermissionFlags.RESTRICTION_REVOKED
- }
- newFlags = if (permission.isSoftRestricted && !isExempt) {
- newFlags or PermissionFlags.SOFT_RESTRICTED
- } else {
- newFlags andInv PermissionFlags.SOFT_RESTRICTED
- }
+ val isExempt =
+ if (permission.isHardOrSoftRestricted && !wasExempt && !wasRestricted) {
+ // All restricted permissions start as exempt. If there's an installer for the
+ // package, we will drop this UPGRADE_EXEMPT flag when we receive the
+ // onPackageInstalled() callback and set up the INSTALLER_EXEMPT flags.
+ // UPGRADE_EXEMPT is chosen instead of other flags because it is the same flag
+ // that
+ // was assigned to pre-installed apps in RuntimePermissionsUpgradeController,
+ // and to
+ // apps with missing permission state.
+ // This way we make sure both pre-installed apps, and apps updated/installed
+ // after
+ // a rollback snapshot is taken, can get the allowlist for permissions that
+ // won't be
+ // allowlisted otherwise.
+ newFlags = newFlags or PermissionFlags.UPGRADE_EXEMPT
+ true
+ } else {
+ wasExempt
+ }
+ newFlags =
+ if (permission.isHardRestricted && !isExempt) {
+ newFlags or PermissionFlags.RESTRICTION_REVOKED
+ } else {
+ newFlags andInv PermissionFlags.RESTRICTION_REVOKED
+ }
+ newFlags =
+ if (permission.isSoftRestricted && !isExempt) {
+ newFlags or PermissionFlags.SOFT_RESTRICTED
+ } else {
+ newFlags andInv PermissionFlags.SOFT_RESTRICTED
+ }
setPermissionFlags(appId, userId, permissionName, newFlags)
} else {
- Slog.e(LOG_TAG, "Unknown protection level ${permission.protectionLevel}" +
- "for permission ${permission.name} while evaluating permission state" +
- "for appId $appId and userId $userId")
+ Slog.e(
+ LOG_TAG,
+ "Unknown protection level ${permission.protectionLevel}" +
+ "for permission ${permission.name} while evaluating permission state" +
+ "for appId $appId and userId $userId"
+ )
}
}
@@ -985,7 +1108,7 @@
forEachPackageInAppId(appId) {
implicitPermissions += it.androidPackage!!.implicitPermissions
}
- implicitPermissions.forEachIndexed implicitPermissions@ { _, implicitPermissionName ->
+ implicitPermissions.forEachIndexed implicitPermissions@{ _, implicitPermissionName ->
val implicitPermission = newState.systemState.permissions[implicitPermissionName]
checkNotNull(implicitPermission) {
"Unknown implicit permission $implicitPermissionName in split permissions"
@@ -999,10 +1122,11 @@
if (!isNewPermission) {
return@implicitPermissions
}
- val sourcePermissions = newState.externalState
- .implicitToSourcePermissions[implicitPermissionName] ?: return@implicitPermissions
+ val sourcePermissions =
+ newState.externalState.implicitToSourcePermissions[implicitPermissionName]
+ ?: return@implicitPermissions
var newFlags = getPermissionFlags(appId, userId, implicitPermissionName)
- sourcePermissions.forEachIndexed sourcePermissions@ { _, sourcePermissionName ->
+ sourcePermissions.forEachIndexed sourcePermissions@{ _, sourcePermissionName ->
val sourcePermission = newState.systemState.permissions[sourcePermissionName]
checkNotNull(sourcePermission) {
"Unknown source permission $sourcePermissionName in split permissions"
@@ -1032,11 +1156,14 @@
permissionName: String
): Boolean {
for (compatibilityPermission in CompatibilityPermissionInfo.COMPAT_PERMS) {
- if (compatibilityPermission.name == permissionName &&
- androidPackage.targetSdkVersion < compatibilityPermission.sdkVersion) {
+ if (
+ compatibilityPermission.name == permissionName &&
+ androidPackage.targetSdkVersion < compatibilityPermission.sdkVersion
+ ) {
Slog.i(
- LOG_TAG, "Auto-granting $permissionName to old package" +
- " ${androidPackage.packageName}"
+ LOG_TAG,
+ "Auto-granting $permissionName to old package" +
+ " ${androidPackage.packageName}"
)
return true
}
@@ -1058,15 +1185,23 @@
// and the defining package still trusts the old certificate for permissions
// - or it shares the above relationships with the system package
val packageSigningDetails = packageState.androidPackage!!.signingDetails
- val sourceSigningDetails = newState.externalState
- .packageStates[permission.packageName]?.androidPackage?.signingDetails
- val platformSigningDetails = newState.externalState
- .packageStates[PLATFORM_PACKAGE_NAME]!!.androidPackage!!.signingDetails
- return sourceSigningDetails?.hasCommonSignerWithCapability(packageSigningDetails,
- SigningDetails.CertCapabilities.PERMISSION) == true ||
+ val sourceSigningDetails =
+ newState.externalState.packageStates[permission.packageName]
+ ?.androidPackage
+ ?.signingDetails
+ val platformSigningDetails =
+ newState.externalState.packageStates[PLATFORM_PACKAGE_NAME]!!
+ .androidPackage!!
+ .signingDetails
+ return sourceSigningDetails?.hasCommonSignerWithCapability(
+ packageSigningDetails,
+ SigningDetails.CertCapabilities.PERMISSION
+ ) == true ||
packageSigningDetails.hasAncestorOrSelf(platformSigningDetails) ||
- platformSigningDetails.checkCapability(packageSigningDetails,
- SigningDetails.CertCapabilities.PERMISSION)
+ platformSigningDetails.checkCapability(
+ packageSigningDetails,
+ SigningDetails.CertCapabilities.PERMISSION
+ )
}
private fun MutateStateScope.checkPrivilegedPermissionAllowlist(
@@ -1082,8 +1217,9 @@
if (!(packageState.isSystem && packageState.isPrivileged)) {
return true
}
- if (permission.packageName !in
- newState.externalState.privilegedPermissionAllowlistPackages) {
+ if (
+ permission.packageName !in newState.externalState.privilegedPermissionAllowlistPackages
+ ) {
return true
}
val allowlistState = getPrivilegedPermissionAllowlistState(packageState, permission.name)
@@ -1099,13 +1235,15 @@
// Apps that are in updated apex's do not need to be allowlisted
if (!packageState.isApkInUpdatedApex) {
Slog.w(
- LOG_TAG, "Privileged permission ${permission.name} for package" +
- " ${packageState.packageName} (${packageState.path}) not in" +
- " privileged permission allowlist"
+ LOG_TAG,
+ "Privileged permission ${permission.name} for package" +
+ " ${packageState.packageName} (${packageState.path}) not in" +
+ " privileged permission allowlist"
)
if (RoSystemProperties.CONTROL_PRIVAPP_PERMISSIONS_ENFORCE) {
- privilegedPermissionAllowlistViolations += "${packageState.packageName}" +
- " (${packageState.path}): ${permission.name}"
+ privilegedPermissionAllowlistViolations +=
+ "${packageState.packageName}" +
+ " (${packageState.path}): ${permission.name}"
}
}
}
@@ -1124,32 +1262,40 @@
val apexModuleName = packageState.apexModuleName
val packageName = packageState.packageName
return when {
- packageState.isVendor -> permissionAllowlist.getVendorPrivilegedAppAllowlistState(
- packageName, permissionName
- )
- packageState.isProduct -> permissionAllowlist.getProductPrivilegedAppAllowlistState(
- packageName, permissionName
- )
+ packageState.isVendor ->
+ permissionAllowlist.getVendorPrivilegedAppAllowlistState(
+ packageName,
+ permissionName
+ )
+ packageState.isProduct ->
+ permissionAllowlist.getProductPrivilegedAppAllowlistState(
+ packageName,
+ permissionName
+ )
packageState.isSystemExt ->
permissionAllowlist.getSystemExtPrivilegedAppAllowlistState(
- packageName, permissionName
+ packageName,
+ permissionName
)
apexModuleName != null -> {
- val nonApexAllowlistState = permissionAllowlist.getPrivilegedAppAllowlistState(
- packageName, permissionName
- )
+ val nonApexAllowlistState =
+ permissionAllowlist.getPrivilegedAppAllowlistState(packageName, permissionName)
if (nonApexAllowlistState != null) {
// TODO(andreionea): Remove check as soon as all apk-in-apex
// permission allowlists are migrated.
Slog.w(
- LOG_TAG, "Package $packageName is an APK in APEX but has permission" +
+ LOG_TAG,
+ "Package $packageName is an APK in APEX but has permission" +
" allowlist on the system image, please bundle the allowlist in the" +
" $apexModuleName APEX instead"
)
}
- val apexAllowlistState = permissionAllowlist.getApexPrivilegedAppAllowlistState(
- apexModuleName, packageName, permissionName
- )
+ val apexAllowlistState =
+ permissionAllowlist.getApexPrivilegedAppAllowlistState(
+ apexModuleName,
+ packageName,
+ permissionName
+ )
apexAllowlistState ?: nonApexAllowlistState
}
else -> permissionAllowlist.getPrivilegedAppAllowlistState(packageName, permissionName)
@@ -1208,18 +1354,19 @@
val knownPackages = newState.externalState.knownPackages
val packageName = packageState.packageName
if ((permission.isPrivileged || permission.isOem) && packageState.isSystem) {
- val shouldGrant = if (packageState.isUpdatedSystemApp) {
- // For updated system applications, a privileged/oem permission
- // is granted only if it had been defined by the original application.
- val disabledSystemPackageState = newState.externalState
- .disabledSystemPackageStates[packageState.packageName]
- val disabledSystemPackage = disabledSystemPackageState?.androidPackage
- disabledSystemPackage != null &&
- permission.name in disabledSystemPackage.requestedPermissions &&
- shouldGrantPrivilegedOrOemPermission(disabledSystemPackageState, permission)
- } else {
- shouldGrantPrivilegedOrOemPermission(packageState, permission)
- }
+ val shouldGrant =
+ if (packageState.isUpdatedSystemApp) {
+ // For updated system applications, a privileged/oem permission
+ // is granted only if it had been defined by the original application.
+ val disabledSystemPackageState =
+ newState.externalState.disabledSystemPackageStates[packageState.packageName]
+ val disabledSystemPackage = disabledSystemPackageState?.androidPackage
+ disabledSystemPackage != null &&
+ permission.name in disabledSystemPackage.requestedPermissions &&
+ shouldGrantPrivilegedOrOemPermission(disabledSystemPackageState, permission)
+ } else {
+ shouldGrantPrivilegedOrOemPermission(packageState, permission)
+ }
if (shouldGrant) {
return true
}
@@ -1230,16 +1377,18 @@
// we still want to blindly grant it to old apps.
return true
}
- if (permission.isInstaller && (
- packageName in knownPackages[KnownPackages.PACKAGE_INSTALLER]!! ||
- packageName in knownPackages[KnownPackages.PACKAGE_PERMISSION_CONTROLLER]!!
- )) {
+ if (
+ permission.isInstaller &&
+ (packageName in knownPackages[KnownPackages.PACKAGE_INSTALLER]!! ||
+ packageName in knownPackages[KnownPackages.PACKAGE_PERMISSION_CONTROLLER]!!)
+ ) {
// If this permission is to be granted to the system installer and
// this app is an installer or permission controller, then it gets the permission.
return true
}
- if (permission.isVerifier &&
- packageName in knownPackages[KnownPackages.PACKAGE_VERIFIER]!!) {
+ if (
+ permission.isVerifier && packageName in knownPackages[KnownPackages.PACKAGE_VERIFIER]!!
+ ) {
// If this permission is to be granted to the system verifier and
// this app is a verifier, then it gets the permission.
return true
@@ -1248,53 +1397,67 @@
// Any pre-installed system app is allowed to get this permission.
return true
}
- if (permission.isKnownSigner &&
- androidPackage.signingDetails.hasAncestorOrSelfWithDigest(permission.knownCerts)) {
+ if (
+ permission.isKnownSigner &&
+ androidPackage.signingDetails.hasAncestorOrSelfWithDigest(permission.knownCerts)
+ ) {
// If the permission is to be granted to a known signer then check if any of this
// app's signing certificates are in the trusted certificate digest Set.
return true
}
- if (permission.isSetup &&
- packageName in knownPackages[KnownPackages.PACKAGE_SETUP_WIZARD]!!) {
+ if (
+ permission.isSetup && packageName in knownPackages[KnownPackages.PACKAGE_SETUP_WIZARD]!!
+ ) {
// If this permission is to be granted to the system setup wizard and
// this app is a setup wizard, then it gets the permission.
return true
}
- if (permission.isSystemTextClassifier &&
- packageName in knownPackages[KnownPackages.PACKAGE_SYSTEM_TEXT_CLASSIFIER]!!) {
+ if (
+ permission.isSystemTextClassifier &&
+ packageName in knownPackages[KnownPackages.PACKAGE_SYSTEM_TEXT_CLASSIFIER]!!
+ ) {
// Special permissions for the system default text classifier.
return true
}
- if (permission.isConfigurator &&
- packageName in knownPackages[KnownPackages.PACKAGE_CONFIGURATOR]!!) {
+ if (
+ permission.isConfigurator &&
+ packageName in knownPackages[KnownPackages.PACKAGE_CONFIGURATOR]!!
+ ) {
// Special permissions for the device configurator.
return true
}
- if (permission.isIncidentReportApprover &&
- packageName in knownPackages[KnownPackages.PACKAGE_INCIDENT_REPORT_APPROVER]!!) {
+ if (
+ permission.isIncidentReportApprover &&
+ packageName in knownPackages[KnownPackages.PACKAGE_INCIDENT_REPORT_APPROVER]!!
+ ) {
// If this permission is to be granted to the incident report approver and
// this app is the incident report approver, then it gets the permission.
return true
}
- if (permission.isAppPredictor &&
- packageName in knownPackages[KnownPackages.PACKAGE_APP_PREDICTOR]!!) {
+ if (
+ permission.isAppPredictor &&
+ packageName in knownPackages[KnownPackages.PACKAGE_APP_PREDICTOR]!!
+ ) {
// Special permissions for the system app predictor.
return true
}
- if (permission.isCompanion &&
- packageName in knownPackages[KnownPackages.PACKAGE_COMPANION]!!) {
+ if (
+ permission.isCompanion &&
+ packageName in knownPackages[KnownPackages.PACKAGE_COMPANION]!!
+ ) {
// Special permissions for the system companion device manager.
return true
}
- if (permission.isRetailDemo &&
- packageName in knownPackages[KnownPackages.PACKAGE_RETAIL_DEMO]!!) {
+ if (
+ permission.isRetailDemo &&
+ packageName in knownPackages[KnownPackages.PACKAGE_RETAIL_DEMO]!!
+ ) {
// Special permission granted only to the OEM specified retail demo app.
// Note that the original code was passing app ID as UID, so this behavior is kept
// unchanged.
return true
}
- if (permission.isRecents &&
- packageName in knownPackages[KnownPackages.PACKAGE_RECENTS]!!) {
+ if (permission.isRecents && packageName in knownPackages[KnownPackages.PACKAGE_RECENTS]!!) {
// Special permission for the recents app.
return true
}
@@ -1319,9 +1482,10 @@
// flag.
if (packageState.isVendor && !permission.isVendorPrivileged) {
Slog.w(
- LOG_TAG, "Permission $permissionName cannot be granted to privileged" +
- " vendor app $packageName because it isn't a vendorPrivileged" +
- " permission"
+ LOG_TAG,
+ "Permission $permissionName cannot be granted to privileged" +
+ " vendor app $packageName because it isn't a vendorPrivileged" +
+ " permission"
)
return false
}
@@ -1330,8 +1494,11 @@
}
permission.isOem -> {
if (packageState.isOem) {
- val allowlistState = newState.externalState.permissionAllowlist
- .getOemAppAllowlistState(packageName, permissionName)
+ val allowlistState =
+ newState.externalState.permissionAllowlist.getOemAppAllowlistState(
+ packageName,
+ permissionName
+ )
checkNotNull(allowlistState) {
"OEM permission $permissionName requested by package" +
" $packageName must be explicitly declared granted or not"
@@ -1358,13 +1525,18 @@
val appId = externalState.packageStates[packageName]?.appId ?: continue
newState.userStates.forEachIndexed { _, userId, _ ->
evaluatePermissionState(
- appId, userId, Manifest.permission.PACKAGE_USAGE_STATS, null
+ appId,
+ userId,
+ Manifest.permission.PACKAGE_USAGE_STATS,
+ null
)
}
}
if (!privilegedPermissionAllowlistViolations.isEmpty()) {
- throw IllegalStateException("Signature|privileged permissions not in privileged" +
- " permission allowlist: $privilegedPermissionAllowlistViolations")
+ throw IllegalStateException(
+ "Signature|privileged permissions not in privileged" +
+ " permission allowlist: $privilegedPermissionAllowlistViolations"
+ )
}
}
@@ -1389,10 +1561,14 @@
fun GetStateScope.findPermissionTree(permissionName: String): Permission? =
state.systemState.permissionTrees.firstNotNullOfOrNullIndexed {
- _, permissionTreeName, permissionTree ->
- if (permissionName.startsWith(permissionTreeName) &&
- permissionName.length > permissionTreeName.length &&
- permissionName[permissionTreeName.length] == '.') {
+ _,
+ permissionTreeName,
+ permissionTree ->
+ if (
+ permissionName.startsWith(permissionTreeName) &&
+ permissionName.length > permissionTreeName.length &&
+ permissionName[permissionTreeName.length] == '.'
+ ) {
permissionTree
} else {
null
@@ -1403,15 +1579,11 @@
newState.mutateSystemState().mutatePermissionTrees()[permission.name] = permission
}
- /**
- * returns all permission group definitions available in the system
- */
+ /** returns all permission group definitions available in the system */
fun GetStateScope.getPermissionGroups(): IndexedMap<String, PermissionGroupInfo> =
state.systemState.permissionGroups
- /**
- * returns all permission definitions available in the system
- */
+ /** returns all permission definitions available in the system */
fun GetStateScope.getPermissions(): IndexedMap<String, Permission> =
state.systemState.permissions
@@ -1430,11 +1602,8 @@
fun GetStateScope.getUidPermissionFlags(appId: Int, userId: Int): IndexedMap<String, Int>? =
state.userStates[userId]?.appIdPermissionFlags?.get(appId)
- fun GetStateScope.getPermissionFlags(
- appId: Int,
- userId: Int,
- permissionName: String
- ): Int = getPermissionFlags(state, appId, userId, permissionName)
+ fun GetStateScope.getPermissionFlags(appId: Int, userId: Int, permissionName: String): Int =
+ getPermissionFlags(state, appId, userId, permissionName)
private fun MutateStateScope.getOldStatePermissionFlags(
appId: Int,
@@ -1465,8 +1634,10 @@
flagMask: Int,
flagValues: Int
): Boolean {
- val oldFlags = newState.userStates[userId]!!.appIdPermissionFlags[appId]
- .getWithDefault(permissionName, 0)
+ val oldFlags =
+ newState.userStates[userId]!!
+ .appIdPermissionFlags[appId]
+ .getWithDefault(permissionName, 0)
val newFlags = (oldFlags andInv flagMask) or (flagValues and flagMask)
if (oldFlags == newFlags) {
return false
@@ -1517,38 +1688,37 @@
private const val PLATFORM_PACKAGE_NAME = "android"
// A set of permissions that we don't want to revoke when they are no longer implicit.
- private val RETAIN_IMPLICIT_FLAGS_PERMISSIONS = indexedSetOf(
- Manifest.permission.ACCESS_MEDIA_LOCATION,
- Manifest.permission.ACTIVITY_RECOGNITION,
- Manifest.permission.READ_MEDIA_AUDIO,
- Manifest.permission.READ_MEDIA_IMAGES,
- Manifest.permission.READ_MEDIA_VIDEO,
- )
+ private val RETAIN_IMPLICIT_FLAGS_PERMISSIONS =
+ indexedSetOf(
+ Manifest.permission.ACCESS_MEDIA_LOCATION,
+ Manifest.permission.ACTIVITY_RECOGNITION,
+ Manifest.permission.READ_MEDIA_AUDIO,
+ Manifest.permission.READ_MEDIA_IMAGES,
+ Manifest.permission.READ_MEDIA_VIDEO,
+ )
- private val NEARBY_DEVICES_PERMISSIONS = indexedSetOf(
- Manifest.permission.BLUETOOTH_ADVERTISE,
- Manifest.permission.BLUETOOTH_CONNECT,
- Manifest.permission.BLUETOOTH_SCAN,
- Manifest.permission.NEARBY_WIFI_DEVICES
- )
+ private val NEARBY_DEVICES_PERMISSIONS =
+ indexedSetOf(
+ Manifest.permission.BLUETOOTH_ADVERTISE,
+ Manifest.permission.BLUETOOTH_CONNECT,
+ Manifest.permission.BLUETOOTH_SCAN,
+ Manifest.permission.NEARBY_WIFI_DEVICES
+ )
- private val NOTIFICATIONS_PERMISSIONS = indexedSetOf(
- Manifest.permission.POST_NOTIFICATIONS
- )
+ private val NOTIFICATIONS_PERMISSIONS = indexedSetOf(Manifest.permission.POST_NOTIFICATIONS)
- private val STORAGE_AND_MEDIA_PERMISSIONS = indexedSetOf(
- Manifest.permission.READ_EXTERNAL_STORAGE,
- Manifest.permission.WRITE_EXTERNAL_STORAGE,
- Manifest.permission.READ_MEDIA_AUDIO,
- Manifest.permission.READ_MEDIA_VIDEO,
- Manifest.permission.READ_MEDIA_IMAGES,
- Manifest.permission.ACCESS_MEDIA_LOCATION,
- Manifest.permission.READ_MEDIA_VISUAL_USER_SELECTED
- )
+ private val STORAGE_AND_MEDIA_PERMISSIONS =
+ indexedSetOf(
+ Manifest.permission.READ_EXTERNAL_STORAGE,
+ Manifest.permission.WRITE_EXTERNAL_STORAGE,
+ Manifest.permission.READ_MEDIA_AUDIO,
+ Manifest.permission.READ_MEDIA_VIDEO,
+ Manifest.permission.READ_MEDIA_IMAGES,
+ Manifest.permission.ACCESS_MEDIA_LOCATION,
+ Manifest.permission.READ_MEDIA_VISUAL_USER_SELECTED
+ )
- /**
- * Mask for all permission flags that can be set by the user
- */
+ /** Mask for all permission flags that can be set by the user */
private const val USER_SETTABLE_MASK =
PermissionFlags.USER_SET or
PermissionFlags.USER_FIXED or
@@ -1558,16 +1728,14 @@
PermissionFlags.USER_SELECTED
/**
- * Mask for all permission flags that imply we shouldn't automatically modify the
- * permission grant state.
+ * Mask for all permission flags that imply we shouldn't automatically modify the permission
+ * grant state.
*/
private const val SYSTEM_OR_POLICY_FIXED_MASK =
PermissionFlags.SYSTEM_FIXED or PermissionFlags.POLICY_FIXED
}
- /**
- * Listener for permission flags changes.
- */
+ /** Listener for permission flags changes. */
abstract class OnPermissionFlagsChangedListener {
/**
* Called when a permission flags change has been made to the upcoming new state.
diff --git a/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionUpgrade.kt b/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionUpgrade.kt
index b644d8f..edacda0 100644
--- a/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionUpgrade.kt
+++ b/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionUpgrade.kt
@@ -32,7 +32,6 @@
* Upgrade the package permissions, if needed.
*
* @param version package version
- *
* @see [com.android.server.permission.access.util.PackageVersionMigration.getVersion]
*/
fun MutateStateScope.upgradePackageState(
@@ -43,7 +42,8 @@
val packageName = packageState.packageName
if (version <= 3) {
Slog.v(
- LOG_TAG, "Allowlisting and upgrading background location permission for " +
+ LOG_TAG,
+ "Allowlisting and upgrading background location permission for " +
"package: $packageName, version: $version, user:$userId"
)
allowlistRestrictedPermissions(packageState, userId)
@@ -51,7 +51,8 @@
}
if (version <= 10) {
Slog.v(
- LOG_TAG, "Upgrading access media location permission for package: $packageName" +
+ LOG_TAG,
+ "Upgrading access media location permission for package: $packageName" +
", version: $version, user: $userId"
)
upgradeAccessMediaLocationPermission(packageState, userId)
@@ -59,7 +60,8 @@
// TODO Enable isAtLeastT check, when moving subsystem to mainline.
if (version <= 12 /*&& SdkLevel.isAtLeastT()*/) {
Slog.v(
- LOG_TAG, "Upgrading scoped permissions for package: $packageName" +
+ LOG_TAG,
+ "Upgrading scoped permissions for package: $packageName" +
", version: $version, user: $userId"
)
upgradeAuralVisualMediaPermissions(packageState, userId)
@@ -67,7 +69,8 @@
// TODO Enable isAtLeastU check, when moving subsystem to mainline.
if (version <= 14 /*&& SdkLevel.isAtLeastU()*/) {
Slog.v(
- LOG_TAG, "Upgrading visual media permission for package: $packageName" +
+ LOG_TAG,
+ "Upgrading visual media permission for package: $packageName" +
", version: $version, user: $userId"
)
upgradeUserSelectedVisualMediaPermission(packageState, userId)
@@ -84,8 +87,11 @@
if (permissionName in LEGACY_RESTRICTED_PERMISSIONS) {
with(policy) {
updatePermissionFlags(
- packageState.appId, userId, permissionName,
- PermissionFlags.UPGRADE_EXEMPT, PermissionFlags.UPGRADE_EXEMPT
+ packageState.appId,
+ userId,
+ permissionName,
+ PermissionFlags.UPGRADE_EXEMPT,
+ PermissionFlags.UPGRADE_EXEMPT
)
}
}
@@ -96,21 +102,27 @@
packageState: PackageState,
userId: Int
) {
- if (Manifest.permission.ACCESS_BACKGROUND_LOCATION in
- packageState.androidPackage!!.requestedPermissions) {
+ if (
+ Manifest.permission.ACCESS_BACKGROUND_LOCATION in
+ packageState.androidPackage!!.requestedPermissions
+ ) {
val appId = packageState.appId
- val accessFineLocationFlags = with(policy) {
- getPermissionFlags(appId, userId, Manifest.permission.ACCESS_FINE_LOCATION)
- }
- val accessCoarseLocationFlags = with(policy) {
- getPermissionFlags(appId, userId, Manifest.permission.ACCESS_COARSE_LOCATION)
- }
+ val accessFineLocationFlags =
+ with(policy) {
+ getPermissionFlags(appId, userId, Manifest.permission.ACCESS_FINE_LOCATION)
+ }
+ val accessCoarseLocationFlags =
+ with(policy) {
+ getPermissionFlags(appId, userId, Manifest.permission.ACCESS_COARSE_LOCATION)
+ }
val isForegroundLocationGranted =
PermissionFlags.isAppOpGranted(accessFineLocationFlags) ||
PermissionFlags.isAppOpGranted(accessCoarseLocationFlags)
if (isForegroundLocationGranted) {
grantRuntimePermission(
- packageState, userId, Manifest.permission.ACCESS_BACKGROUND_LOCATION
+ packageState,
+ userId,
+ Manifest.permission.ACCESS_BACKGROUND_LOCATION
)
}
}
@@ -120,24 +132,29 @@
packageState: PackageState,
userId: Int
) {
- if (Manifest.permission.ACCESS_MEDIA_LOCATION in
- packageState.androidPackage!!.requestedPermissions) {
- val flags = with(policy) {
- getPermissionFlags(
- packageState.appId, userId, Manifest.permission.READ_EXTERNAL_STORAGE
- )
- }
+ if (
+ Manifest.permission.ACCESS_MEDIA_LOCATION in
+ packageState.androidPackage!!.requestedPermissions
+ ) {
+ val flags =
+ with(policy) {
+ getPermissionFlags(
+ packageState.appId,
+ userId,
+ Manifest.permission.READ_EXTERNAL_STORAGE
+ )
+ }
if (PermissionFlags.isAppOpGranted(flags)) {
grantRuntimePermission(
- packageState, userId, Manifest.permission.ACCESS_MEDIA_LOCATION
+ packageState,
+ userId,
+ Manifest.permission.ACCESS_MEDIA_LOCATION
)
}
}
}
- /**
- * Upgrade permissions based on storage permissions grant
- */
+ /** Upgrade permissions based on storage permissions grant */
private fun MutateStateScope.upgradeAuralVisualMediaPermissions(
packageState: PackageState,
userId: Int
@@ -147,15 +164,15 @@
return
}
val requestedPermissionNames = androidPackage.requestedPermissions
- val isStorageUserGranted = STORAGE_PERMISSIONS.anyIndexed { _, permissionName ->
- if (permissionName !in requestedPermissionNames) {
- return@anyIndexed false
+ val isStorageUserGranted =
+ STORAGE_PERMISSIONS.anyIndexed { _, permissionName ->
+ if (permissionName !in requestedPermissionNames) {
+ return@anyIndexed false
+ }
+ val flags =
+ with(policy) { getPermissionFlags(packageState.appId, userId, permissionName) }
+ PermissionFlags.isAppOpGranted(flags) && flags.hasBits(PermissionFlags.USER_SET)
}
- val flags = with(policy) {
- getPermissionFlags(packageState.appId, userId, permissionName)
- }
- PermissionFlags.isAppOpGranted(flags) && flags.hasBits(PermissionFlags.USER_SET)
- }
if (isStorageUserGranted) {
AURAL_VISUAL_MEDIA_PERMISSIONS.forEachIndexed { _, permissionName ->
if (permissionName in requestedPermissionNames) {
@@ -165,9 +182,7 @@
}
}
- /**
- * Upgrade permission based on the grant in [Manifest.permission_group.READ_MEDIA_VISUAL]
- */
+ /** Upgrade permission based on the grant in [Manifest.permission_group.READ_MEDIA_VISUAL] */
private fun MutateStateScope.upgradeUserSelectedVisualMediaPermission(
packageState: PackageState,
userId: Int
@@ -177,19 +192,21 @@
return
}
val requestedPermissionNames = androidPackage.requestedPermissions
- val isVisualMediaUserGranted = VISUAL_MEDIA_PERMISSIONS.anyIndexed { _, permissionName ->
- if (permissionName !in requestedPermissionNames) {
- return@anyIndexed false
+ val isVisualMediaUserGranted =
+ VISUAL_MEDIA_PERMISSIONS.anyIndexed { _, permissionName ->
+ if (permissionName !in requestedPermissionNames) {
+ return@anyIndexed false
+ }
+ val flags =
+ with(policy) { getPermissionFlags(packageState.appId, userId, permissionName) }
+ PermissionFlags.isAppOpGranted(flags) && flags.hasBits(PermissionFlags.USER_SET)
}
- val flags = with(policy) {
- getPermissionFlags(packageState.appId, userId, permissionName)
- }
- PermissionFlags.isAppOpGranted(flags) && flags.hasBits(PermissionFlags.USER_SET)
- }
if (isVisualMediaUserGranted) {
if (Manifest.permission.READ_MEDIA_VISUAL_USER_SELECTED in requestedPermissionNames) {
grantRuntimePermission(
- packageState, userId, Manifest.permission.READ_MEDIA_VISUAL_USER_SELECTED
+ packageState,
+ userId,
+ Manifest.permission.READ_MEDIA_VISUAL_USER_SELECTED
)
}
}
@@ -201,7 +218,8 @@
permissionName: String
) {
Slog.v(
- LOG_TAG, "Granting runtime permission for package: ${packageState.packageName}, " +
+ LOG_TAG,
+ "Granting runtime permission for package: ${packageState.packageName}, " +
"permission: $permissionName, userId: $userId"
)
val permission = newState.systemState.permissions[permissionName]!!
@@ -220,13 +238,13 @@
}
flags = flags or PermissionFlags.RUNTIME_GRANTED
- flags = flags andInv (
- PermissionFlags.APP_OP_REVOKED or
- PermissionFlags.IMPLICIT or
- PermissionFlags.LEGACY_GRANTED or
- PermissionFlags.HIBERNATION or
- PermissionFlags.ONE_TIME
- )
+ flags =
+ flags andInv
+ (PermissionFlags.APP_OP_REVOKED or
+ PermissionFlags.IMPLICIT or
+ PermissionFlags.LEGACY_GRANTED or
+ PermissionFlags.HIBERNATION or
+ PermissionFlags.ONE_TIME)
with(policy) { setPermissionFlags(appId, userId, permissionName, flags) }
}
@@ -234,39 +252,45 @@
private val LOG_TAG = AppIdPermissionUpgrade::class.java.simpleName
private const val MASK_ANY_FIXED =
- PermissionFlags.USER_SET or PermissionFlags.USER_FIXED or
- PermissionFlags.POLICY_FIXED or PermissionFlags.SYSTEM_FIXED
+ PermissionFlags.USER_SET or
+ PermissionFlags.USER_FIXED or
+ PermissionFlags.POLICY_FIXED or
+ PermissionFlags.SYSTEM_FIXED
- private val LEGACY_RESTRICTED_PERMISSIONS = indexedSetOf(
- Manifest.permission.ACCESS_BACKGROUND_LOCATION,
- Manifest.permission.READ_EXTERNAL_STORAGE,
- Manifest.permission.WRITE_EXTERNAL_STORAGE,
- Manifest.permission.SEND_SMS,
- Manifest.permission.RECEIVE_SMS,
- Manifest.permission.RECEIVE_WAP_PUSH,
- Manifest.permission.RECEIVE_MMS,
- Manifest.permission.READ_CELL_BROADCASTS,
- Manifest.permission.READ_CALL_LOG,
- Manifest.permission.WRITE_CALL_LOG,
- Manifest.permission.PROCESS_OUTGOING_CALLS
- )
+ private val LEGACY_RESTRICTED_PERMISSIONS =
+ indexedSetOf(
+ Manifest.permission.ACCESS_BACKGROUND_LOCATION,
+ Manifest.permission.READ_EXTERNAL_STORAGE,
+ Manifest.permission.WRITE_EXTERNAL_STORAGE,
+ Manifest.permission.SEND_SMS,
+ Manifest.permission.RECEIVE_SMS,
+ Manifest.permission.RECEIVE_WAP_PUSH,
+ Manifest.permission.RECEIVE_MMS,
+ Manifest.permission.READ_CELL_BROADCASTS,
+ Manifest.permission.READ_CALL_LOG,
+ Manifest.permission.WRITE_CALL_LOG,
+ Manifest.permission.PROCESS_OUTGOING_CALLS
+ )
- private val STORAGE_PERMISSIONS = indexedSetOf(
- Manifest.permission.READ_EXTERNAL_STORAGE,
- Manifest.permission.WRITE_EXTERNAL_STORAGE
- )
- private val AURAL_VISUAL_MEDIA_PERMISSIONS = indexedSetOf(
- Manifest.permission.READ_MEDIA_AUDIO,
- Manifest.permission.READ_MEDIA_IMAGES,
- Manifest.permission.READ_MEDIA_VIDEO,
- Manifest.permission.ACCESS_MEDIA_LOCATION,
- Manifest.permission.READ_MEDIA_VISUAL_USER_SELECTED
- )
+ private val STORAGE_PERMISSIONS =
+ indexedSetOf(
+ Manifest.permission.READ_EXTERNAL_STORAGE,
+ Manifest.permission.WRITE_EXTERNAL_STORAGE
+ )
+ private val AURAL_VISUAL_MEDIA_PERMISSIONS =
+ indexedSetOf(
+ Manifest.permission.READ_MEDIA_AUDIO,
+ Manifest.permission.READ_MEDIA_IMAGES,
+ Manifest.permission.READ_MEDIA_VIDEO,
+ Manifest.permission.ACCESS_MEDIA_LOCATION,
+ Manifest.permission.READ_MEDIA_VISUAL_USER_SELECTED
+ )
// Visual media permissions in T
- private val VISUAL_MEDIA_PERMISSIONS = indexedSetOf(
- Manifest.permission.READ_MEDIA_IMAGES,
- Manifest.permission.READ_MEDIA_VIDEO,
- Manifest.permission.ACCESS_MEDIA_LOCATION
- )
+ private val VISUAL_MEDIA_PERMISSIONS =
+ indexedSetOf(
+ Manifest.permission.READ_MEDIA_IMAGES,
+ Manifest.permission.READ_MEDIA_VIDEO,
+ Manifest.permission.ACCESS_MEDIA_LOCATION
+ )
}
}
diff --git a/services/permission/java/com/android/server/permission/access/permission/DevicePermissionPersistence.kt b/services/permission/java/com/android/server/permission/access/permission/DevicePermissionPersistence.kt
index 37a4a90..1bee356 100644
--- a/services/permission/java/com/android/server/permission/access/permission/DevicePermissionPersistence.kt
+++ b/services/permission/java/com/android/server/permission/access/permission/DevicePermissionPersistence.kt
@@ -135,9 +135,7 @@
) {
tag(TAG_DEVICE) {
attributeInterned(ATTR_ID, deviceId)
- permissionFlags.forEachIndexed { _, name, flags ->
- serializePermission(name, flags)
- }
+ permissionFlags.forEachIndexed { _, name, flags -> serializePermission(name, flags) }
}
}
@@ -145,11 +143,12 @@
tag(TAG_PERMISSION) {
attributeInterned(ATTR_NAME, name)
// Never serialize one-time permissions as granted.
- val serializedFlags = if (flags.hasBits(PermissionFlags.ONE_TIME)) {
- flags andInv PermissionFlags.RUNTIME_GRANTED
- } else {
- flags
- }
+ val serializedFlags =
+ if (flags.hasBits(PermissionFlags.ONE_TIME)) {
+ flags andInv PermissionFlags.RUNTIME_GRANTED
+ } else {
+ flags
+ }
attributeInt(ATTR_FLAGS, serializedFlags)
}
}
@@ -166,4 +165,4 @@
private const val ATTR_NAME = "name"
private const val ATTR_FLAGS = "flags"
}
-}
\ No newline at end of file
+}
diff --git a/services/permission/java/com/android/server/permission/access/permission/DevicePermissionPolicy.kt b/services/permission/java/com/android/server/permission/access/permission/DevicePermissionPolicy.kt
index 240585c..15a5859 100644
--- a/services/permission/java/com/android/server/permission/access/permission/DevicePermissionPolicy.kt
+++ b/services/permission/java/com/android/server/permission/access/permission/DevicePermissionPolicy.kt
@@ -53,8 +53,8 @@
override fun MutateStateScope.onAppIdRemoved(appId: Int) {
newState.userStates.forEachIndexed { userStateIndex, _, userState ->
if (appId in userState.appIdDevicePermissionFlags) {
- newState.mutateUserStateAt(userStateIndex)
- .mutateAppIdDevicePermissionFlags() -= appId
+ newState.mutateUserStateAt(userStateIndex).mutateAppIdDevicePermissionFlags() -=
+ appId
}
}
}
@@ -85,10 +85,10 @@
appId: Int,
userId: Int
) {
- resetPermissionStates(packageName, userId)
+ resetRuntimePermissions(packageName, userId)
}
- private fun MutateStateScope.resetPermissionStates(packageName: String, userId: Int) {
+ fun MutateStateScope.resetRuntimePermissions(packageName: String, userId: Int) {
// It's okay to skip resetting permissions for packages that are removed,
// because their states will be trimmed in onPackageRemoved()/onAppIdRemoved()
val packageState = newState.externalState.packageStates[packageName] ?: return
@@ -96,10 +96,11 @@
val appId = packageState.appId
val appIdPermissionFlags = newState.userStates[userId]!!.appIdDevicePermissionFlags
androidPackage.requestedPermissions.forEach { permissionName ->
- val isRequestedByOtherPackages = anyPackageInAppId(appId) {
- it.packageName != packageName &&
- permissionName in it.androidPackage!!.requestedPermissions
- }
+ val isRequestedByOtherPackages =
+ anyPackageInAppId(appId) {
+ it.packageName != packageName &&
+ permissionName in it.androidPackage!!.requestedPermissions
+ }
if (isRequestedByOtherPackages) {
return@forEach
}
@@ -116,7 +117,9 @@
}
newState.userStates.forEachIndexed { _, userId, userState ->
userState.appIdDevicePermissionFlags[appId]?.forEachReversedIndexed {
- _, deviceId, permissionFlags ->
+ _,
+ deviceId,
+ permissionFlags ->
permissionFlags.forEachReversedIndexed { _, permissionName, _ ->
if (permissionName !in requestedPermissions) {
setPermissionFlags(appId, deviceId, userId, permissionName, 0)
@@ -166,11 +169,17 @@
userId: Int,
permissionName: String
): Int {
- val flags = state.userStates[userId]?.appIdDevicePermissionFlags?.get(appId)?.get(deviceId)
- ?.getWithDefault(permissionName, 0) ?: 0
+ val flags =
+ state.userStates[userId]
+ ?.appIdDevicePermissionFlags
+ ?.get(appId)
+ ?.get(deviceId)
+ ?.getWithDefault(permissionName, 0)
+ ?: 0
if (PermissionManager.DEBUG_DEVICE_PERMISSIONS) {
Slog.i(
- LOG_TAG, "getPermissionFlags: appId=$appId, userId=$userId," +
+ LOG_TAG,
+ "getPermissionFlags: appId=$appId, userId=$userId," +
" deviceId=$deviceId, permissionName=$permissionName," +
" flags=${PermissionFlags.toString(flags)}"
)
@@ -186,7 +195,12 @@
flags: Int
): Boolean =
updatePermissionFlags(
- appId, deviceId, userId, permissionName, PermissionFlags.MASK_ALL, flags
+ appId,
+ deviceId,
+ userId,
+ permissionName,
+ PermissionFlags.MASK_ALL,
+ flags
)
private fun MutateStateScope.updatePermissionFlags(
@@ -201,20 +215,23 @@
Slog.w(LOG_TAG, "$permissionName is not a device aware permission.")
return false
}
- val oldFlags = newState.userStates[userId]!!.appIdDevicePermissionFlags[appId]
- ?.get(deviceId).getWithDefault(permissionName, 0)
+ val oldFlags =
+ newState.userStates[userId]!!
+ .appIdDevicePermissionFlags[appId]
+ ?.get(deviceId)
+ .getWithDefault(permissionName, 0)
val newFlags = (oldFlags andInv flagMask) or (flagValues and flagMask)
if (oldFlags == newFlags) {
return false
}
val appIdDevicePermissionFlags =
newState.mutateUserState(userId)!!.mutateAppIdDevicePermissionFlags()
- val devicePermissionFlags = appIdDevicePermissionFlags.mutateOrPut(appId) {
- MutableIndexedReferenceMap()
- }
+ val devicePermissionFlags =
+ appIdDevicePermissionFlags.mutateOrPut(appId) { MutableIndexedReferenceMap() }
if (PermissionManager.DEBUG_DEVICE_PERMISSIONS) {
Slog.i(
- LOG_TAG, "setPermissionFlags(): appId=$appId, userId=$userId," +
+ LOG_TAG,
+ "setPermissionFlags(): appId=$appId, userId=$userId," +
" deviceId=$deviceId, permissionName=$permissionName," +
" newFlags=${PermissionFlags.toString(newFlags)}"
)
@@ -229,40 +246,39 @@
}
listeners.forEachIndexed { _, it ->
it.onDevicePermissionFlagsChanged(
- appId, userId, deviceId, permissionName, oldFlags, newFlags
+ appId,
+ userId,
+ deviceId,
+ permissionName,
+ oldFlags,
+ newFlags
)
}
return true
}
fun addOnPermissionFlagsChangedListener(listener: OnDevicePermissionFlagsChangedListener) {
- synchronized(listenersLock) {
- listeners = listeners + listener
- }
+ synchronized(listenersLock) { listeners = listeners + listener }
}
fun removeOnPermissionFlagsChangedListener(listener: OnDevicePermissionFlagsChangedListener) {
- synchronized(listenersLock) {
- listeners = listeners - listener
- }
+ synchronized(listenersLock) { listeners = listeners - listener }
}
private fun isDeviceAwarePermission(permissionName: String): Boolean =
- DEVICE_AWARE_PERMISSIONS.contains(permissionName)
+ DEVICE_AWARE_PERMISSIONS.contains(permissionName)
companion object {
private val LOG_TAG = DevicePermissionPolicy::class.java.simpleName
- /**
- * These permissions are supported for virtual devices.
- */
+ /** These permissions are supported for virtual devices. */
// TODO: b/298661870 - Use new API to get the list of device aware permissions.
val DEVICE_AWARE_PERMISSIONS = emptySet<String>()
}
/**
- * TODO: b/289355341 - implement listener for permission changes
- * Listener for permission flags changes.
+ * TODO: b/289355341 - implement listener for permission changes Listener for permission flags
+ * changes.
*/
abstract class OnDevicePermissionFlagsChangedListener {
/**
@@ -288,4 +304,4 @@
*/
abstract fun onStateMutated()
}
-}
\ No newline at end of file
+}
diff --git a/services/permission/java/com/android/server/permission/access/permission/Permission.kt b/services/permission/java/com/android/server/permission/access/permission/Permission.kt
index c7fe1a9..aa56928 100644
--- a/services/permission/java/com/android/server/permission/access/permission/Permission.kt
+++ b/services/permission/java/com/android/server/permission/access/permission/Permission.kt
@@ -26,8 +26,7 @@
val isReconciled: Boolean,
val type: Int,
val appId: Int,
- @Suppress("ArrayInDataClass")
- val gids: IntArray = EmptyArray.INT,
+ @Suppress("ArrayInDataClass") val gids: IntArray = EmptyArray.INT,
val areGidsPerUser: Boolean = false
) {
inline val name: String
@@ -43,8 +42,7 @@
get() = type == TYPE_DYNAMIC
inline val protectionLevel: Int
- @Suppress("DEPRECATION")
- get() = permissionInfo.protectionLevel
+ @Suppress("DEPRECATION") get() = permissionInfo.protectionLevel
inline val protection: Int
get() = permissionInfo.protection
diff --git a/services/permission/java/com/android/server/permission/access/permission/PermissionFlags.kt b/services/permission/java/com/android/server/permission/access/permission/PermissionFlags.kt
index 550d148..b9d89c2 100644
--- a/services/permission/java/com/android/server/permission/access/permission/PermissionFlags.kt
+++ b/services/permission/java/com/android/server/permission/access/permission/PermissionFlags.kt
@@ -32,15 +32,12 @@
*
* The old binary permission state is now tracked by multiple `*_GRANTED` and `*_REVOKED` flags, so
* that:
- *
* - With [INSTALL_GRANTED] and [INSTALL_REVOKED], we can now get rid of the old per-package
* `areInstallPermissionsFixed` attribute and correctly track it per-permission, finally fixing
* edge cases during module rollbacks.
- *
* - With [LEGACY_GRANTED] and [IMPLICIT_GRANTED], we can now ensure that legacy permissions and
* implicit permissions split from non-runtime permissions are never revoked, without checking
* split permissions and package state everywhere slowly and in slightly different ways.
- *
* - With [RESTRICTION_REVOKED], we can now get rid of the error-prone logic about revoking and
* potentially re-granting permissions upon restriction state changes.
*
@@ -55,9 +52,7 @@
* don't have any effect on the binary permission state.
*/
object PermissionFlags {
- /**
- * Permission flag for a normal permission that is granted at package installation.
- */
+ /** Permission flag for a normal permission that is granted at package installation. */
const val INSTALL_GRANTED = 1 shl 0
/**
@@ -97,8 +92,8 @@
/**
* Permission flag for a runtime permission whose state is set by the user.
*
- * For example, this flag may be set when the permission is allowed by the user in the
- * request permission dialog, or managed in the permission settings.
+ * For example, this flag may be set when the permission is allowed by the user in the request
+ * permission dialog, or managed in the permission settings.
*
* @see PackageManager.FLAG_PERMISSION_USER_SET
*/
@@ -290,8 +285,8 @@
/**
* Permission flag for a runtime permission that is selected by the user.
*
- * For example, this flag may be set when one of the coarse/fine location accuracies is
- * selected by the user.
+ * For example, this flag may be set when one of the coarse/fine location accuracies is selected
+ * by the user.
*
* This flag is informational and managed by PermissionController.
*
@@ -299,28 +294,37 @@
*/
const val USER_SELECTED = 1 shl 23
- /**
- * Mask for all permission flags.
- */
+ /** Mask for all permission flags. */
const val MASK_ALL = 0.inv()
- /**
- * Mask for all permission flags that may be applied to a runtime permission.
- */
- const val MASK_RUNTIME = ROLE or RUNTIME_GRANTED or USER_SET or USER_FIXED or POLICY_FIXED or
- SYSTEM_FIXED or PREGRANT or LEGACY_GRANTED or IMPLICIT_GRANTED or IMPLICIT or
- USER_SENSITIVE_WHEN_GRANTED or USER_SENSITIVE_WHEN_REVOKED or INSTALLER_EXEMPT or
- SYSTEM_EXEMPT or UPGRADE_EXEMPT or RESTRICTION_REVOKED or SOFT_RESTRICTED or
- APP_OP_REVOKED or ONE_TIME or HIBERNATION or USER_SELECTED
+ /** Mask for all permission flags that may be applied to a runtime permission. */
+ const val MASK_RUNTIME =
+ ROLE or
+ RUNTIME_GRANTED or
+ USER_SET or
+ USER_FIXED or
+ POLICY_FIXED or
+ SYSTEM_FIXED or
+ PREGRANT or
+ LEGACY_GRANTED or
+ IMPLICIT_GRANTED or
+ IMPLICIT or
+ USER_SENSITIVE_WHEN_GRANTED or
+ USER_SENSITIVE_WHEN_REVOKED or
+ INSTALLER_EXEMPT or
+ SYSTEM_EXEMPT or
+ UPGRADE_EXEMPT or
+ RESTRICTION_REVOKED or
+ SOFT_RESTRICTED or
+ APP_OP_REVOKED or
+ ONE_TIME or
+ HIBERNATION or
+ USER_SELECTED
- /**
- * Mask for all permission flags about permission exemption.
- */
+ /** Mask for all permission flags about permission exemption. */
const val MASK_EXEMPT = INSTALLER_EXEMPT or SYSTEM_EXEMPT or UPGRADE_EXEMPT
- /**
- * Mask for all permission flags about permission restriction.
- */
+ /** Mask for all permission flags about permission restriction. */
const val MASK_RESTRICTED = RESTRICTION_REVOKED or SOFT_RESTRICTED
fun isPermissionGranted(flags: Int): Boolean {
@@ -363,11 +367,13 @@
apiFlags = apiFlags or PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT
}
if (flags.hasBits(IMPLICIT)) {
- apiFlags = apiFlags or if (flags.hasBits(LEGACY_GRANTED)) {
- PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED
- } else {
- PackageManager.FLAG_PERMISSION_REVOKE_WHEN_REQUESTED
- }
+ apiFlags =
+ apiFlags or
+ if (flags.hasBits(LEGACY_GRANTED)) {
+ PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED
+ } else {
+ PackageManager.FLAG_PERMISSION_REVOKE_WHEN_REQUESTED
+ }
}
if (flags.hasBits(USER_SENSITIVE_WHEN_GRANTED)) {
apiFlags = apiFlags or PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED
@@ -440,8 +446,10 @@
}
flags = flags or (oldFlags and LEGACY_GRANTED)
flags = flags or (oldFlags and IMPLICIT_GRANTED)
- if (apiFlags.hasBits(PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) ||
- apiFlags.hasBits(PackageManager.FLAG_PERMISSION_REVOKE_WHEN_REQUESTED)) {
+ if (
+ apiFlags.hasBits(PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) ||
+ apiFlags.hasBits(PackageManager.FLAG_PERMISSION_REVOKE_WHEN_REQUESTED)
+ ) {
flags = flags or IMPLICIT
}
if (apiFlags.hasBits(PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED)) {
diff --git a/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt b/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt
index bb24d51..ab3d78c 100644
--- a/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt
+++ b/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt
@@ -41,10 +41,10 @@
import android.os.ServiceManager
import android.os.UserHandle
import android.os.UserManager
-import android.permission.flags.Flags
import android.permission.IOnPermissionsChangeListener
import android.permission.PermissionControllerManager
import android.permission.PermissionManager
+import android.permission.flags.Flags
import android.provider.Settings
import android.util.ArrayMap
import android.util.ArraySet
@@ -88,28 +88,25 @@
import com.android.server.pm.UserManagerService
import com.android.server.pm.parsing.pkg.AndroidPackageUtils
import com.android.server.pm.permission.LegacyPermission
-import com.android.server.pm.permission.Permission as LegacyPermission2
import com.android.server.pm.permission.LegacyPermissionSettings
import com.android.server.pm.permission.LegacyPermissionState
+import com.android.server.pm.permission.Permission as LegacyPermission2
import com.android.server.pm.permission.PermissionManagerServiceInterface
import com.android.server.pm.permission.PermissionManagerServiceInternal
import com.android.server.pm.pkg.AndroidPackage
import com.android.server.pm.pkg.PackageState
import com.android.server.policy.SoftRestrictedPermissionPolicy
-import libcore.util.EmptyArray
import java.io.FileDescriptor
import java.io.PrintWriter
import java.util.concurrent.CompletableFuture
import java.util.concurrent.ExecutionException
import java.util.concurrent.TimeUnit
import java.util.concurrent.TimeoutException
+import libcore.util.EmptyArray
-/**
- * Modern implementation of [PermissionManagerServiceInterface].
- */
-class PermissionService(
- private val service: AccessCheckingService
-) : PermissionManagerServiceInterface {
+/** Modern implementation of [PermissionManagerServiceInterface]. */
+class PermissionService(private val service: AccessCheckingService) :
+ PermissionManagerServiceInterface {
private val policy =
service.getSchemePolicy(UidUri.SCHEME, PermissionUri.SCHEME) as AppIdPermissionPolicy
@@ -131,8 +128,7 @@
private lateinit var onPermissionFlagsChangedListener: OnPermissionFlagsChangedListener
private val storageVolumeLock = Any()
- @GuardedBy("storageVolumeLock")
- private val mountedStorageVolumes = ArraySet<String?>()
+ @GuardedBy("storageVolumeLock") private val mountedStorageVolumes = ArraySet<String?>()
@GuardedBy("storageVolumeLock")
private val storageVolumePackageNames = ArrayMap<String?, MutableList<String>>()
@@ -144,8 +140,8 @@
* A permission backup might contain apps that are not installed. In this case we delay the
* restoration until the app is installed.
*
- * This array (`userId -> noDelayedBackupLeft`) is `true` for all the users where
- * there is **no more** delayed backup left.
+ * This array (`userId -> noDelayedBackupLeft`) is `true` for all the users where there is **no
+ * more** delayed backup left.
*/
private val isDelayedPermissionBackupFinished = SparseBooleanArray()
@@ -154,9 +150,10 @@
packageManagerInternal = LocalServices.getService(PackageManagerInternal::class.java)
packageManagerLocal =
LocalManagerRegistry.getManagerOrThrow(PackageManagerLocal::class.java)
- platformCompat = IPlatformCompat.Stub.asInterface(
- ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE)
- )
+ platformCompat =
+ IPlatformCompat.Stub.asInterface(
+ ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE)
+ )
systemConfig = SystemConfig.getInstance()
userManagerInternal = LocalServices.getService(UserManagerInternal::class.java)
userManagerService = UserManagerService.getInstance()
@@ -166,8 +163,8 @@
PackageManager.invalidatePackageInfoCache()
PermissionManager.disablePackageNamePermissionCache()
- handlerThread = ServiceThread(LOG_TAG, Process.THREAD_PRIORITY_BACKGROUND, true)
- .apply { start() }
+ handlerThread =
+ ServiceThread(LOG_TAG, Process.THREAD_PRIORITY_BACKGROUND, true).apply { start() }
handler = Handler(handlerThread.looper)
onPermissionsChangeListeners = OnPermissionsChangeListeners(FgThread.get().looper)
onPermissionFlagsChangedListener = OnPermissionFlagsChangedListener()
@@ -181,9 +178,7 @@
return emptyList()
}
- val permissionGroups = service.getState {
- with(policy) { getPermissionGroups() }
- }
+ val permissionGroups = service.getState { with(policy) { getPermissionGroups() } }
return permissionGroups.mapNotNullIndexedTo(ArrayList()) { _, _, permissionGroup ->
if (snapshot.isPackageVisibleToUid(permissionGroup.packageName, callingUid)) {
@@ -206,9 +201,9 @@
return null
}
- permissionGroup = service.getState {
- with(policy) { getPermissionGroups()[permissionGroupName] }
- } ?: return null
+ permissionGroup =
+ service.getState { with(policy) { getPermissionGroups()[permissionGroupName] } }
+ ?: return null
if (!snapshot.isPackageVisibleToUid(permissionGroup.packageName, callingUid)) {
return null
@@ -242,29 +237,28 @@
return null
}
- permission = service.getState {
- with(policy) { getPermissions()[permissionName] }
- } ?: return null
+ permission =
+ service.getState { with(policy) { getPermissions()[permissionName] } }
+ ?: return null
if (!snapshot.isPackageVisibleToUid(permission.packageName, callingUid)) {
return null
}
val opPackage = snapshot.getPackageState(opPackageName)?.androidPackage
- targetSdkVersion = when {
- // System sees all flags.
- isRootOrSystemOrShellUid(callingUid) -> Build.VERSION_CODES.CUR_DEVELOPMENT
- opPackage != null -> opPackage.targetSdkVersion
- else -> Build.VERSION_CODES.CUR_DEVELOPMENT
- }
+ targetSdkVersion =
+ when {
+ // System sees all flags.
+ isRootOrSystemOrShellUid(callingUid) -> Build.VERSION_CODES.CUR_DEVELOPMENT
+ opPackage != null -> opPackage.targetSdkVersion
+ else -> Build.VERSION_CODES.CUR_DEVELOPMENT
+ }
}
return permission.generatePermissionInfo(flags, targetSdkVersion)
}
- /**
- * Generate a new [PermissionInfo] from [Permission] and adjust it accordingly.
- */
+ /** Generate a new [PermissionInfo] from [Permission] and adjust it accordingly. */
private fun Permission.generatePermissionInfo(
flags: Int,
targetSdkVersion: Int = Build.VERSION_CODES.CUR_DEVELOPMENT
@@ -296,22 +290,27 @@
return null
}
- val permissions = service.getState {
- if (permissionGroupName != null) {
- val permissionGroup =
- with(policy) { getPermissionGroups()[permissionGroupName] } ?: return null
+ val permissions =
+ service.getState {
+ if (permissionGroupName != null) {
+ val permissionGroup =
+ with(policy) { getPermissionGroups()[permissionGroupName] }
+ ?: return null
- if (!snapshot.isPackageVisibleToUid(permissionGroup.packageName, callingUid)) {
- return null
+ if (
+ !snapshot.isPackageVisibleToUid(permissionGroup.packageName, callingUid)
+ ) {
+ return null
+ }
}
+
+ with(policy) { getPermissions() }
}
- with(policy) { getPermissions() }
- }
-
return permissions.mapNotNullIndexedTo(ArrayList()) { _, _, permission ->
- if (permission.groupName == permissionGroupName &&
- snapshot.isPackageVisibleToUid(permission.packageName, callingUid)
+ if (
+ permission.groupName == permissionGroupName &&
+ snapshot.isPackageVisibleToUid(permission.packageName, callingUid)
) {
permission.generatePermissionInfo(flags)
} else {
@@ -334,9 +333,7 @@
private inline fun getPermissionsWithProtectionOrProtectionFlags(
predicate: (Permission) -> Boolean
): List<PermissionInfo> {
- val permissions = service.getState {
- with(policy) { getPermissions() }
- }
+ val permissions = service.getState { with(policy) { getPermissions() } }
return permissions.mapNotNullIndexedTo(ArrayList()) { _, _, permission ->
if (predicate(permission)) {
@@ -348,18 +345,16 @@
}
override fun getPermissionGids(permissionName: String, userId: Int): IntArray {
- val permission = service.getState {
- with(policy) { getPermissions()[permissionName] }
- } ?: return EmptyArray.INT
+ val permission =
+ service.getState { with(policy) { getPermissions()[permissionName] } }
+ ?: return EmptyArray.INT
return permission.getGidsForUser(userId)
}
override fun getInstalledPermissions(packageName: String): Set<String> {
requireNotNull(packageName) { "packageName cannot be null" }
- val permissions = service.getState {
- with(policy) { getPermissions() }
- }
+ val permissions = service.getState { with(policy) { getPermissions() } }
return permissions.mapNotNullIndexedTo(ArraySet()) { _, _, permission ->
if (permission.packageName == packageName) {
@@ -398,9 +393,8 @@
permissionInfo.protectionLevel =
PermissionInfo.fixProtectionLevel(permissionInfo.protectionLevel)
- val newPermission = Permission(
- permissionInfo, true, Permission.TYPE_DYNAMIC, permissionTree.appId
- )
+ val newPermission =
+ Permission(permissionInfo, true, Permission.TYPE_DYNAMIC, permissionTree.appId)
with(policy) { addPermission(newPermission, !async) }
}
@@ -431,7 +425,7 @@
val callingUid = Binder.getCallingUid()
val permissionTree = with(policy) { findPermissionTree(permissionName) }
if (permissionTree != null && permissionTree.appId == UserHandle.getAppId(callingUid)) {
- return permissionTree
+ return permissionTree
}
throw SecurityException(
@@ -447,8 +441,9 @@
// if that plus the size of 'info' would exceed our stated maximum.
if (permissionTree.appId != Process.SYSTEM_UID) {
val permissionTreeFootprint = calculatePermissionTreeFootprint(permissionTree)
- if (permissionTreeFootprint + permissionInfo.calculateFootprint() >
- MAX_PERMISSION_TREE_FOOTPRINT
+ if (
+ permissionTreeFootprint + permissionInfo.calculateFootprint() >
+ MAX_PERMISSION_TREE_FOOTPRINT
) {
throw SecurityException("Permission tree size cap exceeded")
}
@@ -483,14 +478,16 @@
packageManagerInternal.getPackageStateInternal(androidPackage.packageName)
if (packageState == null) {
Slog.e(
- LOG_TAG, "checkUidPermission: PackageState not found for AndroidPackage" +
+ LOG_TAG,
+ "checkUidPermission: PackageState not found for AndroidPackage" +
" $androidPackage"
)
return PackageManager.PERMISSION_DENIED
}
- val isPermissionGranted = service.getState {
- isPermissionGranted(packageState, userId, permissionName, deviceId)
- }
+ val isPermissionGranted =
+ service.getState {
+ isPermissionGranted(packageState, userId, permissionName, deviceId)
+ }
return if (isPermissionGranted) {
PackageManager.PERMISSION_GRANTED
} else {
@@ -505,9 +502,7 @@
}
}
- /**
- * Internal implementation that should only be called by [checkUidPermission].
- */
+ /** Internal implementation that should only be called by [checkUidPermission]. */
private fun isSystemUidPermissionGranted(uid: Int, permissionName: String): Boolean {
val uidPermissions = systemConfig.systemPermissions[uid] ?: return false
if (permissionName in uidPermissions) {
@@ -532,12 +527,14 @@
return PackageManager.PERMISSION_DENIED
}
- val packageState = packageManagerLocal.withFilteredSnapshot(Binder.getCallingUid(), userId)
- .use { it.getPackageState(packageName) } ?: return PackageManager.PERMISSION_DENIED
+ val packageState =
+ packageManagerLocal.withFilteredSnapshot(Binder.getCallingUid(), userId).use {
+ it.getPackageState(packageName)
+ }
+ ?: return PackageManager.PERMISSION_DENIED
- val isPermissionGranted = service.getState {
- isPermissionGranted(packageState, userId, permissionName, deviceId)
- }
+ val isPermissionGranted =
+ service.getState { isPermissionGranted(packageState, userId, permissionName, deviceId) }
return if (isPermissionGranted) {
PackageManager.PERMISSION_GRANTED
} else {
@@ -566,8 +563,15 @@
}
val fullerPermissionName = FULLER_PERMISSIONS[permissionName]
- if (fullerPermissionName != null &&
- isSinglePermissionGranted(appId, userId, isInstantApp, fullerPermissionName, deviceId)
+ if (
+ fullerPermissionName != null &&
+ isSinglePermissionGranted(
+ appId,
+ userId,
+ isInstantApp,
+ fullerPermissionName,
+ deviceId
+ )
) {
return true
}
@@ -575,9 +579,7 @@
return false
}
- /**
- * Internal implementation that should only be called by [isPermissionGranted].
- */
+ /** Internal implementation that should only be called by [isPermissionGranted]. */
private fun GetStateScope.isSinglePermissionGranted(
appId: Int,
userId: Int,
@@ -604,20 +606,27 @@
requireNotNull(packageName) { "packageName cannot be null" }
Preconditions.checkArgumentNonnegative(userId, "userId")
- val packageState = packageManagerLocal.withUnfilteredSnapshot()
- .use { it.getPackageState(packageName) }
+ val packageState =
+ packageManagerLocal.withUnfilteredSnapshot().use { it.getPackageState(packageName) }
if (packageState == null) {
Slog.w(LOG_TAG, "getGrantedPermissions: Unknown package $packageName")
return emptySet()
}
service.getState {
- val permissionFlags = with(policy) { getUidPermissionFlags(packageState.appId, userId) }
- ?: return emptySet()
+ val permissionFlags =
+ with(policy) { getUidPermissionFlags(packageState.appId, userId) }
+ ?: return emptySet()
return permissionFlags.mapNotNullIndexedTo(ArraySet()) { _, permissionName, _ ->
- if (isPermissionGranted(
- packageState, userId, permissionName, Context.DEVICE_ID_DEFAULT)) {
+ if (
+ isPermissionGranted(
+ packageState,
+ userId,
+ permissionName,
+ Context.DEVICE_ID_DEFAULT
+ )
+ ) {
permissionName
} else {
null
@@ -635,8 +644,8 @@
// permission state is not found, now we always return at least global GIDs. This is
// more consistent with the pre-S-refactor behavior. This is also because we are now
// actively trimming the per-UID objects when empty.
- val permissionFlags = with(policy) { getUidPermissionFlags(appId, userId) }
- ?: return globalGids.copyOf()
+ val permissionFlags =
+ with(policy) { getUidPermissionFlags(appId, userId) } ?: return globalGids.copyOf()
val gids = GrowingIntArray.wrap(globalGids)
permissionFlags.forEachIndexed { _, permissionName, flags ->
@@ -644,8 +653,8 @@
return@forEachIndexed
}
- val permission = with(policy) { getPermissions()[permissionName] }
- ?: return@forEachIndexed
+ val permission =
+ with(policy) { getPermissions()[permissionName] } ?: return@forEachIndexed
val permissionGids = permission.getGidsForUser(userId)
if (permissionGids.isEmpty()) {
return@forEachIndexed
@@ -662,9 +671,7 @@
deviceId: Int,
userId: Int
) {
- setRuntimePermissionGranted(
- packageName, userId, permissionName, deviceId, isGranted = true
- )
+ setRuntimePermissionGranted(packageName, userId, permissionName, deviceId, isGranted = true)
}
override fun revokeRuntimePermission(
@@ -675,7 +682,12 @@
reason: String?
) {
setRuntimePermissionGranted(
- packageName, userId, permissionName, deviceId, isGranted = false, revokeReason = reason
+ packageName,
+ userId,
+ permissionName,
+ deviceId,
+ isGranted = false,
+ revokeReason = reason
)
}
@@ -684,8 +696,12 @@
userId: Int
) {
setRuntimePermissionGranted(
- packageName, userId, Manifest.permission.POST_NOTIFICATIONS, Context.DEVICE_ID_DEFAULT,
- isGranted = false, skipKillUid = true
+ packageName,
+ userId,
+ Manifest.permission.POST_NOTIFICATIONS,
+ Context.DEVICE_ID_DEFAULT,
+ isGranted = false,
+ skipKillUid = true
)
}
@@ -704,19 +720,24 @@
) {
val methodName = if (isGranted) "grantRuntimePermission" else "revokeRuntimePermission"
val callingUid = Binder.getCallingUid()
- val isDebugEnabled = if (isGranted) {
- PermissionManager.DEBUG_TRACE_GRANTS
- } else {
- PermissionManager.DEBUG_TRACE_PERMISSION_UPDATES
- }
- if (isDebugEnabled &&
- PermissionManager.shouldTraceGrant(packageName, permissionName, userId)) {
+ val isDebugEnabled =
+ if (isGranted) {
+ PermissionManager.DEBUG_TRACE_GRANTS
+ } else {
+ PermissionManager.DEBUG_TRACE_PERMISSION_UPDATES
+ }
+ if (
+ isDebugEnabled &&
+ PermissionManager.shouldTraceGrant(packageName, permissionName, userId)
+ ) {
val callingUidName = packageManagerInternal.getNameForUid(callingUid)
Slog.i(
- LOG_TAG, "$methodName(packageName = $packageName," +
+ LOG_TAG,
+ "$methodName(packageName = $packageName," +
" permissionName = $permissionName" +
(if (isGranted) "" else "skipKillUid = $skipKillUid, reason = $revokeReason") +
- ", userId = $userId," + " callingUid = $callingUidName ($callingUid))",
+ ", userId = $userId," +
+ " callingUid = $callingUidName ($callingUid))",
RuntimeException()
)
}
@@ -727,23 +748,31 @@
}
enforceCallingOrSelfCrossUserPermission(
- userId, enforceFullPermission = true, enforceShellRestriction = true, methodName
+ userId,
+ enforceFullPermission = true,
+ enforceShellRestriction = true,
+ methodName
)
- val enforcedPermissionName = if (isGranted) {
- Manifest.permission.GRANT_RUNTIME_PERMISSIONS
- } else {
- Manifest.permission.REVOKE_RUNTIME_PERMISSIONS
- }
+ val enforcedPermissionName =
+ if (isGranted) {
+ Manifest.permission.GRANT_RUNTIME_PERMISSIONS
+ } else {
+ Manifest.permission.REVOKE_RUNTIME_PERMISSIONS
+ }
context.enforceCallingOrSelfPermission(enforcedPermissionName, methodName)
val packageState: PackageState?
- val permissionControllerPackageName = packageManagerInternal.getKnownPackageNames(
- KnownPackages.PACKAGE_PERMISSION_CONTROLLER, UserHandle.USER_SYSTEM
- ).first()
+ val permissionControllerPackageName =
+ packageManagerInternal
+ .getKnownPackageNames(
+ KnownPackages.PACKAGE_PERMISSION_CONTROLLER,
+ UserHandle.USER_SYSTEM
+ )
+ .first()
val permissionControllerPackageState: PackageState?
packageManagerLocal.withUnfilteredSnapshot().use { snapshot ->
- packageState = snapshot.filtered(callingUid, userId)
- .use { it.getPackageState(packageName) }
+ packageState =
+ snapshot.filtered(callingUid, userId).use { it.getPackageState(packageName) }
permissionControllerPackageState =
snapshot.getPackageState(permissionControllerPackageName)
}
@@ -756,11 +785,13 @@
return
}
- val canManageRolePermission = isRootOrSystemUid(callingUid) ||
- UserHandle.getAppId(callingUid) == permissionControllerPackageState!!.appId
- val overridePolicyFixed = context.checkCallingOrSelfPermission(
- Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY
- ) == PackageManager.PERMISSION_GRANTED
+ val canManageRolePermission =
+ isRootOrSystemUid(callingUid) ||
+ UserHandle.getAppId(callingUid) == permissionControllerPackageState!!.appId
+ val overridePolicyFixed =
+ context.checkCallingOrSelfPermission(
+ Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY
+ ) == PackageManager.PERMISSION_GRANTED
service.mutateState {
with(onPermissionFlagsChangedListener) {
@@ -773,8 +804,15 @@
}
setRuntimePermissionGranted(
- packageState, userId, permissionName, deviceId, isGranted, canManageRolePermission,
- overridePolicyFixed, reportError = true, methodName
+ packageState,
+ userId,
+ permissionName,
+ deviceId,
+ isGranted,
+ canManageRolePermission,
+ overridePolicyFixed,
+ reportError = true,
+ methodName
)
}
}
@@ -791,8 +829,9 @@
PackageInstaller.SessionParams.PERMISSION_STATE_DENIED -> {}
else -> {
Slog.w(
- LOG_TAG, "setRequestedPermissionStates: Unknown permission state" +
- " $permissionState for permission $permissionName"
+ LOG_TAG,
+ "setRequestedPermissionStates: Unknown permission state" +
+ " $permissionState for permission $permissionName"
)
return@forEachIndexed
}
@@ -800,35 +839,50 @@
if (permissionName !in packageState.androidPackage!!.requestedPermissions) {
return@forEachIndexed
}
- val permission = with(policy) { getPermissions()[permissionName] }
- ?: return@forEachIndexed
+ val permission =
+ with(policy) { getPermissions()[permissionName] } ?: return@forEachIndexed
when {
permission.isDevelopment || permission.isRuntime -> {
- if (permissionState ==
- PackageInstaller.SessionParams.PERMISSION_STATE_GRANTED) {
+ if (
+ permissionState ==
+ PackageInstaller.SessionParams.PERMISSION_STATE_GRANTED
+ ) {
setRuntimePermissionGranted(
- packageState, userId, permissionName, Context.DEVICE_ID_DEFAULT,
- isGranted = true, canManageRolePermission = false,
- overridePolicyFixed = false, reportError = false,
+ packageState,
+ userId,
+ permissionName,
+ Context.DEVICE_ID_DEFAULT,
+ isGranted = true,
+ canManageRolePermission = false,
+ overridePolicyFixed = false,
+ reportError = false,
"setRequestedPermissionStates"
)
updatePermissionFlags(
- packageState.appId, userId, permissionName,
+ packageState.appId,
+ userId,
+ permissionName,
Context.DEVICE_ID_DEFAULT,
PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED or
- PackageManager.FLAG_PERMISSION_REVOKED_COMPAT, 0,
+ PackageManager.FLAG_PERMISSION_REVOKED_COMPAT,
+ 0,
canUpdateSystemFlags = false,
reportErrorForUnknownPermission = false,
- isPermissionRequested = true, "setRequestedPermissionStates",
+ isPermissionRequested = true,
+ "setRequestedPermissionStates",
packageState.packageName
)
}
}
- permission.isAppOp && permissionName in
+ permission.isAppOp &&
+ permissionName in
PackageInstallerService.INSTALLER_CHANGEABLE_APP_OP_PERMISSIONS ->
setAppOpPermissionGranted(
- packageState, userId, permissionName, permissionState ==
- PackageInstaller.SessionParams.PERMISSION_STATE_GRANTED
+ packageState,
+ userId,
+ permissionName,
+ permissionState ==
+ PackageInstaller.SessionParams.PERMISSION_STATE_GRANTED
)
else -> {}
}
@@ -836,9 +890,7 @@
}
}
- /**
- * Set whether a runtime permission is granted, without any validation on caller.
- */
+ /** Set whether a runtime permission is granted, without any validation on caller. */
private fun MutateStateScope.setRuntimePermissionGranted(
packageState: PackageState,
userId: Int,
@@ -876,8 +928,11 @@
// their permissions as always granted
return
}
- if (isGranted && packageState.getUserStateOrDefault(userId).isInstantApp &&
- !permission.isInstant) {
+ if (
+ isGranted &&
+ packageState.getUserStateOrDefault(userId).isInstantApp &&
+ !permission.isInstant
+ ) {
if (reportError) {
throw SecurityException(
"Cannot grant non-instant permission $permissionName to package" +
@@ -913,7 +968,8 @@
if (oldFlags.hasBits(PermissionFlags.SYSTEM_FIXED)) {
if (reportError) {
Slog.e(
- LOG_TAG, "$methodName: Cannot change system fixed permission $permissionName" +
+ LOG_TAG,
+ "$methodName: Cannot change system fixed permission $permissionName" +
" for package $packageName"
)
}
@@ -923,7 +979,8 @@
if (oldFlags.hasBits(PermissionFlags.POLICY_FIXED) && !overridePolicyFixed) {
if (reportError) {
Slog.e(
- LOG_TAG, "$methodName: Cannot change policy fixed permission $permissionName" +
+ LOG_TAG,
+ "$methodName: Cannot change policy fixed permission $permissionName" +
" for package $packageName"
)
}
@@ -933,7 +990,8 @@
if (isGranted && oldFlags.hasBits(PermissionFlags.RESTRICTION_REVOKED)) {
if (reportError) {
Slog.e(
- LOG_TAG, "$methodName: Cannot grant hard-restricted non-exempt permission" +
+ LOG_TAG,
+ "$methodName: Cannot grant hard-restricted non-exempt permission" +
" $permissionName to package $packageName"
)
}
@@ -942,14 +1000,19 @@
if (isGranted && oldFlags.hasBits(PermissionFlags.SOFT_RESTRICTED)) {
// TODO: Refactor SoftRestrictedPermissionPolicy.
- val softRestrictedPermissionPolicy = SoftRestrictedPermissionPolicy.forPermission(
- context, AndroidPackageUtils.generateAppInfoWithoutState(androidPackage),
- androidPackage, UserHandle.of(userId), permissionName
- )
+ val softRestrictedPermissionPolicy =
+ SoftRestrictedPermissionPolicy.forPermission(
+ context,
+ AndroidPackageUtils.generateAppInfoWithoutState(androidPackage),
+ androidPackage,
+ UserHandle.of(userId),
+ permissionName
+ )
if (!softRestrictedPermissionPolicy.mayGrantPermission()) {
if (reportError) {
Slog.e(
- LOG_TAG, "$methodName: Cannot grant soft-restricted non-exempt permission" +
+ LOG_TAG,
+ "$methodName: Cannot grant soft-restricted non-exempt permission" +
" $permissionName to package $packageName"
)
}
@@ -965,15 +1028,17 @@
setPermissionFlagsWithPolicy(appId, userId, permissionName, deviceId, newFlags)
if (permission.isRuntime) {
- val action = if (isGranted) {
- MetricsProto.MetricsEvent.ACTION_PERMISSION_GRANTED
- } else {
- MetricsProto.MetricsEvent.ACTION_PERMISSION_REVOKED
- }
- val log = LogMaker(action).apply {
- setPackageName(packageName)
- addTaggedData(MetricsProto.MetricsEvent.FIELD_PERMISSION, permissionName)
- }
+ val action =
+ if (isGranted) {
+ MetricsProto.MetricsEvent.ACTION_PERMISSION_GRANTED
+ } else {
+ MetricsProto.MetricsEvent.ACTION_PERMISSION_REVOKED
+ }
+ val log =
+ LogMaker(action).apply {
+ setPackageName(packageName)
+ addTaggedData(MetricsProto.MetricsEvent.FIELD_PERMISSION, permissionName)
+ }
metricsLogger.write(log)
}
}
@@ -984,8 +1049,8 @@
permissionName: String,
isGranted: Boolean
) {
- val appOpPolicy = service.getSchemePolicy(UidUri.SCHEME, AppOpUri.SCHEME) as
- AppIdAppOpPolicy
+ val appOpPolicy =
+ service.getSchemePolicy(UidUri.SCHEME, AppOpUri.SCHEME) as AppIdAppOpPolicy
val appOpName = AppOpsManager.permissionToOp(permissionName)!!
val mode = if (isGranted) AppOpsManager.MODE_ALLOWED else AppOpsManager.MODE_ERRORED
with(appOpPolicy) { setAppOpMode(packageState.appId, userId, appOpName, mode) }
@@ -1003,17 +1068,20 @@
}
enforceCallingOrSelfCrossUserPermission(
- userId, enforceFullPermission = true, enforceShellRestriction = false,
+ userId,
+ enforceFullPermission = true,
+ enforceShellRestriction = false,
"getPermissionFlags"
)
enforceCallingOrSelfAnyPermission(
- "getPermissionFlags", Manifest.permission.GRANT_RUNTIME_PERMISSIONS,
+ "getPermissionFlags",
+ Manifest.permission.GRANT_RUNTIME_PERMISSIONS,
Manifest.permission.REVOKE_RUNTIME_PERMISSIONS,
Manifest.permission.GET_RUNTIME_PERMISSIONS
)
- val packageState = packageManagerLocal.withFilteredSnapshot()
- .use { it.getPackageState(packageName) }
+ val packageState =
+ packageManagerLocal.withFilteredSnapshot().use { it.getPackageState(packageName) }
if (packageState == null) {
Slog.w(LOG_TAG, "getPermissionFlags: Unknown package $packageName")
return 0
@@ -1045,12 +1113,17 @@
}
enforceCallingOrSelfCrossUserPermission(
- userId, enforceFullPermission = true, enforceShellRestriction = false,
+ userId,
+ enforceFullPermission = true,
+ enforceShellRestriction = false,
"isPermissionRevokedByPolicy"
)
- val packageState = packageManagerLocal.withFilteredSnapshot(Binder.getCallingUid(), userId)
- .use { it.getPackageState(packageName) } ?: return false
+ val packageState =
+ packageManagerLocal.withFilteredSnapshot(Binder.getCallingUid(), userId).use {
+ it.getPackageState(packageName)
+ }
+ ?: return false
service.getState {
if (isPermissionGranted(packageState, userId, permissionName, deviceId)) {
@@ -1069,12 +1142,13 @@
// TODO(b/173235285): Some caller may pass USER_ALL as userId.
// Preconditions.checkArgumentNonnegative(userId, "userId")
- val packageState = packageManagerLocal.withUnfilteredSnapshot()
- .use { it.getPackageState(packageName) } ?: return false
+ val packageState =
+ packageManagerLocal.withUnfilteredSnapshot().use { it.getPackageState(packageName) }
+ ?: return false
- val permissionFlags = service.getState {
- with(policy) { getUidPermissionFlags(packageState.appId, userId) }
- } ?: return false
+ val permissionFlags =
+ service.getState { with(policy) { getUidPermissionFlags(packageState.appId, userId) } }
+ ?: return false
return permissionFlags.anyIndexed { _, _, it -> it.hasBits(REVIEW_REQUIRED_FLAGS) }
}
@@ -1090,13 +1164,18 @@
}
enforceCallingOrSelfCrossUserPermission(
- userId, enforceFullPermission = true, enforceShellRestriction = false,
+ userId,
+ enforceFullPermission = true,
+ enforceShellRestriction = false,
"shouldShowRequestPermissionRationale"
)
val callingUid = Binder.getCallingUid()
- val packageState = packageManagerLocal.withFilteredSnapshot(callingUid, userId)
- .use { it.getPackageState(packageName) } ?: return false
+ val packageState =
+ packageManagerLocal.withFilteredSnapshot(callingUid, userId).use {
+ it.getPackageState(packageName)
+ }
+ ?: return false
val appId = packageState.appId
if (UserHandle.getAppId(callingUid) != appId) {
return false
@@ -1115,17 +1194,24 @@
}
if (permissionName == Manifest.permission.ACCESS_BACKGROUND_LOCATION) {
- val isBackgroundRationaleChangeEnabled = Binder::class.withClearedCallingIdentity {
- try {
- platformCompat.isChangeEnabledByPackageName(
- BACKGROUND_RATIONALE_CHANGE_ID, packageName, userId
- )
- } catch (e: RemoteException) {
- Slog.e(LOG_TAG, "shouldShowRequestPermissionRationale: Unable to check if" +
- " compatibility change is enabled", e)
- false
+ val isBackgroundRationaleChangeEnabled =
+ Binder::class.withClearedCallingIdentity {
+ try {
+ platformCompat.isChangeEnabledByPackageName(
+ BACKGROUND_RATIONALE_CHANGE_ID,
+ packageName,
+ userId
+ )
+ } catch (e: RemoteException) {
+ Slog.e(
+ LOG_TAG,
+ "shouldShowRequestPermissionRationale: Unable to check if" +
+ " compatibility change is enabled",
+ e
+ )
+ false
+ }
}
- }
if (isBackgroundRationaleChangeEnabled) {
return true
}
@@ -1144,20 +1230,30 @@
userId: Int
) {
val callingUid = Binder.getCallingUid()
- if (PermissionManager.DEBUG_TRACE_PERMISSION_UPDATES &&
- PermissionManager.shouldTraceGrant(packageName, permissionName, userId)) {
- val flagMaskString = DebugUtils.flagsToString(
- PackageManager::class.java, "FLAG_PERMISSION_", flagMask.toLong()
- )
- val flagValuesString = DebugUtils.flagsToString(
- PackageManager::class.java, "FLAG_PERMISSION_", flagValues.toLong()
- )
+ if (
+ PermissionManager.DEBUG_TRACE_PERMISSION_UPDATES &&
+ PermissionManager.shouldTraceGrant(packageName, permissionName, userId)
+ ) {
+ val flagMaskString =
+ DebugUtils.flagsToString(
+ PackageManager::class.java,
+ "FLAG_PERMISSION_",
+ flagMask.toLong()
+ )
+ val flagValuesString =
+ DebugUtils.flagsToString(
+ PackageManager::class.java,
+ "FLAG_PERMISSION_",
+ flagValues.toLong()
+ )
val callingUidName = packageManagerInternal.getNameForUid(callingUid)
Slog.i(
- LOG_TAG, "updatePermissionFlags(packageName = $packageName," +
+ LOG_TAG,
+ "updatePermissionFlags(packageName = $packageName," +
" permissionName = $permissionName, flagMask = $flagMaskString," +
" flagValues = $flagValuesString, userId = $userId," +
- " callingUid = $callingUidName ($callingUid))", RuntimeException()
+ " callingUid = $callingUidName ($callingUid))",
+ RuntimeException()
)
}
@@ -1167,11 +1263,14 @@
}
enforceCallingOrSelfCrossUserPermission(
- userId, enforceFullPermission = true, enforceShellRestriction = true,
+ userId,
+ enforceFullPermission = true,
+ enforceShellRestriction = true,
"updatePermissionFlags"
)
enforceCallingOrSelfAnyPermission(
- "updatePermissionFlags", Manifest.permission.GRANT_RUNTIME_PERMISSIONS,
+ "updatePermissionFlags",
+ Manifest.permission.GRANT_RUNTIME_PERMISSIONS,
Manifest.permission.REVOKE_RUNTIME_PERMISSIONS
)
@@ -1208,8 +1307,10 @@
// Different from the old implementation, which returns when package doesn't exist but
// throws when package exists but isn't visible, we now return in both cases to avoid
// leaking the package existence.
- if (androidPackage == null ||
- packageManagerInternal.filterAppAccess(packageName, callingUid, userId, false)) {
+ if (
+ androidPackage == null ||
+ packageManagerInternal.filterAppAccess(packageName, callingUid, userId, false)
+ ) {
Slog.w(LOG_TAG, "updatePermissionFlags: Unknown package $packageName")
return
}
@@ -1219,26 +1320,35 @@
// permissions.
val canUpdateSystemFlags = isRootOrSystemUid(callingUid)
- val isPermissionRequested = if (permissionName in androidPackage.requestedPermissions) {
- // Fast path, the current package has requested the permission.
- true
- } else {
- // Slow path, go through all shared user packages.
- val sharedUserPackageNames =
- packageManagerInternal.getSharedUserPackagesForPackage(packageName, userId)
- sharedUserPackageNames.any { sharedUserPackageName ->
- val sharedUserPackage = packageManagerInternal.getPackage(sharedUserPackageName)
- sharedUserPackage != null &&
- permissionName in sharedUserPackage.requestedPermissions
+ val isPermissionRequested =
+ if (permissionName in androidPackage.requestedPermissions) {
+ // Fast path, the current package has requested the permission.
+ true
+ } else {
+ // Slow path, go through all shared user packages.
+ val sharedUserPackageNames =
+ packageManagerInternal.getSharedUserPackagesForPackage(packageName, userId)
+ sharedUserPackageNames.any { sharedUserPackageName ->
+ val sharedUserPackage = packageManagerInternal.getPackage(sharedUserPackageName)
+ sharedUserPackage != null &&
+ permissionName in sharedUserPackage.requestedPermissions
+ }
}
- }
val appId = packageState.appId
service.mutateState {
updatePermissionFlags(
- appId, userId, permissionName, deviceId, flagMask, flagValues, canUpdateSystemFlags,
- reportErrorForUnknownPermission = true, isPermissionRequested,
- "updatePermissionFlags", packageName
+ appId,
+ userId,
+ permissionName,
+ deviceId,
+ flagMask,
+ flagValues,
+ canUpdateSystemFlags,
+ reportErrorForUnknownPermission = true,
+ isPermissionRequested,
+ "updatePermissionFlags",
+ packageName
)
}
}
@@ -1246,17 +1356,25 @@
override fun updatePermissionFlagsForAllApps(flagMask: Int, flagValues: Int, userId: Int) {
val callingUid = Binder.getCallingUid()
if (PermissionManager.DEBUG_TRACE_PERMISSION_UPDATES) {
- val flagMaskString = DebugUtils.flagsToString(
- PackageManager::class.java, "FLAG_PERMISSION_", flagMask.toLong()
- )
- val flagValuesString = DebugUtils.flagsToString(
- PackageManager::class.java, "FLAG_PERMISSION_", flagValues.toLong()
- )
+ val flagMaskString =
+ DebugUtils.flagsToString(
+ PackageManager::class.java,
+ "FLAG_PERMISSION_",
+ flagMask.toLong()
+ )
+ val flagValuesString =
+ DebugUtils.flagsToString(
+ PackageManager::class.java,
+ "FLAG_PERMISSION_",
+ flagValues.toLong()
+ )
val callingUidName = packageManagerInternal.getNameForUid(callingUid)
Slog.i(
- LOG_TAG, "updatePermissionFlagsForAllApps(flagMask = $flagMaskString," +
+ LOG_TAG,
+ "updatePermissionFlagsForAllApps(flagMask = $flagMaskString," +
" flagValues = $flagValuesString, userId = $userId," +
- " callingUid = $callingUidName ($callingUid))", RuntimeException()
+ " callingUid = $callingUidName ($callingUid))",
+ RuntimeException()
)
}
@@ -1266,11 +1384,14 @@
}
enforceCallingOrSelfCrossUserPermission(
- userId, enforceFullPermission = true, enforceShellRestriction = true,
+ userId,
+ enforceFullPermission = true,
+ enforceShellRestriction = true,
"updatePermissionFlagsForAllApps"
)
enforceCallingOrSelfAnyPermission(
- "updatePermissionFlagsForAllApps", Manifest.permission.GRANT_RUNTIME_PERMISSIONS,
+ "updatePermissionFlagsForAllApps",
+ Manifest.permission.GRANT_RUNTIME_PERMISSIONS,
Manifest.permission.REVOKE_RUNTIME_PERMISSIONS
)
@@ -1278,26 +1399,30 @@
// flag, we now properly sanitize all flags as in updatePermissionFlags().
val canUpdateSystemFlags = isRootOrSystemUid(callingUid)
- val packageStates = packageManagerLocal.withUnfilteredSnapshot()
- .use { it.packageStates }
+ val packageStates = packageManagerLocal.withUnfilteredSnapshot().use { it.packageStates }
service.mutateState {
packageStates.forEach { (packageName, packageState) ->
val androidPackage = packageState.androidPackage ?: return@forEach
androidPackage.requestedPermissions.forEach { permissionName ->
updatePermissionFlags(
- packageState.appId, userId, permissionName, Context.DEVICE_ID_DEFAULT,
- flagMask, flagValues, canUpdateSystemFlags,
+ packageState.appId,
+ userId,
+ permissionName,
+ Context.DEVICE_ID_DEFAULT,
+ flagMask,
+ flagValues,
+ canUpdateSystemFlags,
reportErrorForUnknownPermission = false,
- isPermissionRequested = true, "updatePermissionFlagsForAllApps", packageName
+ isPermissionRequested = true,
+ "updatePermissionFlagsForAllApps",
+ packageName
)
}
}
}
}
- /**
- * Update flags for a permission, without any validation on caller.
- */
+ /** Update flags for a permission, without any validation on caller. */
private fun MutateStateScope.updatePermissionFlags(
appId: Int,
userId: Int,
@@ -1311,20 +1436,19 @@
methodName: String,
packageName: String
) {
- @Suppress("NAME_SHADOWING")
- var flagMask = flagMask
- @Suppress("NAME_SHADOWING")
- var flagValues = flagValues
+ @Suppress("NAME_SHADOWING") var flagMask = flagMask
+ @Suppress("NAME_SHADOWING") var flagValues = flagValues
// Only the system can change these flags and nothing else.
if (!canUpdateSystemFlags) {
// Different from the old implementation, which allowed non-system UIDs to remove (but
// not add) permission restriction flags, we now consistently ignore them altogether.
- val ignoredMask = PackageManager.FLAG_PERMISSION_SYSTEM_FIXED or
- PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT or
- PackageManager.FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT or
- PackageManager.FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT or
- PackageManager.FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT or
- PackageManager.FLAG_PERMISSION_APPLY_RESTRICTION
+ val ignoredMask =
+ PackageManager.FLAG_PERMISSION_SYSTEM_FIXED or
+ PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT or
+ PackageManager.FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT or
+ PackageManager.FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT or
+ PackageManager.FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT or
+ PackageManager.FLAG_PERMISSION_APPLY_RESTRICTION
flagMask = flagMask andInv ignoredMask
flagValues = flagValues andInv ignoredMask
}
@@ -1340,7 +1464,8 @@
val oldFlags = getPermissionFlagsWithPolicy(appId, userId, permissionName, deviceId)
if (!isPermissionRequested && oldFlags == 0) {
Slog.w(
- LOG_TAG, "$methodName: Permission $permissionName isn't requested by package" +
+ LOG_TAG,
+ "$methodName: Permission $permissionName isn't requested by package" +
" $packageName"
)
return
@@ -1365,21 +1490,29 @@
}
enforceCallingOrSelfCrossUserPermission(
- userId, enforceFullPermission = false, enforceShellRestriction = false,
+ userId,
+ enforceFullPermission = false,
+ enforceShellRestriction = false,
"getAllowlistedRestrictedPermissions"
)
val callingUid = Binder.getCallingUid()
- val packageState = packageManagerLocal.withFilteredSnapshot(callingUid, userId)
- .use { it.getPackageState(packageName) } ?: return null
+ val packageState =
+ packageManagerLocal.withFilteredSnapshot(callingUid, userId).use {
+ it.getPackageState(packageName)
+ }
+ ?: return null
val androidPackage = packageState.androidPackage ?: return null
- val isCallerPrivileged = context.checkCallingOrSelfPermission(
- Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS
- ) == PackageManager.PERMISSION_GRANTED
+ val isCallerPrivileged =
+ context.checkCallingOrSelfPermission(
+ Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS
+ ) == PackageManager.PERMISSION_GRANTED
- if (allowlistedFlags.hasBits(PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM) &&
- !isCallerPrivileged) {
+ if (
+ allowlistedFlags.hasBits(PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM) &&
+ !isCallerPrivileged
+ ) {
throw SecurityException(
"Querying system allowlist requires " +
Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS
@@ -1389,8 +1522,12 @@
val isCallerInstallerOnRecord =
packageManagerInternal.isCallerInstallerOfRecord(androidPackage, callingUid)
- if (allowlistedFlags.hasAnyBit(PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE or
- PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER)) {
+ if (
+ allowlistedFlags.hasAnyBit(
+ PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE or
+ PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER
+ )
+ ) {
if (!isCallerPrivileged && !isCallerInstallerOnRecord) {
throw SecurityException(
"Querying upgrade or installer allowlist requires being installer on record" +
@@ -1400,7 +1537,9 @@
}
return getAllowlistedRestrictedPermissionsUnchecked(
- packageState.appId, allowlistedFlags, userId
+ packageState.appId,
+ allowlistedFlags,
+ userId
)
}
@@ -1414,8 +1553,11 @@
with(policy) { getPermissionFlags(appId, userId, permissionName) }
} else {
if (permissionName !in DevicePermissionPolicy.DEVICE_AWARE_PERMISSIONS) {
- Slog.i(LOG_TAG, "$permissionName is not device aware permission, " +
- " get the flags for default device.")
+ Slog.i(
+ LOG_TAG,
+ "$permissionName is not device aware permission, " +
+ " get the flags for default device."
+ )
return with(policy) { getPermissionFlags(appId, userId, permissionName) }
}
val virtualDeviceManagerInternal = virtualDeviceManagerInternal
@@ -1423,8 +1565,7 @@
Slog.e(LOG_TAG, "Virtual device manager service is not available.")
return 0
}
- val persistentDeviceId =
- virtualDeviceManagerInternal.getPersistentIdForDevice(deviceId)
+ val persistentDeviceId = virtualDeviceManagerInternal.getPersistentIdForDevice(deviceId)
if (persistentDeviceId != null) {
with(devicePolicy) {
getPermissionFlags(appId, persistentDeviceId, userId, permissionName)
@@ -1444,13 +1585,14 @@
flags: Int
): Boolean {
return if (!Flags.deviceAwarePermissionApis() || deviceId == Context.DEVICE_ID_DEFAULT) {
- with(policy) {
- setPermissionFlags(appId, userId, permissionName, flags)
- }
+ with(policy) { setPermissionFlags(appId, userId, permissionName, flags) }
} else {
if (permissionName !in DevicePermissionPolicy.DEVICE_AWARE_PERMISSIONS) {
- Slog.i(LOG_TAG, "$permissionName is not device aware permission, " +
- " set the flags for default device.")
+ Slog.i(
+ LOG_TAG,
+ "$permissionName is not device aware permission, " +
+ " set the flags for default device."
+ )
return with(policy) { setPermissionFlags(appId, userId, permissionName, flags) }
}
@@ -1459,8 +1601,7 @@
Slog.e(LOG_TAG, "Virtual device manager service is not available.")
return false
}
- val persistentDeviceId =
- virtualDeviceManagerInternal.getPersistentIdForDevice(deviceId)
+ val persistentDeviceId = virtualDeviceManagerInternal.getPersistentIdForDevice(deviceId)
if (persistentDeviceId != null) {
with(devicePolicy) {
setPermissionFlags(appId, persistentDeviceId, userId, permissionName, flags)
@@ -1473,17 +1614,17 @@
}
/**
- * This method does not enforce checks on the caller, should only be called after
- * required checks.
+ * This method does not enforce checks on the caller, should only be called after required
+ * checks.
*/
private fun getAllowlistedRestrictedPermissionsUnchecked(
appId: Int,
allowlistedFlags: Int,
userId: Int
): ArrayList<String>? {
- val permissionFlags = service.getState {
- with(policy) { getUidPermissionFlags(appId, userId) }
- } ?: return null
+ val permissionFlags =
+ service.getState { with(policy) { getUidPermissionFlags(appId, userId) } }
+ ?: return null
var queryFlags = 0
if (allowlistedFlags.hasBits(PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM)) {
@@ -1512,14 +1653,18 @@
return false
}
- val permissionNames = getAllowlistedRestrictedPermissions(
- packageName, allowlistedFlags, userId
- ) ?: ArrayList(1)
+ val permissionNames =
+ getAllowlistedRestrictedPermissions(packageName, allowlistedFlags, userId)
+ ?: ArrayList(1)
if (permissionName !in permissionNames) {
permissionNames += permissionName
return setAllowlistedRestrictedPermissions(
- packageName, permissionNames, allowlistedFlags, userId, isAddingPermission = true
+ packageName,
+ permissionNames,
+ allowlistedFlags,
+ userId,
+ isAddingPermission = true
)
}
return false
@@ -1531,14 +1676,22 @@
permissionNames: List<String>,
userId: Int
) {
- val newPermissionNames = getAllowlistedRestrictedPermissionsUnchecked(appId,
- PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER, userId
- )?.let {
- ArraySet(permissionNames).apply { this += it }.toList()
- } ?: permissionNames
+ val newPermissionNames =
+ getAllowlistedRestrictedPermissionsUnchecked(
+ appId,
+ PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER,
+ userId
+ )
+ ?.let { ArraySet(permissionNames).apply { this += it }.toList() }
+ ?: permissionNames
- setAllowlistedRestrictedPermissionsUnchecked(androidPackage, appId, newPermissionNames,
- PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER, userId)
+ setAllowlistedRestrictedPermissionsUnchecked(
+ androidPackage,
+ appId,
+ newPermissionNames,
+ PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER,
+ userId
+ )
}
override fun removeAllowlistedRestrictedPermission(
@@ -1552,13 +1705,17 @@
return false
}
- val permissions = getAllowlistedRestrictedPermissions(
- packageName, allowlistedFlags, userId
- ) ?: return false
+ val permissions =
+ getAllowlistedRestrictedPermissions(packageName, allowlistedFlags, userId)
+ ?: return false
if (permissions.remove(permissionName)) {
return setAllowlistedRestrictedPermissions(
- packageName, permissions, allowlistedFlags, userId, isAddingPermission = false
+ packageName,
+ permissions,
+ allowlistedFlags,
+ userId,
+ isAddingPermission = false
)
}
@@ -1572,16 +1729,22 @@
return false
}
- if (packageManagerLocal.withFilteredSnapshot()
- .use { it.getPackageState(permission.packageName) } == null) {
+ if (
+ packageManagerLocal.withFilteredSnapshot().use {
+ it.getPackageState(permission.packageName)
+ } == null
+ ) {
return false
}
val isImmutablyRestrictedPermission =
permission.isHardOrSoftRestricted && permission.isImmutablyRestricted
- if (isImmutablyRestrictedPermission && context.checkCallingOrSelfPermission(
- Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS
- ) != PackageManager.PERMISSION_GRANTED) {
+ if (
+ isImmutablyRestrictedPermission &&
+ context.checkCallingOrSelfPermission(
+ Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS
+ ) != PackageManager.PERMISSION_GRANTED
+ ) {
throw SecurityException(
"Cannot modify allowlist of an immutably restricted permission: ${permission.name}"
)
@@ -1599,13 +1762,16 @@
): Boolean {
Preconditions.checkArgument(allowlistedFlags.countOneBits() == 1)
- val isCallerPrivileged = context.checkCallingOrSelfPermission(
- Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS
- ) == PackageManager.PERMISSION_GRANTED
+ val isCallerPrivileged =
+ context.checkCallingOrSelfPermission(
+ Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS
+ ) == PackageManager.PERMISSION_GRANTED
val callingUid = Binder.getCallingUid()
- val packageState = packageManagerLocal.withFilteredSnapshot(callingUid, userId)
- .use { snapshot -> snapshot.packageStates[packageName] ?: return false }
+ val packageState =
+ packageManagerLocal.withFilteredSnapshot(callingUid, userId).use { snapshot ->
+ snapshot.packageStates[packageName] ?: return false
+ }
val androidPackage = packageState.androidPackage ?: return false
val isCallerInstallerOnRecord =
@@ -1627,15 +1793,19 @@
}
setAllowlistedRestrictedPermissionsUnchecked(
- androidPackage, packageState.appId, permissionNames, allowlistedFlags, userId
+ androidPackage,
+ packageState.appId,
+ permissionNames,
+ allowlistedFlags,
+ userId
)
return true
}
/**
- * This method does not enforce checks on the caller, should only be called after
- * required checks.
+ * This method does not enforce checks on the caller, should only be called after required
+ * checks.
*/
private fun setAllowlistedRestrictedPermissionsUnchecked(
androidPackage: AndroidPackage,
@@ -1712,22 +1882,24 @@
}
}
- newFlags = if (permission.isHardRestricted && !isExempt) {
- newFlags or PermissionFlags.RESTRICTION_REVOKED
- } else {
- newFlags andInv PermissionFlags.RESTRICTION_REVOKED
- }
- newFlags = if (permission.isSoftRestricted && !isExempt) {
- newFlags or PermissionFlags.SOFT_RESTRICTED
- } else {
- newFlags andInv PermissionFlags.SOFT_RESTRICTED
- }
- mask = mask or PermissionFlags.RESTRICTION_REVOKED or
- PermissionFlags.SOFT_RESTRICTED
+ newFlags =
+ if (permission.isHardRestricted && !isExempt) {
+ newFlags or PermissionFlags.RESTRICTION_REVOKED
+ } else {
+ newFlags andInv PermissionFlags.RESTRICTION_REVOKED
+ }
+ newFlags =
+ if (permission.isSoftRestricted && !isExempt) {
+ newFlags or PermissionFlags.SOFT_RESTRICTED
+ } else {
+ newFlags andInv PermissionFlags.SOFT_RESTRICTED
+ }
+ mask =
+ mask or
+ PermissionFlags.RESTRICTION_REVOKED or
+ PermissionFlags.SOFT_RESTRICTED
- updatePermissionFlags(
- appId, userId, requestedPermission, mask, newFlags
- )
+ updatePermissionFlags(appId, userId, requestedPermission, mask, newFlags)
}
}
}
@@ -1735,9 +1907,8 @@
override fun resetRuntimePermissions(androidPackage: AndroidPackage, userId: Int) {
service.mutateState {
- with(policy) {
- resetRuntimePermissions(androidPackage.packageName, userId)
- }
+ with(policy) { resetRuntimePermissions(androidPackage.packageName, userId) }
+ with(devicePolicy) { resetRuntimePermissions(androidPackage.packageName, userId) }
}
}
@@ -1745,9 +1916,8 @@
packageManagerLocal.withUnfilteredSnapshot().use { snapshot ->
service.mutateState {
snapshot.packageStates.forEach { (_, packageState) ->
- with(policy) {
- resetRuntimePermissions(packageState.packageName, userId)
- }
+ with(policy) { resetRuntimePermissions(packageState.packageName, userId) }
+ with(devicePolicy) { resetRuntimePermissions(packageState.packageName, userId) }
}
}
}
@@ -1755,7 +1925,8 @@
override fun addOnPermissionsChangeListener(listener: IOnPermissionsChangeListener) {
context.enforceCallingOrSelfPermission(
- Manifest.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS, "addOnPermissionsChangeListener"
+ Manifest.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS,
+ "addOnPermissionsChangeListener"
)
onPermissionsChangeListeners.addListener(listener)
@@ -1780,9 +1951,7 @@
requireNotNull(permissionName) { "permissionName cannot be null" }
val packageNames = ArraySet<String>()
- val permission = service.getState {
- with(policy) { getPermissions()[permissionName] }
- }
+ val permission = service.getState { with(policy) { getPermissions()[permissionName] } }
if (permission == null || !permission.isAppOp) {
packageNames.toTypedArray()
}
@@ -1808,8 +1977,8 @@
androidPackage.requestedPermissions.forEach requestedPermissions@{ permissionName ->
val permission = permissions[permissionName] ?: return@requestedPermissions
if (permission.isAppOp) {
- val packageNames = appOpPermissionPackageNames
- .getOrPut(permissionName) { ArraySet() }
+ val packageNames =
+ appOpPermissionPackageNames.getOrPut(permissionName) { ArraySet() }
packageNames += androidPackage.packageName
}
}
@@ -1822,14 +1991,18 @@
Preconditions.checkArgumentNonnegative(userId, "userId cannot be null")
val backup = CompletableFuture<ByteArray>()
permissionControllerManager.getRuntimePermissionBackup(
- UserHandle.of(userId), PermissionThread.getExecutor(), backup::complete
+ UserHandle.of(userId),
+ PermissionThread.getExecutor(),
+ backup::complete
)
return try {
backup.get(BACKUP_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)
} catch (e: Exception) {
when (e) {
- is TimeoutException, is InterruptedException, is ExecutionException -> {
+ is TimeoutException,
+ is InterruptedException,
+ is ExecutionException -> {
Slog.e(LOG_TAG, "Cannot create permission backup for user $userId", e)
null
}
@@ -1846,7 +2019,8 @@
isDelayedPermissionBackupFinished -= userId
}
permissionControllerManager.stageAndApplyRuntimePermissionsBackup(
- backup, UserHandle.of(userId)
+ backup,
+ UserHandle.of(userId)
)
}
@@ -1860,7 +2034,9 @@
}
}
permissionControllerManager.applyStagedRuntimePermissionBackup(
- packageName, UserHandle.of(userId), PermissionThread.getExecutor()
+ packageName,
+ UserHandle.of(userId),
+ PermissionThread.getExecutor()
) { hasMoreBackup ->
if (hasMoreBackup) {
return@applyStagedRuntimePermissionBackup
@@ -1907,21 +2083,15 @@
): IndexedMap<Int, MutableIndexedSet<String>> {
val appIds = MutableIndexedSet<Int>()
- val packageStates = packageManagerLocal.withUnfilteredSnapshot().use {
- it.packageStates
- }
+ val packageStates = packageManagerLocal.withUnfilteredSnapshot().use { it.packageStates }
state.userStates.forEachIndexed { _, _, userState ->
- userState.appIdPermissionFlags.forEachIndexed { _, appId, _ ->
- appIds.add(appId)
- }
- userState.appIdAppOpModes.forEachIndexed { _, appId, _ ->
- appIds.add(appId)
- }
- userState.packageVersions.forEachIndexed packageVersions@ { _, packageName, _ ->
+ userState.appIdPermissionFlags.forEachIndexed { _, appId, _ -> appIds.add(appId) }
+ userState.appIdAppOpModes.forEachIndexed { _, appId, _ -> appIds.add(appId) }
+ userState.packageVersions.forEachIndexed packageVersions@{ _, packageName, _ ->
val appId = packageStates[packageName]?.appId ?: return@packageVersions
appIds.add(appId)
}
- userState.packageAppOpModes.forEachIndexed packageAppOpModes@ { _, packageName, _ ->
+ userState.packageAppOpModes.forEachIndexed packageAppOpModes@{ _, packageName, _ ->
val appId = packageStates[packageName]?.appId ?: return@packageAppOpModes
appIds.add(appId)
}
@@ -1929,7 +2099,8 @@
val appIdPackageNames = MutableIndexedMap<Int, MutableIndexedSet<String>>()
packageStates.forEach { (_, packageState) ->
- appIdPackageNames.getOrPut(packageState.appId) { MutableIndexedSet() }
+ appIdPackageNames
+ .getOrPut(packageState.appId) { MutableIndexedSet() }
.add(packageState.packageName)
}
// add non-package app IDs which might not be reported by package manager.
@@ -1960,10 +2131,7 @@
println("Permission groups:")
withIndent {
state.systemState.permissionGroups.forEachIndexed { _, _, permissionGroup ->
- println(
- "${permissionGroup.name}: " +
- "packageName=${permissionGroup.packageName}"
- )
+ println("${permissionGroup.name}: " + "packageName=${permissionGroup.packageName}")
}
}
@@ -1992,7 +2160,9 @@
println("Permissions:")
withIndent {
userState.appIdPermissionFlags[appId]?.forEachIndexed {
- _, permissionName, flags ->
+ _,
+ permissionName,
+ flags ->
val isGranted = PermissionFlags.isPermissionGranted(flags)
println(
"$permissionName: granted=$isGranted, flags=" +
@@ -2002,7 +2172,9 @@
}
userState.appIdDevicePermissionFlags[appId]?.forEachIndexed {
- _, deviceId, devicePermissionFlags ->
+ _,
+ deviceId,
+ devicePermissionFlags ->
println("Permissions (Device $deviceId):")
withIndent {
devicePermissionFlags.forEachIndexed { _, permissionName, flags ->
@@ -2017,7 +2189,8 @@
println("App ops:")
withIndent {
- userState.appIdAppOpModes[appId]?.forEachIndexed {_, appOpName, appOpMode ->
+ userState.appIdAppOpModes[appId]?.forEachIndexed { _, appOpName, appOpMode
+ ->
println("$appOpName: mode=${AppOpsManager.modeToName(appOpMode)}")
}
}
@@ -2029,7 +2202,9 @@
println("App ops:")
withIndent {
userState.packageAppOpModes[packageName]?.forEachIndexed {
- _, appOpName, appOpMode ->
+ _,
+ appOpName,
+ appOpMode ->
val modeName = AppOpsManager.modeToName(appOpMode)
println("$appOpName: mode=$modeName")
}
@@ -2048,24 +2223,30 @@
}
override fun getPermissionTEMP(permissionName: String): LegacyPermission2? {
- val permission = service.getState {
- with(policy) { getPermissions()[permissionName] }
- } ?: return null
+ val permission =
+ service.getState { with(policy) { getPermissions()[permissionName] } } ?: return null
return LegacyPermission2(
- permission.permissionInfo, permission.type, permission.isReconciled, permission.appId,
- permission.gids, permission.areGidsPerUser
+ permission.permissionInfo,
+ permission.type,
+ permission.isReconciled,
+ permission.appId,
+ permission.gids,
+ permission.areGidsPerUser
)
}
override fun getLegacyPermissions(): List<LegacyPermission> =
- service.getState {
- with(policy) { getPermissions() }
- }.mapIndexedTo(ArrayList()) { _, _, permission ->
- LegacyPermission(
- permission.permissionInfo, permission.type, permission.appId, permission.gids
- )
- }
+ service
+ .getState { with(policy) { getPermissions() } }
+ .mapIndexedTo(ArrayList()) { _, _, permission ->
+ LegacyPermission(
+ permission.permissionInfo,
+ permission.type,
+ permission.appId,
+ permission.gids
+ )
+ }
override fun readLegacyPermissionsTEMP(legacyPermissionSettings: LegacyPermissionSettings) {
// Package settings has been read when this method is called.
@@ -2086,9 +2267,7 @@
): List<LegacyPermission> =
permissions.mapIndexedTo(ArrayList()) { _, _, permission ->
// We don't need to provide UID and GIDs, which are only retrieved when dumping.
- LegacyPermission(
- permission.permissionInfo, permission.type, 0, EmptyArray.INT
- )
+ LegacyPermission(permission.permissionInfo, permission.type, 0, EmptyArray.INT)
}
override fun getLegacyPermissionState(appId: Int): LegacyPermissionState {
@@ -2097,17 +2276,18 @@
service.getState {
val permissions = with(policy) { getPermissions() }
userIds.forEachIndexed { _, userId ->
- val permissionFlags = with(policy) { getUidPermissionFlags(appId, userId) }
- ?: return@forEachIndexed
+ val permissionFlags =
+ with(policy) { getUidPermissionFlags(appId, userId) } ?: return@forEachIndexed
permissionFlags.forEachIndexed permissionFlags@{ _, permissionName, flags ->
val permission = permissions[permissionName] ?: return@permissionFlags
- val legacyPermissionState = LegacyPermissionState.PermissionState(
- permissionName,
- permission.isRuntime,
- PermissionFlags.isPermissionGranted(flags),
- PermissionFlags.toApiFlags(flags)
- )
+ val legacyPermissionState =
+ LegacyPermissionState.PermissionState(
+ permissionName,
+ permission.isRuntime,
+ PermissionFlags.isPermissionGranted(flags),
+ PermissionFlags.toApiFlags(flags)
+ )
legacyState.putPermissionState(legacyPermissionState, userId)
}
}
@@ -2131,16 +2311,13 @@
override fun onSystemReady() {
service.onSystemReady()
virtualDeviceManagerInternal =
- LocalServices.getService(VirtualDeviceManagerInternal::class.java)
- permissionControllerManager = PermissionControllerManager(
- context, PermissionThread.getHandler()
- )
+ LocalServices.getService(VirtualDeviceManagerInternal::class.java)
+ permissionControllerManager =
+ PermissionControllerManager(context, PermissionThread.getHandler())
}
override fun onUserCreated(userId: Int) {
- withCorkedPackageInfoCache {
- service.onUserAdded(userId)
- }
+ withCorkedPackageInfoCache { service.onUserAdded(userId) }
}
override fun onUserRemoved(userId: Int) {
@@ -2170,9 +2347,8 @@
// of onPackageAdded() and reuse this order in onStorageVolumeAdded(). We need the
// packages to be iterated in onStorageVolumeAdded() in the same order so that the
// ownership of permissions is consistent.
- storageVolumePackageNames.getOrPut(packageState.volumeUuid) {
- mutableListOf()
- } += packageState.packageName
+ storageVolumePackageNames.getOrPut(packageState.volumeUuid) { mutableListOf() } +=
+ packageState.packageName
if (packageState.volumeUuid !in mountedStorageVolumes) {
// Wait for the storage volume to be mounted and batch the state mutation there.
return
@@ -2212,23 +2388,26 @@
return
}
}
- val userIds = if (userId == UserHandle.USER_ALL) {
- userManagerService.userIdsIncludingPreCreated
- } else {
- intArrayOf(userId)
- }
+ val userIds =
+ if (userId == UserHandle.USER_ALL) {
+ userManagerService.userIdsIncludingPreCreated
+ } else {
+ intArrayOf(userId)
+ }
@Suppress("NAME_SHADOWING")
- userIds.forEach { userId ->
- service.onPackageInstalled(androidPackage.packageName, userId)
- }
+ userIds.forEach { userId -> service.onPackageInstalled(androidPackage.packageName, userId) }
@Suppress("NAME_SHADOWING")
userIds.forEach { userId ->
// TODO: Remove when this callback receives packageState directly.
val packageState =
packageManagerInternal.getPackageStateInternal(androidPackage.packageName)!!
- addAllowlistedRestrictedPermissionsUnchecked(androidPackage, packageState.appId,
- params.allowlistedRestrictedPermissions, userId)
+ addAllowlistedRestrictedPermissionsUnchecked(
+ androidPackage,
+ packageState.appId,
+ params.allowlistedRestrictedPermissions,
+ userId
+ )
setRequestedPermissionStates(packageState, userId, params.permissionStates)
}
}
@@ -2241,11 +2420,12 @@
sharedUserPkgs: List<AndroidPackage>,
userId: Int
) {
- val userIds = if (userId == UserHandle.USER_ALL) {
- userManagerService.userIdsIncludingPreCreated
- } else {
- intArrayOf(userId)
- }
+ val userIds =
+ if (userId == UserHandle.USER_ALL) {
+ userManagerService.userIdsIncludingPreCreated
+ } else {
+ intArrayOf(userId)
+ }
userIds.forEach { service.onPackageUninstalled(packageName, appId, it) }
val packageState = packageManagerInternal.packageStates[packageName]
if (packageState == null) {
@@ -2262,31 +2442,26 @@
}
}
- /**
- * Check whether a UID is root or system UID.
- */
+ /** Check whether a UID is root or system UID. */
private fun isRootOrSystemUid(uid: Int) =
when (UserHandle.getAppId(uid)) {
- Process.ROOT_UID, Process.SYSTEM_UID -> true
+ Process.ROOT_UID,
+ Process.SYSTEM_UID -> true
else -> false
}
- /**
- * Check whether a UID is shell UID.
- */
+ /** Check whether a UID is shell UID. */
private fun isShellUid(uid: Int) = UserHandle.getAppId(uid) == Process.SHELL_UID
- /**
- * Check whether a UID is root, system or shell UID.
- */
+ /** Check whether a UID is root, system or shell UID. */
private fun isRootOrSystemOrShellUid(uid: Int) = isRootOrSystemUid(uid) || isShellUid(uid)
/**
* This method should typically only be used when granting or revoking permissions, since the
* app may immediately restart after this call.
*
- * If you're doing surgery on app code/data, use [PackageFreezer] to guard your work against
- * the app being relaunched.
+ * If you're doing surgery on app code/data, use [PackageFreezer] to guard your work against the
+ * app being relaunched.
*/
private fun killUid(uid: Int, reason: String) {
val activityManager = ActivityManager.getService()
@@ -2303,9 +2478,7 @@
}
}
- /**
- * @see PackageManagerLocal.withFilteredSnapshot
- */
+ /** @see PackageManagerLocal.withFilteredSnapshot */
private fun PackageManagerLocal.withFilteredSnapshot(
callingUid: Int,
userId: Int
@@ -2323,35 +2496,27 @@
packageName: String
): PackageState? = packageStates[packageName]
- /**
- * Check whether a UID belongs to an instant app.
- */
+ /** Check whether a UID belongs to an instant app. */
private fun PackageManagerLocal.UnfilteredSnapshot.isUidInstantApp(uid: Int): Boolean =
// Unfortunately we don't have the API for getting the owner UID of an isolated UID or the
// API for getting the SharedUserApi object for an app ID yet, so for now we just keep
// calling the old API.
packageManagerInternal.getInstantAppPackageName(uid) != null
- /**
- * Check whether a package is visible to a UID within the same user as the UID.
- */
+ /** Check whether a package is visible to a UID within the same user as the UID. */
private fun PackageManagerLocal.UnfilteredSnapshot.isPackageVisibleToUid(
packageName: String,
uid: Int
): Boolean = isPackageVisibleToUid(packageName, UserHandle.getUserId(uid), uid)
- /**
- * Check whether a package in a particular user is visible to a UID.
- */
+ /** Check whether a package in a particular user is visible to a UID. */
private fun PackageManagerLocal.UnfilteredSnapshot.isPackageVisibleToUid(
packageName: String,
userId: Int,
uid: Int
): Boolean = filtered(uid, userId).use { it.getPackageState(packageName) != null }
- /**
- * @see PackageManagerLocal.UnfilteredSnapshot.filtered
- */
+ /** @see PackageManagerLocal.UnfilteredSnapshot.filtered */
private fun PackageManagerLocal.UnfilteredSnapshot.filtered(
callingUid: Int,
userId: Int
@@ -2374,13 +2539,16 @@
val callingUid = Binder.getCallingUid()
val callingUserId = UserHandle.getUserId(callingUid)
if (userId != callingUserId) {
- val permissionName = if (enforceFullPermission) {
- Manifest.permission.INTERACT_ACROSS_USERS_FULL
- } else {
- Manifest.permission.INTERACT_ACROSS_USERS
- }
- if (context.checkCallingOrSelfPermission(permissionName) !=
- PackageManager.PERMISSION_GRANTED) {
+ val permissionName =
+ if (enforceFullPermission) {
+ Manifest.permission.INTERACT_ACROSS_USERS_FULL
+ } else {
+ Manifest.permission.INTERACT_ACROSS_USERS
+ }
+ if (
+ context.checkCallingOrSelfPermission(permissionName) !=
+ PackageManager.PERMISSION_GRANTED
+ ) {
val exceptionMessage = buildString {
if (message != null) {
append(message)
@@ -2397,9 +2565,11 @@
}
}
if (enforceShellRestriction && isShellUid(callingUid)) {
- val isShellRestricted = userManagerInternal.hasUserRestriction(
- UserManager.DISALLOW_DEBUGGING_FEATURES, userId
- )
+ val isShellRestricted =
+ userManagerInternal.hasUserRestriction(
+ UserManager.DISALLOW_DEBUGGING_FEATURES,
+ userId
+ )
if (isShellRestricted) {
val exceptionMessage = buildString {
if (message != null) {
@@ -2424,10 +2594,11 @@
message: String?,
vararg permissionNames: String
) {
- val hasAnyPermission = permissionNames.any { permissionName ->
- context.checkCallingOrSelfPermission(permissionName) ==
- PackageManager.PERMISSION_GRANTED
- }
+ val hasAnyPermission =
+ permissionNames.any { permissionName ->
+ context.checkCallingOrSelfPermission(permissionName) ==
+ PackageManager.PERMISSION_GRANTED
+ }
if (!hasAnyPermission) {
val exceptionMessage = buildString {
if (message != null) {
@@ -2443,9 +2614,7 @@
}
}
- /**
- * Callback invoked when interesting actions have been taken on a permission.
- */
+ /** Callback invoked when interesting actions have been taken on a permission. */
private inner class OnPermissionFlagsChangedListener :
AppIdPermissionPolicy.OnPermissionFlagsChangedListener() {
private var isPermissionFlagsChanged = false
@@ -2476,9 +2645,8 @@
isPermissionFlagsChanged = true
val uid = UserHandle.getUid(userId, appId)
- val permission = service.getState {
- with(policy) { getPermissions()[permissionName] }
- } ?: return
+ val permission =
+ service.getState { with(policy) { getPermissions()[permissionName] } } ?: return
val wasPermissionGranted = PermissionFlags.isPermissionGranted(oldFlags)
val isPermissionGranted = PermissionFlags.isPermissionGranted(newFlags)
@@ -2512,16 +2680,20 @@
runtimePermissionChangedUids.clear()
if (!isKillRuntimePermissionRevokedUidsSkipped) {
- val reason = if (killRuntimePermissionRevokedUidsReasons.isNotEmpty()) {
- killRuntimePermissionRevokedUidsReasons.joinToString(", ")
- } else {
- PermissionManager.KILL_APP_REASON_PERMISSIONS_REVOKED
- }
+ val reason =
+ if (killRuntimePermissionRevokedUidsReasons.isNotEmpty()) {
+ killRuntimePermissionRevokedUidsReasons.joinToString(", ")
+ } else {
+ PermissionManager.KILL_APP_REASON_PERMISSIONS_REVOKED
+ }
runtimePermissionRevokedUids.forEachIndexed {
- _, uid, areOnlyNotificationsPermissionsRevoked ->
+ _,
+ uid,
+ areOnlyNotificationsPermissionsRevoked ->
handler.post {
- if (areOnlyNotificationsPermissionsRevoked &&
- isAppBackupAndRestoreRunning(uid)
+ if (
+ areOnlyNotificationsPermissionsRevoked &&
+ isAppBackupAndRestoreRunning(uid)
) {
return@post
}
@@ -2541,19 +2713,27 @@
}
private fun isAppBackupAndRestoreRunning(uid: Int): Boolean {
- if (checkUidPermission(uid, Manifest.permission.BACKUP, Context.DEVICE_ID_DEFAULT) !=
- PackageManager.PERMISSION_GRANTED) {
+ if (
+ checkUidPermission(uid, Manifest.permission.BACKUP, Context.DEVICE_ID_DEFAULT) !=
+ PackageManager.PERMISSION_GRANTED
+ ) {
return false
}
return try {
val contentResolver = context.contentResolver
val userId = UserHandle.getUserId(uid)
- val isInSetup = Settings.Secure.getIntForUser(
- contentResolver, Settings.Secure.USER_SETUP_COMPLETE, userId
- ) == 0
- val isInDeferredSetup = Settings.Secure.getIntForUser(
- contentResolver, Settings.Secure.USER_SETUP_PERSONALIZATION_STATE, userId
- ) == Settings.Secure.USER_SETUP_PERSONALIZATION_STARTED
+ val isInSetup =
+ Settings.Secure.getIntForUser(
+ contentResolver,
+ Settings.Secure.USER_SETUP_COMPLETE,
+ userId
+ ) == 0
+ val isInDeferredSetup =
+ Settings.Secure.getIntForUser(
+ contentResolver,
+ Settings.Secure.USER_SETUP_PERSONALIZATION_STATE,
+ userId
+ ) == Settings.Secure.USER_SETUP_PERSONALIZATION_STARTED
isInSetup || isInDeferredSetup
} catch (e: Settings.SettingNotFoundException) {
Slog.w(LOG_TAG, "Failed to check if the user is in restore: $e")
@@ -2614,31 +2794,34 @@
@EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q)
private val BACKGROUND_RATIONALE_CHANGE_ID = 147316723L
- private val FULLER_PERMISSIONS = ArrayMap<String, String>().apply {
- this[Manifest.permission.ACCESS_COARSE_LOCATION] =
- Manifest.permission.ACCESS_FINE_LOCATION
- this[Manifest.permission.INTERACT_ACROSS_USERS] =
- Manifest.permission.INTERACT_ACROSS_USERS_FULL
- }
+ private val FULLER_PERMISSIONS =
+ ArrayMap<String, String>().apply {
+ this[Manifest.permission.ACCESS_COARSE_LOCATION] =
+ Manifest.permission.ACCESS_FINE_LOCATION
+ this[Manifest.permission.INTERACT_ACROSS_USERS] =
+ Manifest.permission.INTERACT_ACROSS_USERS_FULL
+ }
- private val NOTIFICATIONS_PERMISSIONS = arraySetOf(
- Manifest.permission.POST_NOTIFICATIONS
- )
+ private val NOTIFICATIONS_PERMISSIONS = arraySetOf(Manifest.permission.POST_NOTIFICATIONS)
- private const val REVIEW_REQUIRED_FLAGS = PermissionFlags.LEGACY_GRANTED or
- PermissionFlags.IMPLICIT
- private const val UNREQUESTABLE_MASK = PermissionFlags.RESTRICTION_REVOKED or
- PermissionFlags.SYSTEM_FIXED or PermissionFlags.POLICY_FIXED or
- PermissionFlags.USER_FIXED
+ private const val REVIEW_REQUIRED_FLAGS =
+ PermissionFlags.LEGACY_GRANTED or PermissionFlags.IMPLICIT
+ private const val UNREQUESTABLE_MASK =
+ PermissionFlags.RESTRICTION_REVOKED or
+ PermissionFlags.SYSTEM_FIXED or
+ PermissionFlags.POLICY_FIXED or
+ PermissionFlags.USER_FIXED
private val BACKUP_TIMEOUT_MILLIS = TimeUnit.SECONDS.toMillis(60)
- /** Cap the size of permission trees that 3rd party apps can define; in characters of text */
+ /**
+ * Cap the size of permission trees that 3rd party apps can define; in characters of text
+ */
private const val MAX_PERMISSION_TREE_FOOTPRINT = 32768
private const val PERMISSION_ALLOWLIST_MASK =
PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE or
- PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM or
- PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER
+ PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM or
+ PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER
}
}
diff --git a/services/permission/java/com/android/server/permission/access/util/AtomicFileExtensions.kt b/services/permission/java/com/android/server/permission/access/util/AtomicFileExtensions.kt
index bd82935..6b20ef1 100644
--- a/services/permission/java/com/android/server/permission/access/util/AtomicFileExtensions.kt
+++ b/services/permission/java/com/android/server/permission/access/util/AtomicFileExtensions.kt
@@ -25,9 +25,7 @@
import java.io.FileOutputStream
import java.io.IOException
-/**
- * Read from an [AtomicFile], fallback to reserve file to read the data.
- */
+/** Read from an [AtomicFile], fallback to reserve file to read the data. */
@Throws(Exception::class)
inline fun AtomicFile.readWithReserveCopy(block: (FileInputStream) -> Unit) {
try {
@@ -46,9 +44,7 @@
}
}
-/**
- * Write to actual file and reserve file.
- */
+/** Write to actual file and reserve file. */
@Throws(IOException::class)
inline fun AtomicFile.writeWithReserveCopy(block: (FileOutputStream) -> Unit) {
val reserveFile = File(baseFile.parentFile, baseFile.name + ".reservecopy")
@@ -66,9 +62,7 @@
}
}
-/**
- * Write to an [AtomicFile] and close everything safely when done.
- */
+/** Write to an [AtomicFile] and close everything safely when done. */
@Throws(IOException::class)
// Renamed to writeInlined() to avoid conflict with the hidden AtomicFile.write() that isn't inline.
inline fun AtomicFile.writeInlined(block: (FileOutputStream) -> Unit) {
diff --git a/services/permission/java/com/android/server/permission/access/util/BinaryXmlPullParserExtensions.kt b/services/permission/java/com/android/server/permission/access/util/BinaryXmlPullParserExtensions.kt
index 1d27aef..6ab73c7 100644
--- a/services/permission/java/com/android/server/permission/access/util/BinaryXmlPullParserExtensions.kt
+++ b/services/permission/java/com/android/server/permission/access/util/BinaryXmlPullParserExtensions.kt
@@ -22,9 +22,7 @@
import org.xmlpull.v1.XmlPullParser
import org.xmlpull.v1.XmlPullParserException
-/**
- * Parse content from [InputStream] with [BinaryXmlPullParser].
- */
+/** Parse content from [InputStream] with [BinaryXmlPullParser]. */
@Throws(IOException::class, XmlPullParserException::class)
inline fun InputStream.parseBinaryXml(block: BinaryXmlPullParser.() -> Unit) {
BinaryXmlPullParser().apply {
@@ -35,6 +33,7 @@
/**
* Iterate through child tags of the current tag.
+ *
* <p>
* Attributes for the current tag needs to be accessed before this method is called because this
* method will advance the parser past the start tag of the current tag. The code inspecting each
@@ -50,7 +49,8 @@
inline fun BinaryXmlPullParser.forEachTag(block: BinaryXmlPullParser.() -> Unit) {
when (val eventType = eventType) {
// Document start or start tag of the parent tag.
- XmlPullParser.START_DOCUMENT, XmlPullParser.START_TAG -> nextTagOrEnd()
+ XmlPullParser.START_DOCUMENT,
+ XmlPullParser.START_TAG -> nextTagOrEnd()
else -> throw XmlPullParserException("Unexpected event type $eventType")
}
while (true) {
@@ -90,7 +90,8 @@
nextTagOrEnd()
}
// End tag of the parent tag, or document end.
- XmlPullParser.END_TAG, XmlPullParser.END_DOCUMENT -> break
+ XmlPullParser.END_TAG,
+ XmlPullParser.END_DOCUMENT -> break
else -> throw XmlPullParserException("Unexpected event type $eventType")
}
}
@@ -107,193 +108,146 @@
inline fun BinaryXmlPullParser.nextTagOrEnd(): Int {
while (true) {
when (val eventType = next()) {
- XmlPullParser.START_TAG, XmlPullParser.END_TAG, XmlPullParser.END_DOCUMENT ->
- return eventType
+ XmlPullParser.START_TAG,
+ XmlPullParser.END_TAG,
+ XmlPullParser.END_DOCUMENT -> return eventType
else -> continue
}
}
}
-/**
- * @see BinaryXmlPullParser.getName
- */
+/** @see BinaryXmlPullParser.getName */
inline val BinaryXmlPullParser.tagName: String
get() = name
-/**
- * Check whether an attribute exists for the current tag.
- */
+/** Check whether an attribute exists for the current tag. */
@Suppress("NOTHING_TO_INLINE")
inline fun BinaryXmlPullParser.hasAttribute(name: String): Boolean = getAttributeIndex(name) != -1
-/**
- * @see BinaryXmlPullParser.getAttributeIndex
- */
+/** @see BinaryXmlPullParser.getAttributeIndex */
@Suppress("NOTHING_TO_INLINE")
inline fun BinaryXmlPullParser.getAttributeIndex(name: String): Int = getAttributeIndex(null, name)
-/**
- * @see BinaryXmlPullParser.getAttributeIndexOrThrow
- */
+/** @see BinaryXmlPullParser.getAttributeIndexOrThrow */
@Suppress("NOTHING_TO_INLINE")
@Throws(XmlPullParserException::class)
inline fun BinaryXmlPullParser.getAttributeIndexOrThrow(name: String): Int =
getAttributeIndexOrThrow(null, name)
-/**
- * @see BinaryXmlPullParser.getAttributeValue
- */
+/** @see BinaryXmlPullParser.getAttributeValue */
@Suppress("NOTHING_TO_INLINE")
@Throws(XmlPullParserException::class)
inline fun BinaryXmlPullParser.getAttributeValue(name: String): String? =
getAttributeValue(null, name)
-/**
- * @see BinaryXmlPullParser.getAttributeValue
- */
+/** @see BinaryXmlPullParser.getAttributeValue */
@Suppress("NOTHING_TO_INLINE")
@Throws(XmlPullParserException::class)
inline fun BinaryXmlPullParser.getAttributeValueOrThrow(name: String): String =
getAttributeValue(getAttributeIndexOrThrow(name))
-/**
- * @see BinaryXmlPullParser.getAttributeBytesHex
- */
+/** @see BinaryXmlPullParser.getAttributeBytesHex */
@Suppress("NOTHING_TO_INLINE")
inline fun BinaryXmlPullParser.getAttributeBytesHex(name: String): ByteArray? =
getAttributeBytesHex(null, name, null)
-/**
- * @see BinaryXmlPullParser.getAttributeBytesHex
- */
+/** @see BinaryXmlPullParser.getAttributeBytesHex */
@Suppress("NOTHING_TO_INLINE")
@Throws(XmlPullParserException::class)
inline fun BinaryXmlPullParser.getAttributeBytesHexOrThrow(name: String): ByteArray =
getAttributeBytesHex(null, name)
-/**
- * @see BinaryXmlPullParser.getAttributeBytesBase64
- */
+/** @see BinaryXmlPullParser.getAttributeBytesBase64 */
@Suppress("NOTHING_TO_INLINE")
inline fun BinaryXmlPullParser.getAttributeBytesBase64(name: String): ByteArray? =
getAttributeBytesBase64(null, name, null)
-/**
- * @see BinaryXmlPullParser.getAttributeBytesBase64
- */
+/** @see BinaryXmlPullParser.getAttributeBytesBase64 */
@Suppress("NOTHING_TO_INLINE")
@Throws(XmlPullParserException::class)
inline fun BinaryXmlPullParser.getAttributeBytesBase64OrThrow(name: String): ByteArray =
getAttributeBytesBase64(null, name)
-/**
- * @see BinaryXmlPullParser.getAttributeInt
- */
+/** @see BinaryXmlPullParser.getAttributeInt */
@Suppress("NOTHING_TO_INLINE")
inline fun BinaryXmlPullParser.getAttributeIntOrDefault(name: String, defaultValue: Int): Int =
getAttributeInt(null, name, defaultValue)
-/**
- * @see BinaryXmlPullParser.getAttributeInt
- */
+/** @see BinaryXmlPullParser.getAttributeInt */
@Suppress("NOTHING_TO_INLINE")
@Throws(XmlPullParserException::class)
inline fun BinaryXmlPullParser.getAttributeIntOrThrow(name: String): Int =
getAttributeInt(null, name)
-/**
- * @see BinaryXmlPullParser.getAttributeIntHex
- */
+/** @see BinaryXmlPullParser.getAttributeIntHex */
@Suppress("NOTHING_TO_INLINE")
inline fun BinaryXmlPullParser.getAttributeIntHexOrDefault(name: String, defaultValue: Int): Int =
getAttributeIntHex(null, name, defaultValue)
-/**
- * @see BinaryXmlPullParser.getAttributeIntHex
- */
+/** @see BinaryXmlPullParser.getAttributeIntHex */
@Suppress("NOTHING_TO_INLINE")
@Throws(XmlPullParserException::class)
inline fun BinaryXmlPullParser.getAttributeIntHexOrThrow(name: String): Int =
getAttributeIntHex(null, name)
-/**
- * @see BinaryXmlPullParser.getAttributeLong
- */
+/** @see BinaryXmlPullParser.getAttributeLong */
@Suppress("NOTHING_TO_INLINE")
inline fun BinaryXmlPullParser.getAttributeLongOrDefault(name: String, defaultValue: Long): Long =
getAttributeLong(null, name, defaultValue)
-/**
- * @see BinaryXmlPullParser.getAttributeLong
- */
+/** @see BinaryXmlPullParser.getAttributeLong */
@Suppress("NOTHING_TO_INLINE")
@Throws(XmlPullParserException::class)
inline fun BinaryXmlPullParser.getAttributeLongOrThrow(name: String): Long =
getAttributeLong(null, name)
-/**
- * @see BinaryXmlPullParser.getAttributeLongHex
- */
+/** @see BinaryXmlPullParser.getAttributeLongHex */
@Suppress("NOTHING_TO_INLINE")
inline fun BinaryXmlPullParser.getAttributeLongHexOrDefault(
name: String,
defaultValue: Long
): Long = getAttributeLongHex(null, name, defaultValue)
-/**
- * @see BinaryXmlPullParser.getAttributeLongHex
- */
+/** @see BinaryXmlPullParser.getAttributeLongHex */
@Suppress("NOTHING_TO_INLINE")
@Throws(XmlPullParserException::class)
inline fun BinaryXmlPullParser.getAttributeLongHexOrThrow(name: String): Long =
getAttributeLongHex(null, name)
-/**
- * @see BinaryXmlPullParser.getAttributeFloat
- */
+/** @see BinaryXmlPullParser.getAttributeFloat */
@Suppress("NOTHING_TO_INLINE")
inline fun BinaryXmlPullParser.getAttributeFloatOrDefault(
name: String,
defaultValue: Float
): Float = getAttributeFloat(null, name, defaultValue)
-/**
- * @see BinaryXmlPullParser.getAttributeFloat
- */
+/** @see BinaryXmlPullParser.getAttributeFloat */
@Suppress("NOTHING_TO_INLINE")
@Throws(XmlPullParserException::class)
inline fun BinaryXmlPullParser.getAttributeFloatOrThrow(name: String): Float =
getAttributeFloat(null, name)
-/**
- * @see BinaryXmlPullParser.getAttributeDouble
- */
+/** @see BinaryXmlPullParser.getAttributeDouble */
@Suppress("NOTHING_TO_INLINE")
inline fun BinaryXmlPullParser.getAttributeDoubleOrDefault(
name: String,
defaultValue: Double
): Double = getAttributeDouble(null, name, defaultValue)
-/**
- * @see BinaryXmlPullParser.getAttributeDouble
- */
+/** @see BinaryXmlPullParser.getAttributeDouble */
@Suppress("NOTHING_TO_INLINE")
@Throws(XmlPullParserException::class)
inline fun BinaryXmlPullParser.getAttributeDoubleOrThrow(name: String): Double =
getAttributeDouble(null, name)
-/**
- * @see BinaryXmlPullParser.getAttributeBoolean
- */
+/** @see BinaryXmlPullParser.getAttributeBoolean */
@Suppress("NOTHING_TO_INLINE")
inline fun BinaryXmlPullParser.getAttributeBooleanOrDefault(
name: String,
defaultValue: Boolean
): Boolean = getAttributeBoolean(null, name, defaultValue)
-/**
- * @see BinaryXmlPullParser.getAttributeBoolean
- */
+/** @see BinaryXmlPullParser.getAttributeBoolean */
@Suppress("NOTHING_TO_INLINE")
@Throws(XmlPullParserException::class)
inline fun BinaryXmlPullParser.getAttributeBooleanOrThrow(name: String): Boolean =
diff --git a/services/permission/java/com/android/server/permission/access/util/BinaryXmlSerializerExtensions.kt b/services/permission/java/com/android/server/permission/access/util/BinaryXmlSerializerExtensions.kt
index c8cd586..6500a7d 100644
--- a/services/permission/java/com/android/server/permission/access/util/BinaryXmlSerializerExtensions.kt
+++ b/services/permission/java/com/android/server/permission/access/util/BinaryXmlSerializerExtensions.kt
@@ -20,9 +20,7 @@
import java.io.IOException
import java.io.OutputStream
-/**
- * Serialize content into [OutputStream] with [BinaryXmlSerializer].
- */
+/** Serialize content into [OutputStream] with [BinaryXmlSerializer]. */
@Throws(IOException::class)
inline fun OutputStream.serializeBinaryXml(block: BinaryXmlSerializer.() -> Unit) {
BinaryXmlSerializer().apply {
@@ -57,54 +55,42 @@
endTag(null, name)
}
-/**
- * @see BinaryXmlSerializer.attribute
- */
+/** @see BinaryXmlSerializer.attribute */
@Suppress("NOTHING_TO_INLINE")
@Throws(IOException::class)
inline fun BinaryXmlSerializer.attribute(name: String, value: String) {
attribute(null, name, value)
}
-/**
- * @see BinaryXmlSerializer.attributeInterned
- */
+/** @see BinaryXmlSerializer.attributeInterned */
@Suppress("NOTHING_TO_INLINE")
@Throws(IOException::class)
inline fun BinaryXmlSerializer.attributeInterned(name: String, value: String) {
attributeInterned(null, name, value)
}
-/**
- * @see BinaryXmlSerializer.attributeBytesHex
- */
+/** @see BinaryXmlSerializer.attributeBytesHex */
@Suppress("NOTHING_TO_INLINE")
@Throws(IOException::class)
inline fun BinaryXmlSerializer.attributeBytesHex(name: String, value: ByteArray) {
attributeBytesHex(null, name, value)
}
-/**
- * @see BinaryXmlSerializer.attributeBytesBase64
- */
+/** @see BinaryXmlSerializer.attributeBytesBase64 */
@Suppress("NOTHING_TO_INLINE")
@Throws(IOException::class)
inline fun BinaryXmlSerializer.attributeBytesBase64(name: String, value: ByteArray) {
attributeBytesBase64(null, name, value)
}
-/**
- * @see BinaryXmlSerializer.attributeInt
- */
+/** @see BinaryXmlSerializer.attributeInt */
@Suppress("NOTHING_TO_INLINE")
@Throws(IOException::class)
inline fun BinaryXmlSerializer.attributeInt(name: String, value: Int) {
attributeInt(null, name, value)
}
-/**
- * @see BinaryXmlSerializer.attributeInt
- */
+/** @see BinaryXmlSerializer.attributeInt */
@Suppress("NOTHING_TO_INLINE")
@Throws(IOException::class)
inline fun BinaryXmlSerializer.attributeIntWithDefault(
@@ -117,18 +103,14 @@
}
}
-/**
- * @see BinaryXmlSerializer.attributeIntHex
- */
+/** @see BinaryXmlSerializer.attributeIntHex */
@Suppress("NOTHING_TO_INLINE")
@Throws(IOException::class)
inline fun BinaryXmlSerializer.attributeIntHex(name: String, value: Int) {
attributeIntHex(null, name, value)
}
-/**
- * @see BinaryXmlSerializer.attributeIntHex
- */
+/** @see BinaryXmlSerializer.attributeIntHex */
@Suppress("NOTHING_TO_INLINE")
@Throws(IOException::class)
inline fun BinaryXmlSerializer.attributeIntHexWithDefault(
@@ -141,18 +123,14 @@
}
}
-/**
- * @see BinaryXmlSerializer.attributeLong
- */
+/** @see BinaryXmlSerializer.attributeLong */
@Suppress("NOTHING_TO_INLINE")
@Throws(IOException::class)
inline fun BinaryXmlSerializer.attributeLong(name: String, value: Long) {
attributeLong(null, name, value)
}
-/**
- * @see BinaryXmlSerializer.attributeLong
- */
+/** @see BinaryXmlSerializer.attributeLong */
@Suppress("NOTHING_TO_INLINE")
@Throws(IOException::class)
inline fun BinaryXmlSerializer.attributeLongWithDefault(
@@ -165,18 +143,14 @@
}
}
-/**
- * @see BinaryXmlSerializer.attributeLongHex
- */
+/** @see BinaryXmlSerializer.attributeLongHex */
@Suppress("NOTHING_TO_INLINE")
@Throws(IOException::class)
inline fun BinaryXmlSerializer.attributeLongHex(name: String, value: Long) {
attributeLongHex(null, name, value)
}
-/**
- * @see BinaryXmlSerializer.attributeLongHex
- */
+/** @see BinaryXmlSerializer.attributeLongHex */
@Suppress("NOTHING_TO_INLINE")
@Throws(IOException::class)
inline fun BinaryXmlSerializer.attributeLongHexWithDefault(
@@ -189,18 +163,14 @@
}
}
-/**
- * @see BinaryXmlSerializer.attributeFloat
- */
+/** @see BinaryXmlSerializer.attributeFloat */
@Suppress("NOTHING_TO_INLINE")
@Throws(IOException::class)
inline fun BinaryXmlSerializer.attributeFloat(name: String, value: Float) {
attributeFloat(null, name, value)
}
-/**
- * @see BinaryXmlSerializer.attributeFloat
- */
+/** @see BinaryXmlSerializer.attributeFloat */
@Suppress("NOTHING_TO_INLINE")
@Throws(IOException::class)
inline fun BinaryXmlSerializer.attributeFloatWithDefault(
@@ -213,18 +183,14 @@
}
}
-/**
- * @see BinaryXmlSerializer.attributeDouble
- */
+/** @see BinaryXmlSerializer.attributeDouble */
@Suppress("NOTHING_TO_INLINE")
@Throws(IOException::class)
inline fun BinaryXmlSerializer.attributeDouble(name: String, value: Double) {
attributeDouble(null, name, value)
}
-/**
- * @see BinaryXmlSerializer.attributeDouble
- */
+/** @see BinaryXmlSerializer.attributeDouble */
@Suppress("NOTHING_TO_INLINE")
@Throws(IOException::class)
inline fun BinaryXmlSerializer.attributeDoubleWithDefault(
@@ -237,18 +203,14 @@
}
}
-/**
- * @see BinaryXmlSerializer.attributeBoolean
- */
+/** @see BinaryXmlSerializer.attributeBoolean */
@Suppress("NOTHING_TO_INLINE")
@Throws(IOException::class)
inline fun BinaryXmlSerializer.attributeBoolean(name: String, value: Boolean) {
attributeBoolean(null, name, value)
}
-/**
- * @see BinaryXmlSerializer.attributeBoolean
- */
+/** @see BinaryXmlSerializer.attributeBoolean */
@Suppress("NOTHING_TO_INLINE")
@Throws(IOException::class)
inline fun BinaryXmlSerializer.attributeBooleanWithDefault(
diff --git a/services/permission/java/com/android/server/permission/access/util/PackageVersionMigration.kt b/services/permission/java/com/android/server/permission/access/util/PackageVersionMigration.kt
index a61489c..3ef284b 100644
--- a/services/permission/java/com/android/server/permission/access/util/PackageVersionMigration.kt
+++ b/services/permission/java/com/android/server/permission/access/util/PackageVersionMigration.kt
@@ -22,13 +22,13 @@
object PackageVersionMigration {
/**
- * Maps existing permission and app-op version to a unified version during OTA upgrade. The
- * new unified version is used in determining the upgrade steps for a package (for both
- * permission and app-ops).
+ * Maps existing permission and app-op version to a unified version during OTA upgrade. The new
+ * unified version is used in determining the upgrade steps for a package (for both permission
+ * and app-ops).
*
* @return unified permission/app-op version
* @throws IllegalStateException if the method is called when there is nothing to migrate i.e.
- * permission and app-op file does not exist.
+ * permission and app-op file does not exist.
*/
internal fun getVersion(userId: Int): Int {
val permissionMigrationHelper =
diff --git a/services/permission/java/com/android/server/permission/access/util/PermissionApex.kt b/services/permission/java/com/android/server/permission/access/util/PermissionApex.kt
index e6b4e3e..3d835e8 100644
--- a/services/permission/java/com/android/server/permission/access/util/PermissionApex.kt
+++ b/services/permission/java/com/android/server/permission/access/util/PermissionApex.kt
@@ -23,15 +23,11 @@
object PermissionApex {
private const val MODULE_NAME = "com.android.permission"
- /**
- * @see ApexEnvironment.getDeviceProtectedDataDir
- */
+ /** @see ApexEnvironment.getDeviceProtectedDataDir */
val systemDataDirectory: File
get() = apexEnvironment.deviceProtectedDataDir
- /**
- * @see ApexEnvironment.getDeviceProtectedDataDirForUser
- */
+ /** @see ApexEnvironment.getDeviceProtectedDataDirForUser */
fun getUserDataDirectory(userId: Int): File =
apexEnvironment.getDeviceProtectedDataDirForUser(UserHandle.of(userId))
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerSettingsTests.java
index 2889c74..952cfc4 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerSettingsTests.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerSettingsTests.java
@@ -24,7 +24,9 @@
import static android.content.pm.SuspendDialogInfo.BUTTON_ACTION_UNSUSPEND;
import static android.content.pm.parsing.FrameworkParsingPackageUtils.parsePublicKey;
import static android.content.res.Resources.ID_NULL;
+
import static com.android.server.pm.PackageManagerService.WRITE_USER_PACKAGE_RESTRICTIONS;
+
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.not;
@@ -1065,7 +1067,10 @@
null /*usesStaticLibraries*/,
null /*usesStaticLibrariesVersions*/,
null /*mimeGroups*/,
- UUID.randomUUID());
+ UUID.randomUUID(),
+ false /*isPersistent*/,
+ 34 /*targetSdkVersion*/,
+ null /*restrictUpdateHash*/);
assertThat(testPkgSetting01.getPrimaryCpuAbi(), is("arm64-v8a"));
assertThat(testPkgSetting01.getPrimaryCpuAbiLegacy(), is("arm64-v8a"));
assertThat(testPkgSetting01.getSecondaryCpuAbi(), is("armeabi"));
@@ -1103,7 +1108,10 @@
null /*usesStaticLibraries*/,
null /*usesStaticLibrariesVersions*/,
null /*mimeGroups*/,
- UUID.randomUUID());
+ UUID.randomUUID(),
+ false /*isPersistent*/,
+ 34 /*targetSdkVersion*/,
+ null /*restrictUpdateHash*/);
assertThat(testPkgSetting01.getPrimaryCpuAbi(), is("arm64-v8a"));
assertThat(testPkgSetting01.getPrimaryCpuAbiLegacy(), is("arm64-v8a"));
assertThat(testPkgSetting01.getSecondaryCpuAbi(), is("armeabi"));
@@ -1143,7 +1151,10 @@
null /*usesStaticLibraries*/,
null /*usesStaticLibrariesVersions*/,
null /*mimeGroups*/,
- UUID.randomUUID());
+ UUID.randomUUID(),
+ false /*isPersistent*/,
+ 34 /*targetSdkVersion*/,
+ null /*restrictUpdateHash*/);
fail("Expected a PackageManagerException");
} catch (PackageManagerException expected) {
}
@@ -1179,7 +1190,10 @@
null /*usesStaticLibraries*/,
null /*usesStaticLibrariesVersions*/,
null /*mimeGroups*/,
- UUID.randomUUID());
+ UUID.randomUUID(),
+ false /*isPersistent*/,
+ 34 /*targetSdkVersion*/,
+ null /*restrictUpdateHash*/);
assertThat(testPkgSetting01.getPath(), is(UPDATED_CODE_PATH));
assertThat(testPkgSetting01.getPackageName(), is(PACKAGE_NAME));
assertThat(testPkgSetting01.getFlags(), is(ApplicationInfo.FLAG_SYSTEM));
@@ -1224,7 +1238,10 @@
null /*usesStaticLibraries*/,
null /*usesStaticLibrariesVersions*/,
null /*mimeGroups*/,
- UUID.randomUUID());
+ UUID.randomUUID(),
+ false /*isPersistent*/,
+ 34 /*targetSdkVersion*/,
+ null /*restrictUpdateHash*/);
assertThat(testPkgSetting01.getAppId(), is(0));
assertThat(testPkgSetting01.getPath(), is(INITIAL_CODE_PATH));
assertThat(testPkgSetting01.getPackageName(), is(PACKAGE_NAME));
@@ -1269,7 +1286,10 @@
null /*usesStaticLibraries*/,
null /*usesStaticLibrariesVersions*/,
null /*mimeGroups*/,
- UUID.randomUUID());
+ UUID.randomUUID(),
+ false /*isPersistent*/,
+ 34 /*targetSdkVersion*/,
+ null /*restrictUpdateHash*/);
assertThat(testPkgSetting01.getAppId(), is(10064));
assertThat(testPkgSetting01.getPath(), is(INITIAL_CODE_PATH));
assertThat(testPkgSetting01.getPackageName(), is(PACKAGE_NAME));
@@ -1315,7 +1335,10 @@
null /*usesStaticLibraries*/,
null /*usesStaticLibrariesVersions*/,
null /*mimeGroups*/,
- UUID.randomUUID());
+ UUID.randomUUID(),
+ false /*isPersistent*/,
+ 34 /*targetSdkVersion*/,
+ null /*restrictUpdateHash*/);
assertThat(testPkgSetting01.getAppId(), is(10064));
assertThat(testPkgSetting01.getPath(), is(UPDATED_CODE_PATH));
assertThat(testPkgSetting01.getPackageName(), is(PACKAGE_NAME));
@@ -1358,7 +1381,10 @@
null /*usesStaticLibraries*/,
null /*usesStaticLibrariesVersions*/,
null /*mimeGroups*/,
- UUID.randomUUID());
+ UUID.randomUUID(),
+ false /*isPersistent*/,
+ 34 /*targetSdkVersion*/,
+ null /*restrictUpdateHash*/);
assertThat(testPkgSetting01.getAppId(), is(0));
assertThat(testPkgSetting01.getPath(), is(UPDATED_CODE_PATH));
assertThat(testPkgSetting01.getPackageName(), is(PACKAGE_NAME));
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/AndroidManifest.xml b/services/tests/displayservicetests/AndroidManifest.xml
index 55fde00..e71ea26 100644
--- a/services/tests/displayservicetests/AndroidManifest.xml
+++ b/services/tests/displayservicetests/AndroidManifest.xml
@@ -28,6 +28,7 @@
<uses-permission android:name="android.permission.READ_COMPAT_CHANGE_CONFIG" />
<uses-permission android:name="android.permission.READ_DEVICE_CONFIG" />
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
+ <uses-permission android:name="android.permission.MANAGE_USB" />
<!-- Permissions needed for DisplayTransformManagerTest -->
<uses-permission android:name="android.permission.CHANGE_CONFIGURATION" />
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DeviceStateToLayoutMapTest.java b/services/tests/displayservicetests/src/com/android/server/display/DeviceStateToLayoutMapTest.java
index 4b124ca..8cc3408 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DeviceStateToLayoutMapTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DeviceStateToLayoutMapTest.java
@@ -100,6 +100,14 @@
}
@Test
+ public void testPowerThrottlingMapId() {
+ Layout configLayout = mDeviceStateToLayoutMap.get(5);
+
+ assertEquals("concurrent1", configLayout.getAt(0).getPowerThrottlingMapId());
+ assertEquals("concurrent2", configLayout.getAt(1).getPowerThrottlingMapId());
+ }
+
+ @Test
public void testRearDisplayLayout() {
Layout configLayout = mDeviceStateToLayoutMap.get(2);
@@ -133,13 +141,15 @@
mDisplayIdProducerMock, Layout.Display.POSITION_FRONT,
/* leadDisplayAddress= */ null, /* brightnessThrottlingMapId= */ "brightness1",
/* refreshRateZoneId= */ "zone1",
- /* refreshRateThermalThrottlingMapId= */ "rr1");
+ /* refreshRateThermalThrottlingMapId= */ "rr1",
+ /* powerThrottlingMapId= */ "power1");
testLayout.createDisplayLocked(DisplayAddress.fromPhysicalDisplayId(678L),
/* isDefault= */ false, /* isEnabled= */ false, /* displayGroupName= */ "group1",
mDisplayIdProducerMock, Layout.Display.POSITION_REAR,
/* leadDisplayAddress= */ null, /* brightnessThrottlingMapId= */ "brightness2",
/* refreshRateZoneId= */ "zone2",
- /* refreshRateThermalThrottlingMapId= */ "rr2");
+ /* refreshRateThermalThrottlingMapId= */ "rr2",
+ /* powerThrottlingMapId= */ "power2");
testLayout.postProcessLocked();
assertEquals(testLayout, configLayout);
@@ -200,7 +210,8 @@
mDisplayIdProducerMock, Layout.Display.POSITION_FRONT,
DisplayAddress.fromPhysicalDisplayId(123L),
/* brightnessThrottlingMapId= */ null, /* refreshRateZoneId= */ null,
- /* refreshRateThermalThrottlingMapId= */ null));
+ /* refreshRateThermalThrottlingMapId= */ null,
+ /* powerThrottlingMapId= */ null));
}
@Test
@@ -215,7 +226,8 @@
mDisplayIdProducerMock, Layout.Display.POSITION_FRONT,
DisplayAddress.fromPhysicalDisplayId(987L),
/* brightnessThrottlingMapId= */ null, /* refreshRateZoneId= */ null,
- /* refreshRateThermalThrottlingMapId= */ null));
+ /* refreshRateThermalThrottlingMapId= */ null,
+ /* powerThrottlingMapId= */ null));
}
@Test
@@ -271,7 +283,8 @@
enabled, group, mDisplayIdProducerMock, Layout.Display.POSITION_UNKNOWN,
leadDisplayAddress, /* brightnessThrottlingMapId= */ null,
/* refreshRateZoneId= */ null,
- /* refreshRateThermalThrottlingMapId= */ null);
+ /* refreshRateThermalThrottlingMapId= */ null,
+ /* powerThrottlingMapId= */ null);
}
private void setupDeviceStateToLayoutMap() throws IOException {
@@ -327,7 +340,6 @@
+ "<brightnessThrottlingMapId>concurrent2</brightnessThrottlingMapId>\n"
+ "</display>\n"
+ "</layout>\n"
-
+ "<layout>\n"
+ "<state>3</state> \n"
+ "<display enabled=\"true\" defaultDisplay=\"true\" "
@@ -338,7 +350,6 @@
+ "<address>678</address>\n"
+ "</display>\n"
+ "</layout>\n"
-
+ "<layout>\n"
+ "<state>4</state> \n"
+ "<display enabled=\"true\" defaultDisplay=\"true\" >\n"
@@ -352,6 +363,20 @@
+ "</display>\n"
+ "</layout>\n"
+ "<layout>\n"
+ + "<state>5</state> \n"
+ + "<display enabled=\"true\" defaultDisplay=\"true\">\n"
+ + "<address>345</address>\n"
+ + "<position>front</position>\n"
+ + "<powerThrottlingMapId>concurrent1</powerThrottlingMapId>\n"
+ + "</display>\n"
+ + "<display enabled=\"true\">\n"
+ + "<address>678</address>\n"
+ + "<position>rear</position>\n"
+ + "<powerThrottlingMapId>concurrent2</powerThrottlingMapId>\n"
+ + "</display>\n"
+ + "</layout>\n"
+
+ + "<layout>\n"
+ "<state>99</state> \n"
+ "<display enabled=\"true\" defaultDisplay=\"true\" "
+ "refreshRateZoneId=\"zone1\">\n"
@@ -361,6 +386,7 @@
+ "<refreshRateThermalThrottlingMapId>"
+ "rr1"
+ "</refreshRateThermalThrottlingMapId>"
+ + "<powerThrottlingMapId>power1</powerThrottlingMapId>\n"
+ "</display>\n"
+ "<display enabled=\"false\" displayGroup=\"group1\" "
+ "refreshRateZoneId=\"zone2\">\n"
@@ -370,6 +396,7 @@
+ "<refreshRateThermalThrottlingMapId>"
+ "rr2"
+ "</refreshRateThermalThrottlingMapId>"
+ + "<powerThrottlingMapId>power2</powerThrottlingMapId>\n"
+ "</display>\n"
+ "</layout>\n"
+ "</layouts>\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..16d72e4 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,17 @@
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 +201,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 +251,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 +293,7 @@
private final DisplayManagerService.Injector mBasicInjector = new BasicInjector();
+ @Mock DisplayNotificationManager mMockedDisplayNotificationManager;
@Mock IMediaProjectionManager mMockProjectionService;
@Mock IVirtualDeviceManager mIVirtualDeviceManager;
@Mock InputManagerInternal mMockInputManagerInternal;
@@ -305,7 +311,6 @@
@Mock SensorManager mSensorManager;
@Mock DisplayDeviceConfig mMockDisplayDeviceConfig;
@Mock PackageManagerInternal mMockPackageManagerInternal;
- @Mock UserManagerInternal mMockUserManagerInternal;
@Captor ArgumentCaptor<ContentRecordingSession> mContentRecordingSessionCaptor;
@@ -329,8 +334,6 @@
VirtualDeviceManagerInternal.class, mMockVirtualDeviceManagerInternal);
LocalServices.removeServiceForTest(PackageManagerInternal.class);
LocalServices.addService(PackageManagerInternal.class, mMockPackageManagerInternal);
- LocalServices.removeServiceForTest(UserManagerInternal.class);
- LocalServices.addService(UserManagerInternal.class, mMockUserManagerInternal);
// TODO: b/287945043
mContext = spy(new ContextWrapper(ApplicationProvider.getApplicationContext()));
mResources = Mockito.spy(mContext.getResources());
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..32e2871 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java
@@ -33,6 +33,7 @@
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.when;
import android.content.Context;
@@ -60,6 +61,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 +115,8 @@
@Mock
private LogicalLight mMockedBacklight;
@Mock
+ private DisplayNotificationManager mMockedDisplayNotificationManager;
+ @Mock
private DisplayManagerFlags mFlags;
private Handler mHandler;
@@ -148,7 +152,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();
@@ -696,7 +700,7 @@
// Turn off.
Runnable changeStateRunnable = displayDevice.requestDisplayStateLocked(Display.STATE_OFF, 0,
- 0);
+ 0, null);
waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
assertThat(mListener.changedDisplays.size()).isEqualTo(1);
mListener.changedDisplays.clear();
@@ -1000,7 +1004,7 @@
// Turn on / initialize
assumeTrue(displayDevice.getDisplayDeviceConfig().hasSdrToHdrRatioSpline());
Runnable changeStateRunnable = displayDevice.requestDisplayStateLocked(Display.STATE_ON, 0,
- 0);
+ 0, null);
changeStateRunnable.run();
waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
mListener.changedDisplays.clear();
@@ -1009,7 +1013,7 @@
// HDR time!
Runnable goHdrRunnable = displayDevice.requestDisplayStateLocked(Display.STATE_ON, 1f,
- 0);
+ 0, null);
waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
// Display state didn't change, no listeners should have happened
assertThat(mListener.changedDisplays.size()).isEqualTo(0);
@@ -1040,7 +1044,7 @@
// Turn on / initialize
Runnable changeStateRunnable = displayDevice.requestDisplayStateLocked(Display.STATE_ON, 0,
- 0);
+ 0, null);
changeStateRunnable.run();
waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
mListener.changedDisplays.clear();
@@ -1067,7 +1071,7 @@
// Turn on / initialize
Runnable changeStateRunnable = displayDevice.requestDisplayStateLocked(Display.STATE_ON, 0,
- 0);
+ 0, null);
changeStateRunnable.run();
waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
mListener.changedDisplays.clear();
@@ -1092,7 +1096,7 @@
// Turn on / initialize
Runnable changeStateRunnable = displayDevice.requestDisplayStateLocked(Display.STATE_ON, 0,
- 0);
+ 0, null);
changeStateRunnable.run();
waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
mListener.changedDisplays.clear();
@@ -1115,7 +1119,7 @@
// Turn on / initialize
Runnable changeStateRunnable = displayDevice.requestDisplayStateLocked(Display.STATE_ON, 0,
- 0);
+ 0, null);
changeStateRunnable.run();
waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
mListener.changedDisplays.clear();
@@ -1142,9 +1146,9 @@
Runnable changeStateRunnable = displayDevice.requestDisplayStateLocked(
supportedState, 0, 0, mDisplayOffloadSession);
changeStateRunnable.run();
-
- verify(mDisplayOffloader).startOffload();
}
+
+ verify(mDisplayOffloader, times(mDisplayOffloadSupportedStates.size())).startOffload();
}
@Test
diff --git a/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayMapperTest.java b/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayMapperTest.java
index 065dd1f..8b13018 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayMapperTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayMapperTest.java
@@ -720,13 +720,15 @@
mIdProducer, POSITION_UNKNOWN,
/* leadDisplayAddress= */ null,
/* brightnessThrottlingMapId= */ "concurrent",
- /* refreshRateZoneId= */ null, /* refreshRateThermalThrottlingMapId= */ null);
+ /* refreshRateZoneId= */ null, /* refreshRateThermalThrottlingMapId= */ null,
+ /* powerThrottlingMapId= */ "concurrent");
layout.createDisplayLocked(device2.getDisplayDeviceInfoLocked().address,
/* isDefault= */ false, /* isEnabled= */ true, /* displayGroup= */ null,
mIdProducer, POSITION_UNKNOWN,
/* leadDisplayAddress= */ null,
/* brightnessThrottlingMapId= */ "concurrent",
- /* refreshRateZoneId= */ null, /* refreshRateThermalThrottlingMapId= */ null);
+ /* refreshRateZoneId= */ null, /* refreshRateThermalThrottlingMapId= */ null,
+ /* powerThrottlingMapId= */ "concurrent");
when(mDeviceStateToLayoutMapSpy.get(0)).thenReturn(layout);
layout = new Layout();
@@ -927,7 +929,7 @@
/* isDefault= */ false, /* isEnabled= */ true, /* displayGroupName= */ null,
mIdProducer, POSITION_REAR, /* leadDisplayAddress= */ null,
/* brightnessThrottlingMapId= */ null, /* refreshRateZoneId= */ null,
- /* refreshRateThermalThrottlingMapId= */null);
+ /* refreshRateThermalThrottlingMapId= */null, /* powerThrottlingMapId= */null);
when(mDeviceStateToLayoutMapSpy.get(0)).thenReturn(layout);
when(mDeviceStateToLayoutMapSpy.size()).thenReturn(1);
@@ -986,7 +988,7 @@
layout.createDisplayLocked(address, /* isDefault= */ false, enabled, group, mIdProducer,
Layout.Display.POSITION_UNKNOWN, /* leadDisplayAddress= */ null,
/* brightnessThrottlingMapId= */ null, /* refreshRateZoneId= */ null,
- /* refreshRateThermalThrottlingMapId= */ null);
+ /* refreshRateThermalThrottlingMapId= */ null, /* powerThrottlingMapId= */ null);
}
private void advanceTime(long timeMs) {
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessClamperControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessClamperControllerTest.java
index 22d2622..c0e0df9 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessClamperControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessClamperControllerTest.java
@@ -35,6 +35,7 @@
import com.android.server.display.DisplayBrightnessState;
import com.android.server.display.brightness.BrightnessReason;
import com.android.server.display.feature.DeviceConfigParameterProvider;
+import com.android.server.display.feature.DisplayManagerFlags;
import com.android.server.testutils.TestHandler;
import org.junit.Before;
@@ -63,12 +64,13 @@
@Mock
private BrightnessClamper<BrightnessClamperController.DisplayDeviceData> mMockClamper;
@Mock
+ private DisplayManagerFlags mFlags;
+ @Mock
private BrightnessModifier mMockModifier;
@Mock
private DisplayManagerInternal.DisplayPowerRequest mMockRequest;
@Mock
private DeviceConfig.Properties mMockProperties;
-
private BrightnessClamperController mClamperController;
private TestInjector mTestInjector;
@@ -219,7 +221,7 @@
private BrightnessClamperController createBrightnessClamperController() {
return new BrightnessClamperController(mTestInjector, mTestHandler, mMockExternalListener,
- mMockDisplayDeviceData, mMockContext);
+ mMockDisplayDeviceData, mMockContext, mFlags);
}
private class TestInjector extends BrightnessClamperController.Injector {
@@ -247,7 +249,8 @@
List<BrightnessClamper<? super BrightnessClamperController.DisplayDeviceData>> getClampers(
Handler handler,
BrightnessClamperController.ClamperChangeListener clamperChangeListener,
- BrightnessClamperController.DisplayDeviceData data) {
+ BrightnessClamperController.DisplayDeviceData data,
+ DisplayManagerFlags flags) {
mCapturedChangeListener = clamperChangeListener;
return mClampers;
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessPowerClamperTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessPowerClamperTest.java
new file mode 100644
index 0000000..b3f33ad
--- /dev/null
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessPowerClamperTest.java
@@ -0,0 +1,246 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.brightness.clamper;
+
+import static com.android.server.display.brightness.clamper.BrightnessPowerClamper.PowerChangeListener;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+
+import android.os.PowerManager;
+import android.os.RemoteException;
+import android.os.Temperature;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.server.display.DisplayDeviceConfig;
+import com.android.server.display.DisplayDeviceConfig.PowerThrottlingConfigData;
+import com.android.server.display.DisplayDeviceConfig.PowerThrottlingData;
+import com.android.server.display.DisplayDeviceConfig.PowerThrottlingData.ThrottlingLevel;
+import com.android.server.display.feature.DeviceConfigParameterProvider;
+import com.android.server.testutils.FakeDeviceConfigInterface;
+import com.android.server.testutils.TestHandler;
+
+import junitparams.JUnitParamsRunner;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.List;
+
+@RunWith(JUnitParamsRunner.class)
+public class BrightnessPowerClamperTest {
+ private static final String TAG = "BrightnessPowerClamperTest";
+ private static final float FLOAT_TOLERANCE = 0.001f;
+
+ private static final String DISPLAY_ID = "displayId";
+ @Mock
+ private BrightnessClamperController.ClamperChangeListener mMockClamperChangeListener;
+ private TestPmicMonitor mPmicMonitor;
+ private final FakeDeviceConfigInterface mFakeDeviceConfigInterface =
+ new FakeDeviceConfigInterface();
+ private final TestHandler mTestHandler = new TestHandler(null);
+ private BrightnessPowerClamper mClamper;
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mClamper = new BrightnessPowerClamper(new TestInjector(), mTestHandler,
+ mMockClamperChangeListener, new TestPowerData());
+ mTestHandler.flush();
+ }
+
+ @Test
+ public void testTypeIsPower() {
+ assertEquals(BrightnessClamper.Type.POWER, mClamper.getType());
+ }
+
+ @Test
+ public void testNoThrottlingData() {
+ assertFalse(mClamper.isActive());
+ assertEquals(PowerManager.BRIGHTNESS_MAX, mClamper.getBrightnessCap(), FLOAT_TOLERANCE);
+ }
+
+ @Test
+ public void testPowerThrottlingNoOngoingAnimation() throws RemoteException {
+ mPmicMonitor.setThermalStatus(Temperature.THROTTLING_SEVERE);
+ mTestHandler.flush();
+ assertFalse(mClamper.isActive());
+ assertEquals(PowerManager.BRIGHTNESS_MAX, mClamper.getBrightnessCap(), FLOAT_TOLERANCE);
+
+ // update a new device config for power-throttling.
+ mClamper.onDisplayChanged(new TestPowerData(
+ List.of(new ThrottlingLevel(PowerManager.THERMAL_STATUS_SEVERE, 100f))));
+
+ mPmicMonitor.setAvgPowerConsumed(200f);
+ float expectedBrightness = 0.5f;
+ expectedBrightness = expectedBrightness * PowerManager.BRIGHTNESS_MAX;
+
+ mTestHandler.flush();
+ // Assume current brightness as max, as there is no throttling.
+ assertEquals(expectedBrightness, mClamper.getBrightnessCap(), FLOAT_TOLERANCE);
+ mPmicMonitor.setThermalStatus(Temperature.THROTTLING_CRITICAL);
+ // update a new device config for power-throttling.
+ mClamper.onDisplayChanged(new TestPowerData(
+ List.of(new ThrottlingLevel(PowerManager.THERMAL_STATUS_CRITICAL, 50f))));
+
+ mPmicMonitor.setAvgPowerConsumed(100f);
+ expectedBrightness = 0.5f * PowerManager.BRIGHTNESS_MAX;
+ mTestHandler.flush();
+ assertEquals(expectedBrightness, mClamper.getBrightnessCap(), FLOAT_TOLERANCE);
+ }
+
+ @Test
+ public void testPowerThrottlingWithOngoingAnimation() throws RemoteException {
+ mPmicMonitor.setThermalStatus(Temperature.THROTTLING_SEVERE);
+ mTestHandler.flush();
+ assertEquals(PowerManager.BRIGHTNESS_MAX, mClamper.getBrightnessCap(), FLOAT_TOLERANCE);
+
+ // update a new device config for power-throttling.
+ mClamper.onDisplayChanged(new TestPowerData(
+ List.of(new ThrottlingLevel(PowerManager.THERMAL_STATUS_SEVERE, 100f))));
+
+ mPmicMonitor.setAvgPowerConsumed(200f);
+ float expectedBrightness = 0.5f;
+ expectedBrightness = expectedBrightness * PowerManager.BRIGHTNESS_MAX;
+
+ mTestHandler.flush();
+ // Assume current brightness as max, as there is no throttling.
+ assertEquals(expectedBrightness, mClamper.getBrightnessCap(), FLOAT_TOLERANCE);
+ mPmicMonitor.setThermalStatus(Temperature.THROTTLING_CRITICAL);
+ // update a new device config for power-throttling.
+ mClamper.onDisplayChanged(new TestPowerData(
+ List.of(new ThrottlingLevel(PowerManager.THERMAL_STATUS_CRITICAL, 50f))));
+
+ mPmicMonitor.setAvgPowerConsumed(100f);
+ expectedBrightness = 0.5f * PowerManager.BRIGHTNESS_MAX;
+ mTestHandler.flush();
+ assertEquals(expectedBrightness, mClamper.getBrightnessCap(), FLOAT_TOLERANCE);
+ }
+
+ @Test
+ public void testPowerThrottlingRemoveBrightnessCap() throws RemoteException {
+ mPmicMonitor.setThermalStatus(Temperature.THROTTLING_LIGHT);
+ mTestHandler.flush();
+ assertFalse(mClamper.isActive());
+ assertEquals(PowerManager.BRIGHTNESS_MAX, mClamper.getBrightnessCap(), FLOAT_TOLERANCE);
+
+ // update a new device config for power-throttling.
+ mClamper.onDisplayChanged(new TestPowerData(
+ List.of(new ThrottlingLevel(PowerManager.THERMAL_STATUS_LIGHT, 100f))));
+
+ mPmicMonitor.setAvgPowerConsumed(200f);
+ float expectedBrightness = 0.5f;
+ expectedBrightness = expectedBrightness * PowerManager.BRIGHTNESS_MAX;
+
+ mTestHandler.flush();
+
+ assertEquals(expectedBrightness, mClamper.getBrightnessCap(), FLOAT_TOLERANCE);
+ mPmicMonitor.setThermalStatus(Temperature.THROTTLING_NONE);
+
+ mPmicMonitor.setAvgPowerConsumed(100f);
+ // No cap applied for Temperature.THROTTLING_NONE
+ expectedBrightness = PowerManager.BRIGHTNESS_MAX;
+ mTestHandler.flush();
+
+ // clamper should not be active anymore.
+ assertFalse(mClamper.isActive());
+ // Assume current brightness as max, as there is no throttling.
+ assertEquals(expectedBrightness, mClamper.getBrightnessCap(), FLOAT_TOLERANCE);
+ }
+
+
+ private static class TestPmicMonitor extends PmicMonitor {
+ private Temperature mCurrentTemperature;
+ private final PowerChangeListener mListener;
+ TestPmicMonitor(PowerChangeListener listener, int pollingTime) {
+ super(listener, pollingTime);
+ mListener = listener;
+ }
+ public void setAvgPowerConsumed(float power) {
+ int status = mCurrentTemperature.getStatus();
+ mListener.onChanged(power, status);
+ }
+ public void setThermalStatus(@Temperature.ThrottlingStatus int status) {
+ mCurrentTemperature = new Temperature(100, Temperature.TYPE_SKIN, "test_temp", status);
+ }
+ }
+
+ private class TestInjector extends BrightnessPowerClamper.Injector {
+ @Override
+ TestPmicMonitor getPmicMonitor(PowerChangeListener listener,
+ int pollingTime) {
+ mPmicMonitor = new TestPmicMonitor(listener, pollingTime);
+ return mPmicMonitor;
+ }
+
+ @Override
+ DeviceConfigParameterProvider getDeviceConfigParameterProvider() {
+ return new DeviceConfigParameterProvider(mFakeDeviceConfigInterface);
+ }
+ }
+
+ private static class TestPowerData implements BrightnessPowerClamper.PowerData {
+
+ private final String mUniqueDisplayId;
+ private final String mDataId;
+ private final PowerThrottlingData mData;
+ private final PowerThrottlingConfigData mConfigData;
+
+ private TestPowerData() {
+ this(DISPLAY_ID, DisplayDeviceConfig.DEFAULT_ID, null);
+ }
+
+ private TestPowerData(List<ThrottlingLevel> data) {
+ this(DISPLAY_ID, DisplayDeviceConfig.DEFAULT_ID, data);
+ }
+
+ private TestPowerData(String uniqueDisplayId, String dataId, List<ThrottlingLevel> data) {
+ mUniqueDisplayId = uniqueDisplayId;
+ mDataId = dataId;
+ mData = PowerThrottlingData.create(data);
+ mConfigData = new PowerThrottlingConfigData(0.1f, 10);
+ }
+
+ @NonNull
+ @Override
+ public String getUniqueDisplayId() {
+ return mUniqueDisplayId;
+ }
+
+ @NonNull
+ @Override
+ public String getPowerThrottlingDataId() {
+ return mDataId;
+ }
+
+ @Nullable
+ @Override
+ public PowerThrottlingData getPowerThrottlingData() {
+ return mData;
+ }
+
+ @Nullable
+ @Override
+ public PowerThrottlingConfigData getPowerThrottlingConfigData() {
+ return mConfigData;
+ }
+ }
+}
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/notifications/ConnectedDisplayUsbErrorsDetectorTest.java b/services/tests/displayservicetests/src/com/android/server/display/notifications/ConnectedDisplayUsbErrorsDetectorTest.java
new file mode 100644
index 0000000..d5a92cb
--- /dev/null
+++ b/services/tests/displayservicetests/src/com/android/server/display/notifications/ConnectedDisplayUsbErrorsDetectorTest.java
@@ -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.server.display.notifications;
+
+import static android.hardware.usb.DisplayPortAltModeInfo.DISPLAYPORT_ALT_MODE_STATUS_CAPABLE_DISABLED;
+import static android.hardware.usb.DisplayPortAltModeInfo.DISPLAYPORT_ALT_MODE_STATUS_ENABLED;
+import static android.hardware.usb.DisplayPortAltModeInfo.DISPLAYPORT_ALT_MODE_STATUS_NOT_CAPABLE;
+import static android.hardware.usb.DisplayPortAltModeInfo.LINK_TRAINING_STATUS_FAILURE;
+
+import static org.junit.Assume.assumeFalse;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.hardware.usb.DisplayPortAltModeInfo;
+import android.hardware.usb.UsbManager;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.filters.SmallTest;
+
+import com.android.server.display.feature.DisplayManagerFlags;
+import com.android.server.display.notifications.ConnectedDisplayUsbErrorsDetector.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.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Tests for {@link ConnectedDisplayUsbErrorsDetector}
+ * Run: atest ConnectedDisplayUsbErrorsDetectorTest
+ */
+@SmallTest
+@RunWith(TestParameterInjector.class)
+public class ConnectedDisplayUsbErrorsDetectorTest {
+ @Mock
+ private Injector mMockedInjector;
+ @Mock
+ private UsbManager mMockedUsbManager;
+ @Mock
+ private DisplayManagerFlags mMockedFlags;
+ @Mock
+ private ConnectedDisplayUsbErrorsDetector.Listener mMockedListener;
+
+ /** Setup tests. */
+ @Before
+ public void setup() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ @Test
+ public void testNoErrorTypes(
+ @TestParameter final boolean isUsbManagerAvailable,
+ @TestParameter final boolean isUsbErrorsNotificationEnabled) {
+ // This is tested in #testErrorOnUsbCableNotCapableDp and #testErrorOnDpLinkTrainingFailure
+ assumeFalse(isUsbManagerAvailable && isUsbErrorsNotificationEnabled);
+ var detector = createErrorsDetector(isUsbManagerAvailable, isUsbErrorsNotificationEnabled);
+ // None of these should trigger an error now.
+ detector.onDisplayPortAltModeInfoChanged("portId", createInfoOnUsbCableNotCapableDp());
+ detector.onDisplayPortAltModeInfoChanged("portId", createInfoOnDpLinkTrainingFailure());
+ verify(mMockedUsbManager, never()).registerDisplayPortAltModeInfoListener(any(), any());
+ verify(mMockedListener, never()).onCableNotCapableDisplayPort();
+ verify(mMockedListener, never()).onDisplayPortLinkTrainingFailure();
+ }
+
+ @Test
+ public void testErrorOnUsbCableNotCapableDp() {
+ var detector = createErrorsDetector(/*isUsbManagerAvailable=*/ true,
+ /*isUsbErrorsNotificationEnabled=*/ true);
+ detector.onDisplayPortAltModeInfoChanged("portId", createInfoOnUsbCableNotCapableDp());
+ verify(mMockedUsbManager).registerDisplayPortAltModeInfoListener(any(), any());
+ verify(mMockedListener).onCableNotCapableDisplayPort();
+ verify(mMockedListener, never()).onDisplayPortLinkTrainingFailure();
+ }
+
+ @Test
+ public void testErrorOnDpLinkTrainingFailure() {
+ var detector = createErrorsDetector(/*isUsbManagerAvailable=*/ true,
+ /*isUsbErrorsNotificationEnabled=*/ true);
+ detector.onDisplayPortAltModeInfoChanged("portId", createInfoOnDpLinkTrainingFailure());
+ verify(mMockedUsbManager).registerDisplayPortAltModeInfoListener(any(), any());
+ verify(mMockedListener, never()).onCableNotCapableDisplayPort();
+ verify(mMockedListener).onDisplayPortLinkTrainingFailure();
+ }
+
+ private ConnectedDisplayUsbErrorsDetector createErrorsDetector(
+ final boolean isUsbManagerAvailable,
+ final boolean isConnectedDisplayUsbErrorsNotificationEnabled) {
+ when(mMockedFlags.isConnectedDisplayErrorHandlingEnabled())
+ .thenReturn(isConnectedDisplayUsbErrorsNotificationEnabled);
+ when(mMockedInjector.getUsbManager()).thenReturn(
+ (isUsbManagerAvailable) ? mMockedUsbManager : null);
+ var detector = new ConnectedDisplayUsbErrorsDetector(mMockedFlags,
+ ApplicationProvider.getApplicationContext(), mMockedInjector);
+ detector.registerListener(mMockedListener);
+ return detector;
+ }
+
+ private DisplayPortAltModeInfo createInfoOnUsbCableNotCapableDp() {
+ return new DisplayPortAltModeInfo(
+ DISPLAYPORT_ALT_MODE_STATUS_CAPABLE_DISABLED,
+ DISPLAYPORT_ALT_MODE_STATUS_NOT_CAPABLE, -1, false, 0);
+ }
+
+ private DisplayPortAltModeInfo createInfoOnDpLinkTrainingFailure() {
+ return new DisplayPortAltModeInfo(
+ DISPLAYPORT_ALT_MODE_STATUS_CAPABLE_DISABLED,
+ DISPLAYPORT_ALT_MODE_STATUS_ENABLED, -1, false,
+ LINK_TRAINING_STATUS_FAILURE);
+ }
+}
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..1d2034b
--- /dev/null
+++ b/services/tests/displayservicetests/src/com/android/server/display/notifications/DisplayNotificationManagerTest.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.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 testNotificationOnDisplayPortLinkTrainingFailure() {
+ var dnm = createDisplayNotificationManager(/*isNotificationManagerAvailable=*/ true,
+ /*isErrorHandlingEnabled=*/ true);
+ dnm.onDisplayPortLinkTrainingFailure();
+ assertExpectedNotification();
+ }
+
+ @Test
+ public void testNotificationOnCableNotCapableDisplayPort() {
+ var dnm = createDisplayNotificationManager(/*isNotificationManagerAvailable=*/ true,
+ /*isErrorHandlingEnabled=*/ true);
+ dnm.onCableNotCapableDisplayPort();
+ assertExpectedNotification();
+ }
+
+ @Test
+ public void testNoErrorNotification(
+ @TestParameter final boolean isNotificationManagerAvailable,
+ @TestParameter final boolean isErrorHandlingEnabled) {
+ /* This case is tested by #testNotificationOnHotplugConnectionError,
+ #testNotificationOnDisplayPortLinkTrainingFailure,
+ #testNotificationOnCableNotCapableDisplayPort */
+ assumeFalse(isNotificationManagerAvailable && isErrorHandlingEnabled);
+ var dnm = createDisplayNotificationManager(isNotificationManagerAvailable,
+ isErrorHandlingEnabled);
+ // None of these methods should trigger a notification now.
+ dnm.onHotplugConnectionError();
+ dnm.onDisplayPortLinkTrainingFailure();
+ dnm.onCableNotCapableDisplayPort();
+ 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);
+ // Usb errors detector is tested in ConnectedDisplayUsbErrorsDetectorTest
+ when(mMockedInjector.getUsbErrorsDetector()).thenReturn(/* usbErrorsDetector= */ 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/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/display/SmallAreaDetectionControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/display/SmallAreaDetectionControllerTest.java
index 1ce79a5..05ac5b5 100644
--- a/services/tests/mockingservicestests/src/com/android/server/display/SmallAreaDetectionControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/display/SmallAreaDetectionControllerTest.java
@@ -16,8 +16,6 @@
package com.android.server.display;
-import static android.os.Process.INVALID_UID;
-
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doNothing;
@@ -35,7 +33,7 @@
import androidx.test.runner.AndroidJUnit4;
import com.android.server.LocalServices;
-import com.android.server.pm.UserManagerInternal;
+import com.android.server.pm.pkg.PackageStateInternal;
import org.junit.Before;
import org.junit.Rule;
@@ -55,7 +53,10 @@
@Mock
private PackageManagerInternal mMockPackageManagerInternal;
@Mock
- private UserManagerInternal mMockUserManagerInternal;
+ private PackageStateInternal mMockPkgStateA;
+ @Mock
+ private PackageStateInternal mMockPkgStateB;
+
private SmallAreaDetectionController mSmallAreaDetectionController;
@@ -64,29 +65,18 @@
private static final String PKG_NOT_INSTALLED = "com.not.installed";
private static final float THRESHOLD_A = 0.05f;
private static final float THRESHOLD_B = 0.07f;
- private static final int USER_1 = 110;
- private static final int USER_2 = 111;
- private static final int UID_A_1 = 11011111;
- private static final int UID_A_2 = 11111111;
- private static final int UID_B_1 = 11022222;
- private static final int UID_B_2 = 11122222;
+ private static final int APP_ID_A = 11111;
+ private static final int APP_ID_B = 22222;
@Before
public void setup() {
LocalServices.removeServiceForTest(PackageManagerInternal.class);
LocalServices.addService(PackageManagerInternal.class, mMockPackageManagerInternal);
- LocalServices.removeServiceForTest(UserManagerInternal.class);
- LocalServices.addService(UserManagerInternal.class, mMockUserManagerInternal);
- when(mMockUserManagerInternal.getUserIds()).thenReturn(new int[]{USER_1, USER_2});
- when(mMockPackageManagerInternal.getPackageUid(PKG_A, 0, USER_1)).thenReturn(UID_A_1);
- when(mMockPackageManagerInternal.getPackageUid(PKG_A, 0, USER_2)).thenReturn(UID_A_2);
- when(mMockPackageManagerInternal.getPackageUid(PKG_B, 0, USER_1)).thenReturn(UID_B_1);
- when(mMockPackageManagerInternal.getPackageUid(PKG_B, 0, USER_2)).thenReturn(UID_B_2);
- when(mMockPackageManagerInternal.getPackageUid(PKG_NOT_INSTALLED, 0, USER_1)).thenReturn(
- INVALID_UID);
- when(mMockPackageManagerInternal.getPackageUid(PKG_NOT_INSTALLED, 0, USER_2)).thenReturn(
- INVALID_UID);
+ when(mMockPackageManagerInternal.getPackageStateInternal(PKG_A)).thenReturn(mMockPkgStateA);
+ when(mMockPackageManagerInternal.getPackageStateInternal(PKG_B)).thenReturn(mMockPkgStateB);
+ when(mMockPkgStateA.getAppId()).thenReturn(APP_ID_A);
+ when(mMockPkgStateB.getAppId()).thenReturn(APP_ID_B);
mSmallAreaDetectionController = spy(new SmallAreaDetectionController(
new ContextWrapper(ApplicationProvider.getApplicationContext()),
@@ -99,9 +89,9 @@
final String property = PKG_A + ":" + THRESHOLD_A + "," + PKG_B + ":" + THRESHOLD_B;
mSmallAreaDetectionController.updateAllowlist(property);
- final int[] resultUidArray = {UID_A_1, UID_B_1, UID_A_2, UID_B_2};
- final float[] resultThresholdArray = {THRESHOLD_A, THRESHOLD_B, THRESHOLD_A, THRESHOLD_B};
- verify(mSmallAreaDetectionController).updateSmallAreaDetection(eq(resultUidArray),
+ final int[] resultAppIdArray = {APP_ID_A, APP_ID_B};
+ final float[] resultThresholdArray = {THRESHOLD_A, THRESHOLD_B};
+ verify(mSmallAreaDetectionController).updateSmallAreaDetection(eq(resultAppIdArray),
eq(resultThresholdArray));
}
@@ -110,9 +100,9 @@
final String property = PKG_A + "," + PKG_B + ":" + THRESHOLD_B;
mSmallAreaDetectionController.updateAllowlist(property);
- final int[] resultUidArray = {UID_B_1, UID_B_2};
- final float[] resultThresholdArray = {THRESHOLD_B, THRESHOLD_B};
- verify(mSmallAreaDetectionController).updateSmallAreaDetection(eq(resultUidArray),
+ final int[] resultAppIdArray = {APP_ID_B};
+ final float[] resultThresholdArray = {THRESHOLD_B};
+ verify(mSmallAreaDetectionController).updateSmallAreaDetection(eq(resultAppIdArray),
eq(resultThresholdArray));
}
@@ -122,9 +112,9 @@
PKG_A + ":" + THRESHOLD_A + "," + PKG_NOT_INSTALLED + ":" + THRESHOLD_B;
mSmallAreaDetectionController.updateAllowlist(property);
- final int[] resultUidArray = {UID_A_1, UID_A_2};
- final float[] resultThresholdArray = {THRESHOLD_A, THRESHOLD_A};
- verify(mSmallAreaDetectionController).updateSmallAreaDetection(eq(resultUidArray),
+ final int[] resultAppIdArray = {APP_ID_A};
+ final float[] resultThresholdArray = {THRESHOLD_A};
+ verify(mSmallAreaDetectionController).updateSmallAreaDetection(eq(resultAppIdArray),
eq(resultThresholdArray));
}
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/powerstatstests/src/com/android/server/power/stats/AggregatedPowerStatsTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/AggregatedPowerStatsTest.java
new file mode 100644
index 0000000..2003d04
--- /dev/null
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/AggregatedPowerStatsTest.java
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.power.stats;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.BatteryConsumer;
+import android.os.PersistableBundle;
+import android.util.Xml;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.os.PowerStats;
+import com.android.modules.utils.TypedXmlPullParser;
+import com.android.modules.utils.TypedXmlSerializer;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.text.ParseException;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class AggregatedPowerStatsTest {
+ private static final int TEST_POWER_COMPONENT = 1077;
+ private static final int APP_1 = 27;
+ private static final int APP_2 = 42;
+
+ private AggregatedPowerStatsConfig mAggregatedPowerStatsConfig;
+ private PowerStats.Descriptor mPowerComponentDescriptor;
+
+ @Before
+ public void setup() throws ParseException {
+ mAggregatedPowerStatsConfig = new AggregatedPowerStatsConfig();
+ mAggregatedPowerStatsConfig.trackPowerComponent(TEST_POWER_COMPONENT)
+ .trackDeviceStates(
+ AggregatedPowerStatsConfig.STATE_POWER,
+ AggregatedPowerStatsConfig.STATE_SCREEN)
+ .trackUidStates(
+ AggregatedPowerStatsConfig.STATE_POWER,
+ AggregatedPowerStatsConfig.STATE_SCREEN,
+ AggregatedPowerStatsConfig.STATE_PROCESS_STATE);
+
+ mPowerComponentDescriptor = new PowerStats.Descriptor(TEST_POWER_COMPONENT, "fan", 2, 3,
+ PersistableBundle.forPair("speed", "fast"));
+ }
+
+ @Test
+ public void aggregation() {
+ AggregatedPowerStats stats = prepareAggregatePowerStats();
+
+ verifyAggregatedPowerStats(stats);
+ }
+
+ @Test
+ public void xmlPersistence() throws Exception {
+ AggregatedPowerStats stats = prepareAggregatePowerStats();
+
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ TypedXmlSerializer serializer = Xml.newFastSerializer();
+ serializer.setOutput(baos, "UTF-8");
+ stats.writeXml(serializer);
+ serializer.flush();
+
+ TypedXmlPullParser parser = Xml.newFastPullParser();
+ parser.setInput(new ByteArrayInputStream(baos.toByteArray()), "UTF-8");
+ AggregatedPowerStats actualStats = AggregatedPowerStats.createFromXml(parser,
+ mAggregatedPowerStatsConfig);
+
+ verifyAggregatedPowerStats(actualStats);
+ }
+
+ private AggregatedPowerStats prepareAggregatePowerStats() {
+ AggregatedPowerStats stats = new AggregatedPowerStats(mAggregatedPowerStatsConfig);
+ stats.addClockUpdate(1000, 456);
+ stats.setDuration(789);
+
+ stats.setDeviceState(AggregatedPowerStatsConfig.STATE_SCREEN,
+ AggregatedPowerStatsConfig.SCREEN_STATE_ON, 2000);
+ stats.setUidState(APP_1, AggregatedPowerStatsConfig.STATE_PROCESS_STATE,
+ BatteryConsumer.PROCESS_STATE_CACHED, 2000);
+ stats.setUidState(APP_2, AggregatedPowerStatsConfig.STATE_PROCESS_STATE,
+ BatteryConsumer.PROCESS_STATE_FOREGROUND, 2000);
+
+ PowerStats ps = new PowerStats(mPowerComponentDescriptor);
+ ps.stats[0] = 100;
+ ps.stats[1] = 987;
+
+ ps.uidStats.put(APP_1, new long[]{389, 0, 739});
+ ps.uidStats.put(APP_2, new long[]{278, 314, 628});
+
+ stats.addPowerStats(ps, 3000);
+
+ stats.setDeviceState(AggregatedPowerStatsConfig.STATE_SCREEN,
+ AggregatedPowerStatsConfig.SCREEN_STATE_OTHER, 4000);
+ stats.setUidState(APP_2, AggregatedPowerStatsConfig.STATE_PROCESS_STATE,
+ BatteryConsumer.PROCESS_STATE_BACKGROUND, 4000);
+
+ ps.stats[0] = 444;
+ ps.stats[1] = 0;
+
+ ps.uidStats.put(APP_1, new long[]{0, 0, 400});
+ ps.uidStats.put(APP_2, new long[]{100, 200, 300});
+
+ stats.addPowerStats(ps, 5000);
+
+ return stats;
+ }
+
+ private void verifyAggregatedPowerStats(AggregatedPowerStats stats) {
+ PowerStats.Descriptor descriptor = stats.getPowerComponentStats(TEST_POWER_COMPONENT)
+ .getPowerStatsDescriptor();
+ assertThat(descriptor.powerComponentId).isEqualTo(TEST_POWER_COMPONENT);
+ assertThat(descriptor.name).isEqualTo("fan");
+ assertThat(descriptor.statsArrayLength).isEqualTo(2);
+ assertThat(descriptor.uidStatsArrayLength).isEqualTo(3);
+ assertThat(descriptor.extras.getString("speed")).isEqualTo("fast");
+
+ assertThat(getDeviceStats(stats,
+ AggregatedPowerStatsConfig.POWER_STATE_BATTERY,
+ AggregatedPowerStatsConfig.SCREEN_STATE_ON))
+ .isEqualTo(new long[]{322, 987});
+
+ assertThat(getDeviceStats(stats,
+ AggregatedPowerStatsConfig.POWER_STATE_BATTERY,
+ AggregatedPowerStatsConfig.SCREEN_STATE_OTHER))
+ .isEqualTo(new long[]{222, 0});
+
+ assertThat(getUidDeviceStats(stats,
+ APP_1,
+ AggregatedPowerStatsConfig.POWER_STATE_BATTERY,
+ AggregatedPowerStatsConfig.SCREEN_STATE_ON,
+ BatteryConsumer.PROCESS_STATE_UNSPECIFIED))
+ .isEqualTo(new long[]{259, 0, 492});
+
+ assertThat(getUidDeviceStats(stats,
+ APP_1,
+ AggregatedPowerStatsConfig.POWER_STATE_BATTERY,
+ AggregatedPowerStatsConfig.SCREEN_STATE_ON,
+ BatteryConsumer.PROCESS_STATE_CACHED))
+ .isEqualTo(new long[]{129, 0, 446});
+
+ assertThat(getUidDeviceStats(stats,
+ APP_1,
+ AggregatedPowerStatsConfig.POWER_STATE_BATTERY,
+ AggregatedPowerStatsConfig.SCREEN_STATE_OTHER,
+ BatteryConsumer.PROCESS_STATE_CACHED))
+ .isEqualTo(new long[]{0, 0, 200});
+
+ assertThat(getUidDeviceStats(stats,
+ APP_2,
+ AggregatedPowerStatsConfig.POWER_STATE_BATTERY,
+ AggregatedPowerStatsConfig.SCREEN_STATE_ON,
+ BatteryConsumer.PROCESS_STATE_UNSPECIFIED))
+ .isEqualTo(new long[]{185, 209, 418});
+
+ assertThat(getUidDeviceStats(stats,
+ APP_2,
+ AggregatedPowerStatsConfig.POWER_STATE_BATTERY,
+ AggregatedPowerStatsConfig.SCREEN_STATE_ON,
+ BatteryConsumer.PROCESS_STATE_FOREGROUND))
+ .isEqualTo(new long[]{142, 204, 359});
+
+ assertThat(getUidDeviceStats(stats,
+ APP_2,
+ AggregatedPowerStatsConfig.POWER_STATE_BATTERY,
+ AggregatedPowerStatsConfig.SCREEN_STATE_OTHER,
+ BatteryConsumer.PROCESS_STATE_BACKGROUND))
+ .isEqualTo(new long[]{50, 100, 150});
+ }
+
+ private static long[] getDeviceStats(AggregatedPowerStats stats, int... states) {
+ long[] out = new long[states.length];
+ stats.getPowerComponentStats(TEST_POWER_COMPONENT).getDeviceStats(out, states);
+ return out;
+ }
+
+ private static long[] getUidDeviceStats(AggregatedPowerStats stats, int uid, int... states) {
+ long[] out = new long[states.length];
+ stats.getPowerComponentStats(TEST_POWER_COMPONENT).getUidStats(out, uid, states);
+ return out;
+ }
+}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryExternalStatsWorkerTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryExternalStatsWorkerTest.java
index 5a2d2e3..663af5d 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryExternalStatsWorkerTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryExternalStatsWorkerTest.java
@@ -42,6 +42,7 @@
import androidx.test.InstrumentationRegistry;
+import com.android.internal.os.Clock;
import com.android.internal.os.CpuScalingPolicies;
import com.android.internal.os.PowerProfile;
@@ -214,6 +215,7 @@
public class TestBatteryStatsImpl extends BatteryStatsImpl {
public TestBatteryStatsImpl(Context context) {
+ super(Clock.SYSTEM_CLOCK, null);
mPowerProfile = new PowerProfile(context, true /* forTest */);
SparseArray<int[]> cpusByPolicy = new SparseArray<>();
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsHistoryIteratorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsHistoryIteratorTest.java
index 77124d0..abb3be7 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsHistoryIteratorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsHistoryIteratorTest.java
@@ -113,15 +113,15 @@
public void constrainedIteration() {
prepareHistory();
- // Initial time is 3000
+ // Initial time is 1000_000
assertIncludedEvents(mBatteryStats.iterateBatteryStatsHistory(0, 0),
- 3_000L, 3_000L, 1003_000L, 2003_000L, 2004_000L);
- assertIncludedEvents(mBatteryStats.iterateBatteryStatsHistory(1000_000, 0),
- 1003_000L, 2003_000L, 2004_000L);
- assertIncludedEvents(mBatteryStats.iterateBatteryStatsHistory(0, 2000_000L),
- 3_000L, 3_000L, 1003_000L);
+ 1000_000L, 1000_000L, 2000_000L, 3000_000L, 3001_000L);
+ assertIncludedEvents(mBatteryStats.iterateBatteryStatsHistory(2000_000, 0),
+ 2000_000L, 3000_000L, 3001_000L);
+ assertIncludedEvents(mBatteryStats.iterateBatteryStatsHistory(0, 3000_000L),
+ 1000_000L, 1000_000L, 2000_000L);
assertIncludedEvents(mBatteryStats.iterateBatteryStatsHistory(1003_000L, 2004_000L),
- 1003_000L, 2003_000L);
+ 2000_000L);
}
private void prepareHistory() {
@@ -144,7 +144,7 @@
ArrayList<Long> actualTimestamps = new ArrayList<>();
while (iterator.hasNext()) {
BatteryStats.HistoryItem item = iterator.next();
- actualTimestamps.add(item.currentTime);
+ actualTimestamps.add(item.time);
}
assertThat(actualTimestamps).isEqualTo(Arrays.asList(expectedTimestamps));
}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java
index f22296a..1dd499c 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java
@@ -41,6 +41,7 @@
import com.android.internal.os.BatteryStatsHistory;
import com.android.internal.os.BatteryStatsHistoryIterator;
+import com.android.internal.os.MonotonicClock;
import com.android.internal.os.PowerStats;
import org.junit.Before;
@@ -69,6 +70,7 @@
private File mSystemDir;
private File mHistoryDir;
private final MockClock mClock = new MockClock();
+ private final MonotonicClock mMonotonicClock = new MonotonicClock(0, mClock);
private BatteryStatsHistory mHistory;
private BatteryStats.HistoryPrinter mHistoryPrinter;
@Mock
@@ -94,7 +96,7 @@
mClock.realtime = 123;
mHistory = new BatteryStatsHistory(mHistoryBuffer, mSystemDir, 32, 1024,
- mStepDetailsCalculator, mClock, mTracer) {
+ mStepDetailsCalculator, mClock, mMonotonicClock, mTracer) {
@Override
public boolean readFileToParcel(Parcel out, AtomicFile file) {
mReadFiles.add(file.getBaseFile().getName());
@@ -210,7 +212,7 @@
mClock.realtime = 1000 * i;
fileList.add(mClock.realtime + ".bh");
- mHistory.startNextFile();
+ mHistory.startNextFile(mClock.realtime);
createActiveFile(mHistory);
verifyFileNames(mHistory, fileList);
verifyActiveFile(mHistory, mClock.realtime + ".bh");
@@ -218,7 +220,7 @@
// create file 32
mClock.realtime = 1000 * 32;
- mHistory.startNextFile();
+ mHistory.startNextFile(mClock.realtime);
createActiveFile(mHistory);
fileList.add("32000.bh");
fileList.remove(0);
@@ -229,7 +231,7 @@
// create file 33
mClock.realtime = 1000 * 33;
- mHistory.startNextFile();
+ mHistory.startNextFile(mClock.realtime);
createActiveFile(mHistory);
// verify file 1 is deleted
fileList.add("33000.bh");
@@ -240,7 +242,7 @@
// create a new BatteryStatsHistory object, it will pick up existing history files.
BatteryStatsHistory history2 = new BatteryStatsHistory(mHistoryBuffer, mSystemDir, 32, 1024,
- null, mClock, mTracer);
+ null, mClock, mMonotonicClock, mTracer);
// verify constructor can pick up all files from file system.
verifyFileNames(history2, fileList);
verifyActiveFile(history2, "33000.bh");
@@ -262,7 +264,7 @@
// create file 1.
mClock.realtime = 2345678;
- history2.startNextFile();
+ history2.startNextFile(mClock.realtime);
createActiveFile(history2);
verifyFileNames(history2, Arrays.asList("1234567.bh", "2345678.bh"));
verifyActiveFile(history2, "2345678.bh");
@@ -336,14 +338,14 @@
mHistory.recordEvent(mClock.realtime, mClock.uptime,
BatteryStats.HistoryItem.EVENT_JOB_START, "job", 42);
- mHistory.startNextFile(); // 1000.bh
+ mHistory.startNextFile(mClock.realtime); // 1000.bh
mClock.realtime = 2000;
mClock.uptime = 2000;
mHistory.recordEvent(mClock.realtime, mClock.uptime,
BatteryStats.HistoryItem.EVENT_JOB_FINISH, "job", 42);
- mHistory.startNextFile(); // 2000.bh
+ mHistory.startNextFile(mClock.realtime); // 2000.bh
mClock.realtime = 3000;
mClock.uptime = 3000;
@@ -351,7 +353,7 @@
HistoryItem.EVENT_ALARM, "alarm", 42);
// Flush accumulated history to disk
- mHistory.startNextFile();
+ mHistory.startNextFile(mClock.realtime);
}
private void verifyActiveFile(BatteryStatsHistory history, String file) {
@@ -518,7 +520,7 @@
// Keep the preserved part of history short - we only need to capture the very tail of
// history.
mHistory = new BatteryStatsHistory(mHistoryBuffer, mSystemDir, 1, 6000,
- mStepDetailsCalculator, mClock, mTracer);
+ mStepDetailsCalculator, mClock, mMonotonicClock, mTracer);
mHistory.forceRecordAllHistory();
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsProviderTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsProviderTest.java
index 5df0acb..b1da1fc 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsProviderTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsProviderTest.java
@@ -40,6 +40,7 @@
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.os.BatteryStatsHistoryIterator;
+import com.android.internal.os.MonotonicClock;
import com.android.internal.os.PowerProfile;
import org.junit.Rule;
@@ -64,6 +65,8 @@
new BatteryUsageStatsRule(12345, mHistoryDir)
.setAveragePower(PowerProfile.POWER_FLASHLIGHT, 360.0)
.setAveragePower(PowerProfile.POWER_AUDIO, 720.0);
+ private MockClock mMockClock = mStatsRule.getMockClock();
+
@Test
public void test_getBatteryUsageStats() {
BatteryStatsImpl batteryStats = prepareBatteryStats();
@@ -369,17 +372,23 @@
public void testAggregateBatteryStats() {
Context context = InstrumentationRegistry.getContext();
BatteryStatsImpl batteryStats = mStatsRule.getBatteryStats();
- mStatsRule.setCurrentTime(5 * MINUTE_IN_MS);
+ MonotonicClock monotonicClock = new MonotonicClock(0, mStatsRule.getMockClock());
+
+ setTime(5 * MINUTE_IN_MS);
synchronized (batteryStats) {
batteryStats.resetAllStatsAndHistoryLocked(BatteryStatsImpl.RESET_REASON_ADB_COMMAND);
}
- BatteryUsageStatsStore batteryUsageStatsStore = new BatteryUsageStatsStore(context,
- batteryStats, new File(context.getCacheDir(), "BatteryUsageStatsProviderTest"),
- new TestHandler(), Integer.MAX_VALUE);
- batteryUsageStatsStore.onSystemReady();
+
+ PowerStatsStore powerStatsStore = new PowerStatsStore(
+ new File(context.getCacheDir(), "BatteryUsageStatsProviderTest"),
+ new TestHandler(), null);
BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(context,
- batteryStats, batteryUsageStatsStore);
+ batteryStats, powerStatsStore);
+
+ batteryStats.setBatteryResetListener(reason ->
+ powerStatsStore.storeBatteryUsageStats(monotonicClock.monotonicTime(),
+ provider.getBatteryUsageStats(BatteryUsageStatsQuery.DEFAULT)));
synchronized (batteryStats) {
batteryStats.noteFlashlightOnLocked(APP_UID,
@@ -389,7 +398,7 @@
batteryStats.noteFlashlightOffLocked(APP_UID,
20 * MINUTE_IN_MS, 20 * MINUTE_IN_MS);
}
- mStatsRule.setCurrentTime(25 * MINUTE_IN_MS);
+ setTime(25 * MINUTE_IN_MS);
synchronized (batteryStats) {
batteryStats.resetAllStatsAndHistoryLocked(BatteryStatsImpl.RESET_REASON_ADB_COMMAND);
}
@@ -402,7 +411,7 @@
batteryStats.noteFlashlightOffLocked(APP_UID,
50 * MINUTE_IN_MS, 50 * MINUTE_IN_MS);
}
- mStatsRule.setCurrentTime(55 * MINUTE_IN_MS);
+ setTime(55 * MINUTE_IN_MS);
synchronized (batteryStats) {
batteryStats.resetAllStatsAndHistoryLocked(BatteryStatsImpl.RESET_REASON_ADB_COMMAND);
}
@@ -416,7 +425,7 @@
batteryStats.noteFlashlightOffLocked(APP_UID,
70 * MINUTE_IN_MS, 70 * MINUTE_IN_MS);
}
- mStatsRule.setCurrentTime(75 * MINUTE_IN_MS);
+ setTime(75 * MINUTE_IN_MS);
synchronized (batteryStats) {
batteryStats.resetAllStatsAndHistoryLocked(BatteryStatsImpl.RESET_REASON_ADB_COMMAND);
}
@@ -430,7 +439,7 @@
batteryStats.noteFlashlightOffLocked(APP_UID,
90 * MINUTE_IN_MS, 90 * MINUTE_IN_MS);
}
- mStatsRule.setCurrentTime(95 * MINUTE_IN_MS);
+ setTime(95 * MINUTE_IN_MS);
// Include the first and the second snapshot, but not the third or current
BatteryUsageStatsQuery query = new BatteryUsageStatsQuery.Builder()
@@ -457,29 +466,41 @@
.of(180.0);
}
+ private void setTime(long timeMs) {
+ mMockClock.currentTime = timeMs;
+ mMockClock.realtime = timeMs;
+ }
+
@Test
public void testAggregateBatteryStats_incompatibleSnapshot() {
Context context = InstrumentationRegistry.getContext();
MockBatteryStatsImpl batteryStats = mStatsRule.getBatteryStats();
batteryStats.initMeasuredEnergyStats(new String[]{"FOO", "BAR"});
- BatteryUsageStatsStore batteryUsageStatsStore = mock(BatteryUsageStatsStore.class);
+ PowerStatsStore powerStatsStore = mock(PowerStatsStore.class);
- when(batteryUsageStatsStore.listBatteryUsageStatsTimestamps())
- .thenReturn(new long[]{1000, 2000});
-
- when(batteryUsageStatsStore.loadBatteryUsageStats(1000)).thenReturn(
+ PowerStatsSpan span0 = new PowerStatsSpan(0);
+ span0.addTimeFrame(0, 1000, 1234);
+ span0.addSection(new BatteryUsageStatsSection(
new BatteryUsageStats.Builder(batteryStats.getCustomEnergyConsumerNames())
- .setStatsDuration(1234).build());
+ .setStatsDuration(1234).build()));
- // Add a snapshot, with a different set of custom power components. It should
- // be skipped by the aggregation.
- when(batteryUsageStatsStore.loadBatteryUsageStats(2000)).thenReturn(
+ PowerStatsSpan span1 = new PowerStatsSpan(1);
+ span1.addTimeFrame(0, 2000, 4321);
+ span1.addSection(new BatteryUsageStatsSection(
new BatteryUsageStats.Builder(new String[]{"different"})
- .setStatsDuration(4321).build());
+ .setStatsDuration(4321).build()));
+
+ when(powerStatsStore.getTableOfContents()).thenReturn(
+ List.of(span0.getMetadata(), span1.getMetadata()));
+
+ when(powerStatsStore.loadPowerStatsSpan(0, BatteryUsageStatsSection.TYPE))
+ .thenReturn(span0);
+ when(powerStatsStore.loadPowerStatsSpan(1, BatteryUsageStatsSection.TYPE))
+ .thenReturn(span1);
BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(context,
- batteryStats, batteryUsageStatsStore);
+ batteryStats, powerStatsStore);
BatteryUsageStatsQuery query = new BatteryUsageStatsQuery.Builder()
.aggregateSnapshots(0, 3000)
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsRule.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsRule.java
index 93cbea6..3579fce 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsRule.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsRule.java
@@ -88,6 +88,10 @@
mBatteryStats.onSystemReady();
}
+ public MockClock getMockClock() {
+ return mMockClock;
+ }
+
public BatteryUsageStatsRule setTestPowerProfile(@XmlRes int xmlId) {
mPowerProfile.forceInitForTesting(mContext, xmlId);
return this;
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsStoreTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsStoreTest.java
deleted file mode 100644
index b846e3a..0000000
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsStoreTest.java
+++ /dev/null
@@ -1,227 +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.power.stats;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.mock;
-
-import android.content.Context;
-import android.os.BatteryManager;
-import android.os.BatteryUsageStats;
-import android.os.BatteryUsageStatsQuery;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.util.Xml;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.internal.os.PowerProfile;
-import com.android.modules.utils.TypedXmlSerializer;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.IOException;
-import java.nio.charset.StandardCharsets;
-import java.util.Arrays;
-
-@RunWith(AndroidJUnit4.class)
-@SuppressWarnings("GuardedBy")
-public class BatteryUsageStatsStoreTest {
- private static final long MAX_BATTERY_STATS_SNAPSHOT_STORAGE_BYTES = 2 * 1024;
-
- private final MockClock mMockClock = new MockClock();
- private MockBatteryStatsImpl mBatteryStats;
- private BatteryUsageStatsStore mBatteryUsageStatsStore;
- private BatteryUsageStatsProvider mBatteryUsageStatsProvider;
- private File mStoreDirectory;
-
- @Before
- public void setup() {
- mMockClock.currentTime = 123;
- mBatteryStats = new MockBatteryStatsImpl(mMockClock);
- mBatteryStats.setNoAutoReset(true);
- mBatteryStats.setPowerProfile(mock(PowerProfile.class));
- mBatteryStats.onSystemReady();
-
- Context context = InstrumentationRegistry.getContext();
-
- mStoreDirectory = new File(context.getCacheDir(), "BatteryUsageStatsStoreTest");
- clearDirectory(mStoreDirectory);
-
- mBatteryUsageStatsStore = new BatteryUsageStatsStore(context, mBatteryStats,
- mStoreDirectory, new TestHandler(), MAX_BATTERY_STATS_SNAPSHOT_STORAGE_BYTES);
- mBatteryUsageStatsStore.onSystemReady();
-
- mBatteryUsageStatsProvider = new BatteryUsageStatsProvider(context, mBatteryStats);
- }
-
- @Test
- public void testStoreSnapshot() {
- mMockClock.currentTime = 1_600_000;
- mMockClock.realtime = 1000;
- mMockClock.uptime = 1000;
-
- prepareBatteryStats();
-
- mMockClock.realtime = 1_000_000;
- mMockClock.uptime = 1_000_000;
- mBatteryStats.resetAllStatsAndHistoryLocked(BatteryStatsImpl.RESET_REASON_ADB_COMMAND);
-
- final long[] timestamps = mBatteryUsageStatsStore.listBatteryUsageStatsTimestamps();
- assertThat(timestamps).hasLength(1);
- assertThat(timestamps[0]).isEqualTo(1_600_000);
-
- final BatteryUsageStats batteryUsageStats = mBatteryUsageStatsStore.loadBatteryUsageStats(
- 1_600_000);
- assertThat(batteryUsageStats.getStatsStartTimestamp()).isEqualTo(123);
- assertThat(batteryUsageStats.getStatsEndTimestamp()).isEqualTo(1_600_000);
- assertThat(batteryUsageStats.getBatteryCapacity()).isEqualTo(4000);
- assertThat(batteryUsageStats.getDischargePercentage()).isEqualTo(5);
- assertThat(batteryUsageStats.getDischargeDurationMs()).isEqualTo(1_000_000 - 1_000);
- assertThat(batteryUsageStats.getAggregateBatteryConsumer(
- BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE).getConsumedPower())
- .isEqualTo(600); // (3_600_000 - 3_000_000) / 1000
- }
-
- @Test
- public void testGarbageCollectOldSnapshots() throws Exception {
- prepareBatteryStats();
-
- mMockClock.realtime = 10_000_000;
- mMockClock.uptime = 10_000_000;
- mMockClock.currentTime = 10_000_000;
-
- final int snapshotFileSize = getSnapshotFileSize();
- final int numberOfSnapshots =
- (int) (MAX_BATTERY_STATS_SNAPSHOT_STORAGE_BYTES / snapshotFileSize);
- for (int i = 0; i < numberOfSnapshots + 2; i++) {
- mBatteryStats.resetAllStatsAndHistoryLocked(BatteryStatsImpl.RESET_REASON_ADB_COMMAND);
-
- mMockClock.realtime += 10_000_000;
- mMockClock.uptime += 10_000_000;
- mMockClock.currentTime += 10_000_000;
- prepareBatteryStats();
- }
-
- final long[] timestamps = mBatteryUsageStatsStore.listBatteryUsageStatsTimestamps();
- Arrays.sort(timestamps);
- assertThat(timestamps).hasLength(numberOfSnapshots);
- // Two snapshots (10_000_000 and 20_000_000) should have been discarded
- assertThat(timestamps[0]).isEqualTo(30_000_000);
- assertThat(getDirectorySize(mStoreDirectory))
- .isAtMost(MAX_BATTERY_STATS_SNAPSHOT_STORAGE_BYTES);
- }
-
- @Test
- public void testRemoveAllSnapshots() throws Exception {
- prepareBatteryStats();
-
- for (int i = 0; i < 3; i++) {
- mMockClock.realtime += 10_000_000;
- mMockClock.uptime += 10_000_000;
- mMockClock.currentTime += 10_000_000;
- prepareBatteryStats();
-
- mBatteryStats.resetAllStatsAndHistoryLocked(BatteryStatsImpl.RESET_REASON_ADB_COMMAND);
- }
-
- assertThat(getDirectorySize(mStoreDirectory)).isNotEqualTo(0);
-
- mBatteryUsageStatsStore.removeAllSnapshots();
-
- assertThat(getDirectorySize(mStoreDirectory)).isEqualTo(0);
- }
-
- @Test
- public void testSavingStatsdAtomPullTimestamp() {
- mBatteryUsageStatsStore.setLastBatteryUsageStatsBeforeResetAtomPullTimestamp(1234);
- assertThat(mBatteryUsageStatsStore.getLastBatteryUsageStatsBeforeResetAtomPullTimestamp())
- .isEqualTo(1234);
- mBatteryUsageStatsStore.setLastBatteryUsageStatsBeforeResetAtomPullTimestamp(5478);
- assertThat(mBatteryUsageStatsStore.getLastBatteryUsageStatsBeforeResetAtomPullTimestamp())
- .isEqualTo(5478);
- }
-
- private void prepareBatteryStats() {
- mBatteryStats.setBatteryStateLocked(BatteryManager.BATTERY_STATUS_DISCHARGING, 100,
- /* plugType */ 0, 90, 72, 3700, 3_600_000, 4_000_000, 0,
- mMockClock.realtime, mMockClock.uptime, mMockClock.currentTime);
- mBatteryStats.setBatteryStateLocked(BatteryManager.BATTERY_STATUS_DISCHARGING, 100,
- /* plugType */ 0, 85, 72, 3700, 3_000_000, 4_000_000, 0,
- mMockClock.realtime + 500_000, mMockClock.uptime + 500_000,
- mMockClock.currentTime + 500_000);
- }
-
- private void clearDirectory(File dir) {
- if (dir.exists()) {
- for (File child : dir.listFiles()) {
- if (child.isDirectory()) {
- clearDirectory(child);
- }
- child.delete();
- }
- }
- }
-
- private long getDirectorySize(File dir) {
- long size = 0;
- if (dir.exists()) {
- for (File child : dir.listFiles()) {
- if (child.isDirectory()) {
- size += getDirectorySize(child);
- } else {
- size += child.length();
- }
- }
- }
- return size;
- }
-
- private int getSnapshotFileSize() throws IOException {
- BatteryUsageStats stats = mBatteryUsageStatsProvider.getBatteryUsageStats(
- new BatteryUsageStatsQuery.Builder()
- .setMaxStatsAgeMs(0)
- .includePowerModels()
- .build());
- ByteArrayOutputStream out = new ByteArrayOutputStream();
- TypedXmlSerializer serializer = Xml.newBinarySerializer();
- serializer.setOutput(out, StandardCharsets.UTF_8.name());
- serializer.startDocument(null, true);
- stats.writeXml(serializer);
- serializer.endDocument();
- return out.toByteArray().length;
- }
-
- private static class TestHandler extends Handler {
- TestHandler() {
- super(Looper.getMainLooper());
- }
-
- @Override
- public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
- msg.getCallback().run();
- return true;
- }
- }
-}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/MultiStateStatsTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/MultiStateStatsTest.java
index 4ecee9f..30a73181 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/MultiStateStatsTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/MultiStateStatsTest.java
@@ -122,7 +122,7 @@
// 4 * 10 = 40 bits needed to represent the composite state
MultiStateStats.States[] states = new MultiStateStats.States[10];
for (int i = 0; i < states.length; i++) {
- states[i] = new MultiStateStats.States(true, labels);
+ states[i] = new MultiStateStats.States("foo", true, labels);
}
IllegalArgumentException e = assertThrows(IllegalArgumentException.class,
() -> new MultiStateStats.Factory(DIMENSION_COUNT, states));
@@ -191,8 +191,8 @@
private static MultiStateStats.Factory makeFactory(boolean trackBatteryState,
boolean trackProcState, boolean trackScreenState) {
return new MultiStateStats.Factory(DIMENSION_COUNT,
- new MultiStateStats.States(trackBatteryState, "plugged-in", "on-battery"),
- new MultiStateStats.States(trackProcState,
+ new MultiStateStats.States("bs", trackBatteryState, "plugged-in", "on-battery"),
+ new MultiStateStats.States("ps", trackProcState,
BatteryConsumer.processStateToString(
BatteryConsumer.PROCESS_STATE_UNSPECIFIED),
BatteryConsumer.processStateToString(
@@ -203,7 +203,7 @@
BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE),
BatteryConsumer.processStateToString(
BatteryConsumer.PROCESS_STATE_CACHED)),
- new MultiStateStats.States(trackScreenState, "screen-off", "plugged-in"));
+ new MultiStateStats.States("scr", trackScreenState, "screen-off", "plugged-in"));
}
private FactorySubject assertThatCpuPerformanceStatsFactory(
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsAggregatorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsAggregatorTest.java
index 47de443..b52fc8a 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsAggregatorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsAggregatorTest.java
@@ -24,11 +24,14 @@
import android.os.BatteryConsumer;
import android.os.BatteryStats;
import android.os.PersistableBundle;
+import android.text.format.DateFormat;
+import androidx.annotation.NonNull;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.os.BatteryStatsHistory;
+import com.android.internal.os.MonotonicClock;
import com.android.internal.os.PowerStats;
import org.junit.Before;
@@ -36,16 +39,19 @@
import org.junit.runner.RunWith;
import java.text.ParseException;
-import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.List;
+import java.util.TimeZone;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class PowerStatsAggregatorTest {
private static final int TEST_POWER_COMPONENT = 77;
private static final int TEST_UID = 42;
+ private static final long START_TIME = 1234;
private final MockClock mClock = new MockClock();
- private long mStartTime;
+ private final MonotonicClock mMonotonicClock = new MonotonicClock(START_TIME, mClock);
private BatteryStatsHistory mHistory;
private PowerStatsAggregator mAggregator;
private int mAggregatedStatsCount;
@@ -53,25 +59,25 @@
@Before
public void setup() throws ParseException {
mHistory = new BatteryStatsHistory(32, 1024,
- mock(BatteryStatsHistory.HistoryStepDetailsCalculator.class), mClock);
- mStartTime = new SimpleDateFormat("yyyy-MM-dd HH:mm")
- .parse("2008-09-23 08:00").getTime();
- mClock.currentTime = mStartTime;
+ mock(BatteryStatsHistory.HistoryStepDetailsCalculator.class), mClock,
+ mMonotonicClock);
- PowerStatsAggregator.Builder builder = new PowerStatsAggregator.Builder(mHistory);
- builder.trackPowerComponent(TEST_POWER_COMPONENT)
+ AggregatedPowerStatsConfig config = new AggregatedPowerStatsConfig();
+ config.trackPowerComponent(TEST_POWER_COMPONENT)
.trackDeviceStates(
- PowerStatsAggregator.STATE_POWER,
- PowerStatsAggregator.STATE_SCREEN)
+ AggregatedPowerStatsConfig.STATE_POWER,
+ AggregatedPowerStatsConfig.STATE_SCREEN)
.trackUidStates(
- PowerStatsAggregator.STATE_POWER,
- PowerStatsAggregator.STATE_SCREEN,
- PowerStatsAggregator.STATE_PROCESS_STATE);
- mAggregator = builder.build();
+ AggregatedPowerStatsConfig.STATE_POWER,
+ AggregatedPowerStatsConfig.STATE_SCREEN,
+ AggregatedPowerStatsConfig.STATE_PROCESS_STATE);
+ mAggregator = new PowerStatsAggregator(config, mHistory);
}
@Test
public void stateUpdates() {
+ mClock.currentTime = 1222156800000L; // An important date in world history
+
mHistory.forceRecordAllHistory();
mHistory.recordBatteryState(mClock.realtime, mClock.uptime, 10, /* plugged */ true);
mHistory.recordStateStartEvent(mClock.realtime, mClock.uptime,
@@ -98,15 +104,32 @@
mHistory.recordProcessStateChange(mClock.realtime, mClock.uptime, TEST_UID,
BatteryConsumer.PROCESS_STATE_BACKGROUND);
- advance(3000);
+ advance(1000);
+
+ mClock.currentTime += 60 * 60 * 1000; // one hour
+ mHistory.recordCurrentTimeChange(mClock.realtime, mClock.uptime, mClock.currentTime);
+
+ advance(2000);
powerStats.stats = new long[]{20000};
powerStats.uidStats.put(TEST_UID, new long[]{4444});
mHistory.recordPowerStats(mClock.realtime, mClock.uptime, powerStats);
- mAggregator.aggregateBatteryStats(0, 0, stats -> {
+ mAggregator.aggregatePowerStats(0, 0, stats -> {
assertThat(mAggregatedStatsCount++).isEqualTo(0);
- assertThat(stats.getStartTime()).isEqualTo(mStartTime);
+ assertThat(stats.getStartTime()).isEqualTo(START_TIME);
+
+ List<AggregatedPowerStats.ClockUpdate> clockUpdates = stats.getClockUpdates();
+ assertThat(clockUpdates).hasSize(2);
+
+ AggregatedPowerStats.ClockUpdate clockUpdate0 = clockUpdates.get(0);
+ assertThat(clockUpdate0.monotonicTime).isEqualTo(1234);
+ assertThat(formatDateTime(clockUpdate0.currentTime)).isEqualTo("2008-09-23 08:00:00");
+
+ AggregatedPowerStats.ClockUpdate clockUpdate1 = clockUpdates.get(1);
+ assertThat(clockUpdate1.monotonicTime).isEqualTo(1234 + 3000);
+ assertThat(formatDateTime(clockUpdate1.currentTime)).isEqualTo("2008-09-23 09:00:03");
+
assertThat(stats.getDuration()).isEqualTo(5000);
long[] values = new long[1];
@@ -115,40 +138,47 @@
TEST_POWER_COMPONENT);
assertThat(powerComponentStats.getDeviceStats(values, new int[]{
- PowerStatsAggregator.POWER_STATE_OTHER,
- PowerStatsAggregator.SCREEN_STATE_ON}))
+ AggregatedPowerStatsConfig.POWER_STATE_OTHER,
+ AggregatedPowerStatsConfig.SCREEN_STATE_ON}))
.isTrue();
assertThat(values).isEqualTo(new long[]{10000});
assertThat(powerComponentStats.getDeviceStats(values, new int[]{
- PowerStatsAggregator.POWER_STATE_BATTERY,
- PowerStatsAggregator.SCREEN_STATE_OTHER}))
+ AggregatedPowerStatsConfig.POWER_STATE_BATTERY,
+ AggregatedPowerStatsConfig.SCREEN_STATE_OTHER}))
.isTrue();
assertThat(values).isEqualTo(new long[]{20000});
assertThat(powerComponentStats.getUidStats(values, TEST_UID, new int[]{
- PowerStatsAggregator.POWER_STATE_OTHER,
- PowerStatsAggregator.SCREEN_STATE_ON,
+ AggregatedPowerStatsConfig.POWER_STATE_OTHER,
+ AggregatedPowerStatsConfig.SCREEN_STATE_ON,
BatteryConsumer.PROCESS_STATE_FOREGROUND}))
.isTrue();
assertThat(values).isEqualTo(new long[]{1234});
assertThat(powerComponentStats.getUidStats(values, TEST_UID, new int[]{
- PowerStatsAggregator.POWER_STATE_BATTERY,
- PowerStatsAggregator.SCREEN_STATE_OTHER,
+ AggregatedPowerStatsConfig.POWER_STATE_BATTERY,
+ AggregatedPowerStatsConfig.SCREEN_STATE_OTHER,
BatteryConsumer.PROCESS_STATE_FOREGROUND}))
.isTrue();
assertThat(values).isEqualTo(new long[]{1111});
assertThat(powerComponentStats.getUidStats(values, TEST_UID, new int[]{
- PowerStatsAggregator.POWER_STATE_BATTERY,
- PowerStatsAggregator.SCREEN_STATE_OTHER,
+ AggregatedPowerStatsConfig.POWER_STATE_BATTERY,
+ AggregatedPowerStatsConfig.SCREEN_STATE_OTHER,
BatteryConsumer.PROCESS_STATE_BACKGROUND}))
.isTrue();
assertThat(values).isEqualTo(new long[]{3333});
});
}
+ @NonNull
+ private static CharSequence formatDateTime(long timeInMillis) {
+ Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
+ cal.setTimeInMillis(timeInMillis);
+ return DateFormat.format("yyyy-MM-dd hh:mm:ss", cal);
+ }
+
@Test
public void incompatiblePowerStats() {
mHistory.forceRecordAllHistory();
@@ -181,39 +211,39 @@
mHistory.recordBatteryState(mClock.realtime, mClock.uptime, 50, /* plugged */ true);
- mAggregator.aggregateBatteryStats(0, 0, stats -> {
+ mAggregator.aggregatePowerStats(0, 0, stats -> {
long[] values = new long[1];
PowerComponentAggregatedPowerStats powerComponentStats =
stats.getPowerComponentStats(TEST_POWER_COMPONENT);
if (mAggregatedStatsCount == 0) {
- assertThat(stats.getStartTime()).isEqualTo(mStartTime);
+ assertThat(stats.getStartTime()).isEqualTo(START_TIME);
assertThat(stats.getDuration()).isEqualTo(2000);
assertThat(powerComponentStats.getDeviceStats(values, new int[]{
- PowerStatsAggregator.POWER_STATE_OTHER,
- PowerStatsAggregator.SCREEN_STATE_ON}))
+ AggregatedPowerStatsConfig.POWER_STATE_OTHER,
+ AggregatedPowerStatsConfig.SCREEN_STATE_ON}))
.isTrue();
assertThat(values).isEqualTo(new long[]{10000});
assertThat(powerComponentStats.getUidStats(values, TEST_UID, new int[]{
- PowerStatsAggregator.POWER_STATE_OTHER,
- PowerStatsAggregator.SCREEN_STATE_ON,
+ AggregatedPowerStatsConfig.POWER_STATE_OTHER,
+ AggregatedPowerStatsConfig.SCREEN_STATE_ON,
BatteryConsumer.PROCESS_STATE_FOREGROUND}))
.isTrue();
assertThat(values).isEqualTo(new long[]{1234});
} else if (mAggregatedStatsCount == 1) {
- assertThat(stats.getStartTime()).isEqualTo(mStartTime + 2000);
+ assertThat(stats.getStartTime()).isEqualTo(START_TIME + 2000);
assertThat(stats.getDuration()).isEqualTo(1000);
assertThat(powerComponentStats.getDeviceStats(values, new int[]{
- PowerStatsAggregator.POWER_STATE_BATTERY,
- PowerStatsAggregator.SCREEN_STATE_ON}))
+ AggregatedPowerStatsConfig.POWER_STATE_BATTERY,
+ AggregatedPowerStatsConfig.SCREEN_STATE_ON}))
.isTrue();
assertThat(values).isEqualTo(new long[]{20000});
assertThat(powerComponentStats.getUidStats(values, TEST_UID, new int[]{
- PowerStatsAggregator.POWER_STATE_BATTERY,
- PowerStatsAggregator.SCREEN_STATE_ON,
+ AggregatedPowerStatsConfig.POWER_STATE_BATTERY,
+ AggregatedPowerStatsConfig.SCREEN_STATE_ON,
BatteryConsumer.PROCESS_STATE_FOREGROUND}))
.isTrue();
assertThat(values).isEqualTo(new long[]{4444});
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsSchedulerTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsSchedulerTest.java
new file mode 100644
index 0000000..0e58787
--- /dev/null
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsSchedulerTest.java
@@ -0,0 +1,269 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.power.stats;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.os.BatteryConsumer;
+import android.os.BatteryManager;
+import android.os.BatteryUsageStats;
+import android.os.ConditionVariable;
+import android.os.Handler;
+import android.os.HandlerThread;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.os.MonotonicClock;
+import com.android.internal.os.PowerProfile;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.time.Duration;
+import java.time.Instant;
+import java.util.List;
+import java.util.TimeZone;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
+
+@RunWith(AndroidJUnit4.class)
+public class PowerStatsSchedulerTest {
+ private PowerStatsStore mPowerStatsStore;
+ private Handler mHandler;
+ private MockClock mClock = new MockClock();
+ private MonotonicClock mMonotonicClock = new MonotonicClock(0, mClock);
+ private MockBatteryStatsImpl mBatteryStats;
+ private BatteryUsageStatsProvider mBatteryUsageStatsProvider;
+ private PowerStatsScheduler mPowerStatsScheduler;
+ private PowerProfile mPowerProfile;
+ private PowerStatsAggregator mPowerStatsAggregator;
+ private AggregatedPowerStatsConfig mAggregatedPowerStatsConfig;
+
+ @Before
+ public void setup() {
+ final Context context = InstrumentationRegistry.getContext();
+
+ TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
+
+ mClock.currentTime = Instant.parse("2023-01-02T03:04:05.00Z").toEpochMilli();
+ mClock.realtime = 7654321;
+
+ HandlerThread bgThread = new HandlerThread("bg thread");
+ bgThread.start();
+ File systemDir = context.getCacheDir();
+ mHandler = new Handler(bgThread.getLooper());
+ mAggregatedPowerStatsConfig = new AggregatedPowerStatsConfig();
+ mPowerStatsStore = new PowerStatsStore(systemDir, mHandler, mAggregatedPowerStatsConfig);
+ mPowerProfile = mock(PowerProfile.class);
+ when(mPowerProfile.getAveragePower(PowerProfile.POWER_FLASHLIGHT)).thenReturn(1000000.0);
+ mBatteryStats = new MockBatteryStatsImpl(mClock).setPowerProfile(mPowerProfile);
+ mBatteryUsageStatsProvider = new BatteryUsageStatsProvider(context, mBatteryStats);
+ mPowerStatsAggregator = mock(PowerStatsAggregator.class);
+ mPowerStatsScheduler = new PowerStatsScheduler(context, mPowerStatsAggregator,
+ TimeUnit.MINUTES.toMillis(30), TimeUnit.HOURS.toMillis(1), mPowerStatsStore, mClock,
+ mMonotonicClock, mHandler, mBatteryStats, mBatteryUsageStatsProvider);
+ }
+
+ @Test
+ @SuppressWarnings("unchecked")
+ public void storeAggregatePowerStats() {
+ mPowerStatsStore.reset();
+
+ assertThat(mPowerStatsStore.getTableOfContents()).isEmpty();
+
+ mPowerStatsStore.storeAggregatedPowerStats(
+ createAggregatedPowerStats(mMonotonicClock.monotonicTime(), mClock.currentTime,
+ 123));
+
+ long delayBeforeAggregating = TimeUnit.MINUTES.toMillis(90);
+ mClock.realtime += delayBeforeAggregating;
+ mClock.currentTime += delayBeforeAggregating;
+
+ doAnswer(invocation -> {
+ // The first span is longer than 30 min, because the end time is being aligned with
+ // the wall clock. Subsequent spans should be precisely 30 minutes.
+ long startTime = invocation.getArgument(0);
+ long endTime = invocation.getArgument(1);
+ Consumer<AggregatedPowerStats> consumer = invocation.getArgument(2);
+
+ long startTimeWallClock =
+ mClock.currentTime - (mMonotonicClock.monotonicTime() - startTime);
+ long endTimeWallClock =
+ mClock.currentTime - (mMonotonicClock.monotonicTime() - endTime);
+
+ assertThat(startTime).isEqualTo(7654321 + 123);
+ assertThat(endTime - startTime).isAtLeast(TimeUnit.MINUTES.toMillis(30));
+ assertThat(Instant.ofEpochMilli(endTimeWallClock))
+ .isEqualTo(Instant.parse("2023-01-02T04:00:00Z"));
+
+ consumer.accept(
+ createAggregatedPowerStats(startTime, startTimeWallClock, endTime - startTime));
+ return null;
+ }).doAnswer(invocation -> {
+ long startTime = invocation.getArgument(0);
+ long endTime = invocation.getArgument(1);
+ Consumer<AggregatedPowerStats> consumer = invocation.getArgument(2);
+
+ long startTimeWallClock =
+ mClock.currentTime - (mMonotonicClock.monotonicTime() - startTime);
+ long endTimeWallClock =
+ mClock.currentTime - (mMonotonicClock.monotonicTime() - endTime);
+
+ assertThat(Instant.ofEpochMilli(startTimeWallClock))
+ .isEqualTo(Instant.parse("2023-01-02T04:00:00Z"));
+ assertThat(Instant.ofEpochMilli(endTimeWallClock))
+ .isEqualTo(Instant.parse("2023-01-02T04:30:00Z"));
+
+ consumer.accept(
+ createAggregatedPowerStats(startTime, startTimeWallClock, endTime - startTime));
+ return null;
+ }).when(mPowerStatsAggregator).aggregatePowerStats(anyLong(), anyLong(),
+ any(Consumer.class));
+
+ mPowerStatsScheduler.schedulePowerStatsAggregation();
+ ConditionVariable done = new ConditionVariable();
+ mHandler.post(done::open);
+ done.block();
+
+ verify(mPowerStatsAggregator, times(2))
+ .aggregatePowerStats(anyLong(), anyLong(), any(Consumer.class));
+
+ List<PowerStatsSpan.Metadata> contents = mPowerStatsStore.getTableOfContents();
+ assertThat(contents).hasSize(3);
+ // Skip the first entry, which was placed in the store at the beginning of this test
+ PowerStatsSpan.TimeFrame timeFrame1 = contents.get(1).getTimeFrames().get(0);
+ PowerStatsSpan.TimeFrame timeFrame2 = contents.get(2).getTimeFrames().get(0);
+ assertThat(timeFrame1.startMonotonicTime).isEqualTo(7654321 + 123);
+ assertThat(timeFrame2.startMonotonicTime)
+ .isEqualTo(timeFrame1.startMonotonicTime + timeFrame1.duration);
+ assertThat(Instant.ofEpochMilli(timeFrame2.startTime))
+ .isEqualTo(Instant.parse("2023-01-02T04:00:00Z"));
+ assertThat(Duration.ofMillis(timeFrame2.duration)).isEqualTo(Duration.ofMinutes(30));
+ }
+
+ private AggregatedPowerStats createAggregatedPowerStats(long monotonicTime, long currentTime,
+ long duration) {
+ AggregatedPowerStats stats = new AggregatedPowerStats(mAggregatedPowerStatsConfig);
+ stats.addClockUpdate(monotonicTime, currentTime);
+ stats.setDuration(duration);
+ return stats;
+ }
+
+ @Test
+ public void storeBatteryUsageStatsOnReset() {
+ mBatteryStats.forceRecordAllHistory();
+ synchronized (mBatteryStats) {
+ mBatteryStats.setOnBatteryLocked(mClock.realtime, mClock.uptime, true,
+ BatteryManager.BATTERY_STATUS_DISCHARGING, 50, 0);
+ }
+
+ mPowerStatsScheduler.start(/* schedulePeriodicPowerStatsCollection */false);
+
+ assertThat(mPowerStatsStore.getTableOfContents()).isEmpty();
+
+ mPowerStatsScheduler.start(true);
+
+ synchronized (mBatteryStats) {
+ mBatteryStats.noteFlashlightOnLocked(42, mClock.realtime, mClock.uptime);
+ }
+
+ mClock.realtime += 60000;
+ mClock.currentTime += 60000;
+
+ synchronized (mBatteryStats) {
+ mBatteryStats.noteFlashlightOffLocked(42, mClock.realtime, mClock.uptime);
+ }
+
+ mClock.realtime += 60000;
+ mClock.currentTime += 60000;
+
+ // Battery stats reset should have the side-effect of saving accumulated battery usage stats
+ synchronized (mBatteryStats) {
+ mBatteryStats.resetAllStatsAndHistoryLocked(BatteryStatsImpl.RESET_REASON_ADB_COMMAND);
+ }
+
+ // Await completion
+ ConditionVariable done = new ConditionVariable();
+ mHandler.post(done::open);
+ done.block();
+
+ List<PowerStatsSpan.Metadata> contents = mPowerStatsStore.getTableOfContents();
+ assertThat(contents).hasSize(1);
+
+ PowerStatsSpan.Metadata metadata = contents.get(0);
+
+ PowerStatsSpan span = mPowerStatsStore.loadPowerStatsSpan(metadata.getId(),
+ BatteryUsageStatsSection.TYPE);
+ assertThat(span).isNotNull();
+
+ List<PowerStatsSpan.TimeFrame> timeFrames = span.getMetadata().getTimeFrames();
+ assertThat(timeFrames).hasSize(1);
+ assertThat(timeFrames.get(0).startMonotonicTime).isEqualTo(7654321);
+ assertThat(timeFrames.get(0).duration).isEqualTo(120000);
+
+ List<PowerStatsSpan.Section> sections = span.getSections();
+ assertThat(sections).hasSize(1);
+
+ PowerStatsSpan.Section section = sections.get(0);
+ assertThat(section.getType()).isEqualTo(BatteryUsageStatsSection.TYPE);
+ BatteryUsageStats bus = ((BatteryUsageStatsSection) section).getBatteryUsageStats();
+ assertThat(bus.getAggregateBatteryConsumer(
+ BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE)
+ .getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_FLASHLIGHT))
+ .isEqualTo(60000);
+ }
+
+ @Test
+ public void alignToWallClock() {
+ // Expect the aligned value to be adjusted by 1 min 30 sec - rounded to the next 15 min
+ assertThat(PowerStatsScheduler.alignToWallClock(123, TimeUnit.MINUTES.toMillis(15),
+ 123 + TimeUnit.HOURS.toMillis(2),
+ Instant.parse("2007-12-03T10:13:30.00Z").toEpochMilli())).isEqualTo(
+ 123 + Duration.parse("PT1M30S").toMillis());
+
+ // Expect the aligned value to be adjusted by 2 min 45 sec - rounded to the next 15 min
+ assertThat(PowerStatsScheduler.alignToWallClock(123, TimeUnit.MINUTES.toMillis(15),
+ 123 + TimeUnit.HOURS.toMillis(2),
+ Instant.parse("2007-12-03T10:57:15.00Z").toEpochMilli())).isEqualTo(
+ 123 + Duration.parse("PT2M45S").toMillis());
+
+ // Expect the aligned value to be adjusted by 15 sec - rounded to the next 1 min
+ assertThat(PowerStatsScheduler.alignToWallClock(123, TimeUnit.MINUTES.toMillis(1),
+ 123 + TimeUnit.HOURS.toMillis(2),
+ Instant.parse("2007-12-03T10:14:45.00Z").toEpochMilli())).isEqualTo(
+ 123 + Duration.parse("PT15S").toMillis());
+
+ // Expect the aligned value to be adjusted by 1 hour 46 min 30 sec -
+ // rounded to the next 3 hours
+ assertThat(PowerStatsScheduler.alignToWallClock(123, TimeUnit.HOURS.toMillis(3),
+ 123 + TimeUnit.HOURS.toMillis(9),
+ Instant.parse("2007-12-03T10:13:30.00Z").toEpochMilli())).isEqualTo(
+ 123 + Duration.parse("PT1H46M30S").toMillis());
+ }
+}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsStoreTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsStoreTest.java
new file mode 100644
index 0000000..d3628b5
--- /dev/null
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsStoreTest.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.power.stats;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.modules.utils.TypedXmlPullParser;
+import com.android.modules.utils.TypedXmlSerializer;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+
+@RunWith(AndroidJUnit4.class)
+@SuppressWarnings("GuardedBy")
+public class PowerStatsStoreTest {
+ private static final long MAX_BATTERY_STATS_SNAPSHOT_STORAGE_BYTES = 2 * 1024;
+
+ private PowerStatsStore mPowerStatsStore;
+ private File mStoreDirectory;
+
+ @Before
+ public void setup() {
+ Context context = InstrumentationRegistry.getContext();
+
+ mStoreDirectory = new File(context.getCacheDir(), "PowerStatsStoreTest");
+ clearDirectory(mStoreDirectory);
+
+ mPowerStatsStore = new PowerStatsStore(mStoreDirectory,
+ MAX_BATTERY_STATS_SNAPSHOT_STORAGE_BYTES,
+ new TestHandler(),
+ (sectionType, parser) -> {
+ if (sectionType.equals(TestSection.TYPE)) {
+ return TestSection.readXml(parser);
+ }
+ return null;
+ });
+ }
+
+ @Test
+ public void garbageCollectOldSpans() throws Exception {
+ int spanSize = 500;
+ final int numberOfSnaps =
+ (int) (MAX_BATTERY_STATS_SNAPSHOT_STORAGE_BYTES / spanSize);
+ for (int i = 0; i < numberOfSnaps + 2; i++) {
+ PowerStatsSpan span = new PowerStatsSpan(i);
+ span.addSection(new TestSection(i, spanSize));
+ mPowerStatsStore.storePowerStatsSpan(span);
+ }
+
+ assertThat(getDirectorySize(mStoreDirectory))
+ .isAtMost(MAX_BATTERY_STATS_SNAPSHOT_STORAGE_BYTES);
+
+ List<PowerStatsSpan.Metadata> toc = mPowerStatsStore.getTableOfContents();
+ assertThat(toc.size()).isLessThan(numberOfSnaps);
+ int minPreservedSpanId = numberOfSnaps - toc.size();
+ for (PowerStatsSpan.Metadata metadata : toc) {
+ assertThat(metadata.getId()).isAtLeast(minPreservedSpanId);
+ }
+ }
+
+ @Test
+ public void reset() throws Exception {
+ for (int i = 0; i < 3; i++) {
+ PowerStatsSpan span = new PowerStatsSpan(i);
+ span.addSection(new TestSection(i, 42));
+ mPowerStatsStore.storePowerStatsSpan(span);
+ }
+
+ assertThat(getDirectorySize(mStoreDirectory)).isNotEqualTo(0);
+
+ mPowerStatsStore.reset();
+
+ assertThat(getDirectorySize(mStoreDirectory)).isEqualTo(0);
+ }
+
+ private void clearDirectory(File dir) {
+ if (dir.exists()) {
+ for (File child : dir.listFiles()) {
+ if (child.isDirectory()) {
+ clearDirectory(child);
+ }
+ child.delete();
+ }
+ }
+ }
+
+ private long getDirectorySize(File dir) {
+ long size = 0;
+ if (dir.exists()) {
+ for (File child : dir.listFiles()) {
+ if (child.isDirectory()) {
+ size += getDirectorySize(child);
+ } else {
+ size += child.length();
+ }
+ }
+ }
+ return size;
+ }
+
+ private static class TestSection extends PowerStatsSpan.Section {
+ public static final String TYPE = "much-text";
+
+ private final int mSize;
+ private final int mValue;
+
+ TestSection(int value, int size) {
+ super(TYPE);
+ mSize = size;
+ mValue = value;
+ }
+
+ @Override
+ void write(TypedXmlSerializer serializer) throws IOException {
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < mSize; i++) {
+ sb.append("X");
+ }
+ serializer.startTag(null, "much-text");
+ serializer.attributeInt(null, "value", mValue);
+ serializer.text(sb.toString());
+ serializer.endTag(null, "much-text");
+ }
+
+ public static TestSection readXml(TypedXmlPullParser parser) throws XmlPullParserException {
+ TestSection section = null;
+ int eventType = parser.getEventType();
+ while (eventType != XmlPullParser.END_DOCUMENT
+ && !(eventType == XmlPullParser.END_TAG
+ && parser.getName().equals("much-text"))) {
+ if (eventType == XmlPullParser.START_TAG && parser.getName().equals("much-text")) {
+ section = new TestSection(parser.getAttributeInt(null, "value"), 0);
+ }
+ }
+ return section;
+ }
+ }
+
+ private static class TestHandler extends Handler {
+ TestHandler() {
+ super(Looper.getMainLooper());
+ }
+
+ @Override
+ public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
+ msg.getCallback().run();
+ return true;
+ }
+ }
+}
diff --git a/services/tests/powerstatstests/src/com/android/server/powerstats/PowerStatsServiceTest.java b/services/tests/powerstatstests/src/com/android/server/powerstats/PowerStatsServiceTest.java
index 2ffe4aa..df46054 100644
--- a/services/tests/powerstatstests/src/com/android/server/powerstats/PowerStatsServiceTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/powerstats/PowerStatsServiceTest.java
@@ -1079,7 +1079,7 @@
GetSupportedPowerMonitorsResult result = new GetSupportedPowerMonitorsResult();
mService.getSupportedPowerMonitorsImpl(result);
assertThat(result.powerMonitors).isNotNull();
- assertThat(Arrays.stream(result.powerMonitors).map(pm -> pm.name).toList())
+ assertThat(Arrays.stream(result.powerMonitors).map(PowerMonitor::getName).toList())
.containsAtLeast(
"energyconsumer0",
"BLUETOOTH/1",
@@ -1130,7 +1130,7 @@
mService.getSupportedPowerMonitorsImpl(supportedPowerMonitorsResult);
Map<String, PowerMonitor> map =
Arrays.stream(supportedPowerMonitorsResult.powerMonitors)
- .collect(Collectors.toMap(pm -> pm.name, pm -> pm));
+ .collect(Collectors.toMap(PowerMonitor::getName, pm -> pm));
PowerMonitor consumer1 = map.get("energyconsumer0");
PowerMonitor consumer2 = map.get("BLUETOOTH/1");
PowerMonitor measurement1 = map.get("[channelname0]:channelsubsystem0");
@@ -1196,6 +1196,6 @@
supportedPowerMonitorsResult = new GetSupportedPowerMonitorsResult();
mService.getSupportedPowerMonitorsImpl(supportedPowerMonitorsResult);
assertThat(Arrays.stream(supportedPowerMonitorsResult.powerMonitors)
- .map(pm -> pm.name).toList()).contains("energyconsumer0");
+ .map(PowerMonitor::getName).toList()).contains("energyconsumer0");
}
}
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/gestures/TouchExplorerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java
index 1cd61e9..efcdbd4 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java
@@ -44,6 +44,10 @@
import android.graphics.PointF;
import android.os.Looper;
import android.os.SystemClock;
+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.testing.DexmakerShareClassLoaderRule;
import android.view.InputDevice;
import android.view.MotionEvent;
@@ -56,6 +60,7 @@
import com.android.server.accessibility.AccessibilityManagerService;
import com.android.server.accessibility.AccessibilityTraceManager;
import com.android.server.accessibility.EventStreamTransformation;
+import com.android.server.accessibility.Flags;
import com.android.server.accessibility.utils.GestureLogParser;
import com.android.server.testutils.OffsettableClock;
@@ -76,6 +81,7 @@
import java.util.ArrayList;
import java.util.List;
+
@RunWith(AndroidJUnit4.class)
public class TouchExplorerTest {
@@ -119,6 +125,9 @@
public final DexmakerShareClassLoaderRule mDexmakerShareClassLoaderRule =
new DexmakerShareClassLoaderRule();
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
/**
* {@link TouchExplorer#sendDownForAllNotInjectedPointers} injecting events with the same object
* is resulting {@link ArgumentCaptor} to capture events with last state. Before implementation
@@ -161,11 +170,16 @@
goFromStateClearTo(STATE_TOUCH_EXPLORING_1FINGER);
// Wait for transiting to touch exploring state.
mHandler.fastForward(2 * USER_INTENT_TIMEOUT);
- moveEachPointers(mLastEvent, p(10, 10));
- send(mLastEvent);
+ assertState(STATE_TOUCH_EXPLORING);
+ // Manually construct the next move event. Using moveEachPointers() will batch the move
+ // event which produces zero movement for some reason.
+ float[] x = new float[1];
+ float[] y = new float[1];
+ x[0] = mLastEvent.getX(0) + mTouchSlop;
+ y[0] = mLastEvent.getY(0) + mTouchSlop;
+ send(manyPointerEvent(ACTION_MOVE, x, y));
goToStateClearFrom(STATE_TOUCH_EXPLORING_1FINGER);
assertCapturedEvents(ACTION_HOVER_ENTER, ACTION_HOVER_MOVE, ACTION_HOVER_EXIT);
- assertState(STATE_TOUCH_EXPLORING);
}
/**
@@ -173,7 +187,8 @@
* change the coordinates.
*/
@Test
- public void testOneFingerMoveWithExtraMoveEvents() {
+ @RequiresFlagsEnabled(Flags.FLAG_REDUCE_TOUCH_EXPLORATION_SENSITIVITY)
+ public void testOneFingerMoveWithExtraMoveEvents_generatesOneMoveEvent() {
goFromStateClearTo(STATE_TOUCH_EXPLORING_1FINGER);
// Inject a set of move events that have the same coordinates as the down event.
moveEachPointers(mLastEvent, p(0, 0));
@@ -181,7 +196,33 @@
// Wait for transition to touch exploring state.
mHandler.fastForward(2 * USER_INTENT_TIMEOUT);
// Now move for real.
- moveEachPointers(mLastEvent, p(10, 10));
+ moveAtLeastTouchSlop(mLastEvent);
+ send(mLastEvent);
+ // One more move event with no change.
+ moveEachPointers(mLastEvent, p(0, 0));
+ send(mLastEvent);
+ goToStateClearFrom(STATE_TOUCH_EXPLORING_1FINGER);
+ assertCapturedEvents(
+ ACTION_HOVER_ENTER,
+ ACTION_HOVER_MOVE,
+ ACTION_HOVER_EXIT);
+ }
+
+ /**
+ * Test the case where ACTION_DOWN is followed by a number of ACTION_MOVE events that do not
+ * change the coordinates.
+ */
+ @Test
+ @RequiresFlagsDisabled(Flags.FLAG_REDUCE_TOUCH_EXPLORATION_SENSITIVITY)
+ public void testOneFingerMoveWithExtraMoveEvents_generatesThreeMoveEvent() {
+ goFromStateClearTo(STATE_TOUCH_EXPLORING_1FINGER);
+ // Inject a set of move events that have the same coordinates as the down event.
+ moveEachPointers(mLastEvent, p(0, 0));
+ send(mLastEvent);
+ // Wait for transition to touch exploring state.
+ mHandler.fastForward(2 * USER_INTENT_TIMEOUT);
+ // Now move for real.
+ moveAtLeastTouchSlop(mLastEvent);
send(mLastEvent);
// One more move event with no change.
moveEachPointers(mLastEvent, p(0, 0));
@@ -242,7 +283,7 @@
moveEachPointers(mLastEvent, p(0, 0), p(0, 0));
send(mLastEvent);
// Now move for real.
- moveEachPointers(mLastEvent, p(10, 10), p(10, 10));
+ moveEachPointers(mLastEvent, p(mTouchSlop, mTouchSlop), p(mTouchSlop, mTouchSlop));
send(mLastEvent);
goToStateClearFrom(STATE_DRAGGING_2FINGERS);
assertCapturedEvents(ACTION_DOWN, ACTION_MOVE, ACTION_MOVE, ACTION_MOVE, ACTION_UP);
@@ -251,7 +292,7 @@
@Test
public void testUpEvent_OneFingerMove_clearStateAndInjectHoverEvents() {
goFromStateClearTo(STATE_TOUCH_EXPLORING_1FINGER);
- moveEachPointers(mLastEvent, p(10, 10));
+ moveAtLeastTouchSlop(mLastEvent);
send(mLastEvent);
// Wait 10 ms to make sure that hover enter and exit are not scheduled for the same moment.
mHandler.fastForward(10);
@@ -277,7 +318,7 @@
// Wait for the finger moving to the second view.
mHandler.fastForward(oneThirdUserIntentTimeout);
- moveEachPointers(mLastEvent, p(10, 10));
+ moveAtLeastTouchSlop(mLastEvent);
send(mLastEvent);
// Wait for the finger lifting from the second view.
@@ -402,7 +443,6 @@
// Manually construct the next move event. Using moveEachPointers() will batch the move
// event onto the pointer up event which will mean that the move event still has a pointer
// count of 3.
- // Todo: refactor to avoid using batching as there is no special reason to do it that way.
float[] x = new float[2];
float[] y = new float[2];
x[0] = mLastEvent.getX(0) + 100;
@@ -734,6 +774,9 @@
}
}
+ private void moveAtLeastTouchSlop(MotionEvent event) {
+ moveEachPointers(event, p(2 * mTouchSlop, 0));
+ }
/**
* A {@link android.os.Handler} that doesn't process messages until {@link #fastForward(int)} is
* invoked.
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/AnrTimerTest.java b/services/tests/servicestests/src/com/android/server/am/AnrTimerTest.java
index 70527ce..44d6760 100644
--- a/services/tests/servicestests/src/com/android/server/am/AnrTimerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/AnrTimerTest.java
@@ -238,7 +238,7 @@
}
@Override
- Handler getHandler(Handler.Callback callback) {
+ Handler newHandler(Handler.Callback callback) {
if (mTestHandler == null) {
mTestHandler = new TestHandler(mHandler.getLooper(), callback, mImmediate);
}
@@ -250,14 +250,18 @@
return mTestHandler;
}
+ /**
+ * This override returns the tracker supplied in the constructor. It does not create a
+ * new one.
+ */
@Override
- AnrTimer.CpuTracker getTracker() {
+ AnrTimer.CpuTracker newTracker() {
return mTracker;
}
/** For test purposes, always enable the feature. */
@Override
- boolean getFeatureEnabled() {
+ boolean isFeatureEnabled() {
return true;
}
}
diff --git a/services/tests/servicestests/src/com/android/server/am/BatteryStatsServiceTest.java b/services/tests/servicestests/src/com/android/server/am/BatteryStatsServiceTest.java
index 988cd81..feb6bd9 100644
--- a/services/tests/servicestests/src/com/android/server/am/BatteryStatsServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/BatteryStatsServiceTest.java
@@ -16,6 +16,8 @@
package com.android.server.am;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.junit.Assert.assertTrue;
import android.content.Context;
@@ -34,6 +36,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.io.File;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -49,8 +52,9 @@
final Context context = InstrumentationRegistry.getContext();
mBgThread = new HandlerThread("bg thread");
mBgThread.start();
- mBatteryStatsService = new BatteryStatsService(context,
- context.getCacheDir(), new Handler(mBgThread.getLooper()));
+ File systemDir = context.getCacheDir();
+ Handler handler = new Handler(mBgThread.getLooper());
+ mBatteryStatsService = new BatteryStatsService(context, systemDir, handler);
}
@After
@@ -121,4 +125,14 @@
waitThread.join(1000);
}
}
+
+ @Test
+ public void testSavingStatsdAtomPullTimestamp() {
+ mBatteryStatsService.setLastBatteryUsageStatsBeforeResetAtomPullTimestamp(1234);
+ assertThat(mBatteryStatsService.getLastBatteryUsageStatsBeforeResetAtomPullTimestamp())
+ .isEqualTo(1234);
+ mBatteryStatsService.setLastBatteryUsageStatsBeforeResetAtomPullTimestamp(5478);
+ assertThat(mBatteryStatsService.getLastBatteryUsageStatsBeforeResetAtomPullTimestamp())
+ .isEqualTo(5478);
+ }
}
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/contentcapture/ContentCaptureManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/contentcapture/ContentCaptureManagerServiceTest.java
index e7777f7..5cc84b1 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,9 @@
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
+import java.util.Collections;
+import java.util.List;
+
/**
* Test for {@link ContentCaptureManagerService}.
*
@@ -84,6 +90,8 @@
@Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
+ @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
@Mock private UserManagerInternal mMockUserManagerInternal;
@Mock private ContentProtectionBlocklistManager mMockContentProtectionBlocklistManager;
@@ -96,6 +104,10 @@
private boolean mDevCfgEnableContentProtectionReceiver;
+ private List<List<String>> mDevCfgContentProtectionRequiredGroups = List.of(List.of("a"));
+
+ private List<List<String>> mDevCfgContentProtectionOptionalGroups = Collections.emptyList();
+
private int mContentProtectionBlocklistManagersCreated;
private int mContentProtectionServiceInfosCreated;
@@ -367,7 +379,21 @@
}
@Test
- public void isContentProtectionReceiverEnabled_withoutManagers() {
+ public void isContentProtectionReceiverEnabled_true() {
+ when(mMockContentProtectionConsentManager.isConsentGranted(USER_ID)).thenReturn(true);
+ when(mMockContentProtectionBlocklistManager.isAllowed(PACKAGE_NAME)).thenReturn(true);
+ mDevCfgEnableContentProtectionReceiver = true;
+ mContentCaptureManagerService = new TestContentCaptureManagerService();
+
+ boolean actual =
+ mContentCaptureManagerService.mGlobalContentCaptureOptions.isWhitelisted(
+ USER_ID, PACKAGE_NAME);
+
+ assertThat(actual).isTrue();
+ }
+
+ @Test
+ public void isContentProtectionReceiverEnabled_false_withoutManagers() {
boolean actual =
mContentCaptureManagerService.mGlobalContentCaptureOptions.isWhitelisted(
USER_ID, PACKAGE_NAME);
@@ -378,7 +404,7 @@
}
@Test
- public void isContentProtectionReceiverEnabled_disabledWithFlag() {
+ public void isContentProtectionReceiverEnabled_false_disabledWithFlag() {
mDevCfgEnableContentProtectionReceiver = true;
mContentCaptureManagerService = new TestContentCaptureManagerService();
mContentCaptureManagerService.mDevCfgEnableContentProtectionReceiver = false;
@@ -393,6 +419,22 @@
}
@Test
+ public void isContentProtectionReceiverEnabled_false_emptyGroups() {
+ mDevCfgEnableContentProtectionReceiver = true;
+ mDevCfgContentProtectionRequiredGroups = Collections.emptyList();
+ mDevCfgContentProtectionOptionalGroups = Collections.emptyList();
+ mContentCaptureManagerService = new TestContentCaptureManagerService();
+
+ boolean actual =
+ mContentCaptureManagerService.mGlobalContentCaptureOptions.isWhitelisted(
+ USER_ID, PACKAGE_NAME);
+
+ assertThat(actual).isFalse();
+ verify(mMockContentProtectionConsentManager, never()).isConsentGranted(anyInt());
+ verify(mMockContentProtectionBlocklistManager, never()).isAllowed(anyString());
+ }
+
+ @Test
public void onLoginDetected_disabledAfterConstructor() {
mDevCfgEnableContentProtectionReceiver = true;
mContentCaptureManagerService = new TestContentCaptureManagerService();
@@ -436,12 +478,92 @@
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() {
super(sContext);
this.mDevCfgEnableContentProtectionReceiver =
ContentCaptureManagerServiceTest.this.mDevCfgEnableContentProtectionReceiver;
+ this.mDevCfgContentProtectionRequiredGroups =
+ ContentCaptureManagerServiceTest.this.mDevCfgContentProtectionRequiredGroups;
+ this.mDevCfgContentProtectionOptionalGroups =
+ ContentCaptureManagerServiceTest.this.mDevCfgContentProtectionOptionalGroups;
}
@Override
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/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/BluetoothRouteControllerTest.java b/services/tests/servicestests/src/com/android/server/media/BluetoothRouteControllerTest.java
index 75d71da..06f117b 100644
--- a/services/tests/servicestests/src/com/android/server/media/BluetoothRouteControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/media/BluetoothRouteControllerTest.java
@@ -38,9 +38,10 @@
public class BluetoothRouteControllerTest {
private final BluetoothRouteController.BluetoothRoutesUpdatedListener
- mBluetoothRoutesUpdatedListener = routes -> {
- // Empty on purpose.
- };
+ mBluetoothRoutesUpdatedListener =
+ () -> {
+ // Empty on purpose.
+ };
@Rule
public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
diff --git a/services/tests/servicestests/src/com/android/server/media/DeviceRouteControllerTest.java b/services/tests/servicestests/src/com/android/server/media/DeviceRouteControllerTest.java
index ec4b8a8..14b121d 100644
--- a/services/tests/servicestests/src/com/android/server/media/DeviceRouteControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/media/DeviceRouteControllerTest.java
@@ -38,7 +38,7 @@
public class DeviceRouteControllerTest {
private final DeviceRouteController.OnDeviceRouteChangedListener mOnDeviceRouteChangedListener =
- deviceRoute -> {
+ () -> {
// Empty on purpose.
};
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/media/projection/MediaProjectionSessionIdGeneratorTest.java b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionSessionIdGeneratorTest.java
new file mode 100644
index 0000000..07cdf4d
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionSessionIdGeneratorTest.java
@@ -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.server.media.projection;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+
+/**
+ * Tests for the {@link MediaProjectionSessionIdGenerator} class.
+ *
+ * <p>Build/Install/Run: atest FrameworksServicesTests:MediaProjectionSessionIdGeneratorTest
+ */
+@SmallTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class MediaProjectionSessionIdGeneratorTest {
+
+ private static final String TEST_PREFS_FILE = "media-projection-session-id-test";
+
+ private final Context mContext =
+ InstrumentationRegistry.getInstrumentation().getTargetContext();
+ private final File mSharedPreferencesFile = new File(mContext.getCacheDir(), TEST_PREFS_FILE);
+ private final SharedPreferences mSharedPreferences = createSharePreferences();
+ private final MediaProjectionSessionIdGenerator mGenerator =
+ createGenerator(mSharedPreferences);
+
+ @Before
+ public void setUp() {
+ mSharedPreferences.edit().clear().commit();
+ }
+
+ @After
+ public void tearDown() {
+ mSharedPreferences.edit().clear().commit();
+ mSharedPreferencesFile.delete();
+ }
+
+ @Test
+ public void getCurrentSessionId_byDefault_returns0() {
+ assertThat(mGenerator.getCurrentSessionId()).isEqualTo(0);
+ }
+
+ @Test
+ public void getCurrentSessionId_multipleTimes_returnsSameValue() {
+ assertThat(mGenerator.getCurrentSessionId()).isEqualTo(0);
+ assertThat(mGenerator.getCurrentSessionId()).isEqualTo(0);
+ assertThat(mGenerator.getCurrentSessionId()).isEqualTo(0);
+ }
+
+ @Test
+ public void createAndGetNewSessionId_returnsIncrementedId() {
+ int previousValue = mGenerator.getCurrentSessionId();
+
+ int newValue = mGenerator.createAndGetNewSessionId();
+
+ assertThat(newValue).isEqualTo(previousValue + 1);
+ }
+
+ @Test
+ public void createAndGetNewSessionId_persistsNewValue() {
+ int newValue = mGenerator.createAndGetNewSessionId();
+
+ MediaProjectionSessionIdGenerator newInstance = createGenerator(createSharePreferences());
+
+ assertThat(newInstance.getCurrentSessionId()).isEqualTo(newValue);
+ }
+
+ private SharedPreferences createSharePreferences() {
+ return mContext.getSharedPreferences(mSharedPreferencesFile, Context.MODE_PRIVATE);
+ }
+
+ private MediaProjectionSessionIdGenerator createGenerator(SharedPreferences sharedPreferences) {
+ return new MediaProjectionSessionIdGenerator(sharedPreferences);
+ }
+}
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/pdb/OWNERS b/services/tests/servicestests/src/com/android/server/pdb/OWNERS
new file mode 100644
index 0000000..6dfb888
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/pdb/OWNERS
@@ -0,0 +1 @@
+include /services/core/java/com/android/server/pdb/OWNERS
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 d1f4961..8891413 100644
--- a/services/tests/uiservicestests/Android.bp
+++ b/services/tests/uiservicestests/Android.bp
@@ -43,6 +43,8 @@
// TODO: remove once Android migrates to JUnit 4.12,
// which provides assertThrows
"testng",
+ "flag-junit",
+ "notification_flags_lib",
],
libs: [
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
index 9742384..42ad73a 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
@@ -32,6 +32,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
+import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.anyInt;
@@ -147,6 +148,7 @@
private static final int CUSTOM_LIGHT_ON = 10000;
private static final int CUSTOM_LIGHT_OFF = 10000;
private static final int MAX_VIBRATION_DELAY = 1000;
+ private static final float DEFAULT_VOLUME = 1.0f;
@Before
public void setUp() throws Exception {
@@ -397,19 +399,22 @@
//
private void verifyNeverBeep() throws RemoteException {
- verify(mRingtonePlayer, never()).playAsync(any(), any(), anyBoolean(), any());
+ verify(mRingtonePlayer, never()).playAsync(any(), any(), anyBoolean(), any(), anyFloat());
}
private void verifyBeepUnlooped() throws RemoteException {
- verify(mRingtonePlayer, times(1)).playAsync(any(), any(), eq(false), any());
+ verify(mRingtonePlayer, times(1)).playAsync(any(), any(), eq(false), any(),
+ eq(DEFAULT_VOLUME));
}
private void verifyBeepLooped() throws RemoteException {
- verify(mRingtonePlayer, times(1)).playAsync(any(), any(), eq(true), any());
+ verify(mRingtonePlayer, times(1)).playAsync(any(), any(), eq(true), any(),
+ eq(DEFAULT_VOLUME));
}
private void verifyBeep(int times) throws RemoteException {
- verify(mRingtonePlayer, times(times)).playAsync(any(), any(), anyBoolean(), any());
+ verify(mRingtonePlayer, times(times)).playAsync(any(), any(), anyBoolean(), any(),
+ eq(DEFAULT_VOLUME));
}
private void verifyNeverStopAudio() throws RemoteException {
@@ -905,7 +910,7 @@
verifyDelayedVibrate(
mService.getVibratorHelper().createFallbackVibration(/* insistent= */ false));
verify(mRingtonePlayer, never()).playAsync
- (anyObject(), anyObject(), anyBoolean(), anyObject());
+ (anyObject(), anyObject(), anyBoolean(), anyObject(), anyFloat());
assertTrue(r.isInterruptive());
assertNotEquals(-1, r.getLastAudiblyAlertedMs());
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAttentionHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAttentionHelperTest.java
index 81867df..9bd938f 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAttentionHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAttentionHelperTest.java
@@ -57,6 +57,7 @@
import android.app.PendingIntent;
import android.content.Context;
import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
import android.graphics.Color;
import android.graphics.drawable.Icon;
import android.media.AudioAttributes;
@@ -66,9 +67,11 @@
import android.os.Process;
import android.os.RemoteException;
import android.os.UserHandle;
+import android.os.UserManager;
import android.os.VibrationAttributes;
import android.os.VibrationEffect;
import android.os.Vibrator;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
@@ -87,8 +90,11 @@
import com.android.server.lights.LightsManager;
import com.android.server.lights.LogicalLight;
import com.android.server.pm.PackageManagerService;
+
+import java.util.List;
import java.util.Objects;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
@@ -102,6 +108,8 @@
@RunWith(AndroidJUnit4.class)
@SuppressLint("GuardedBy")
public class NotificationAttentionHelperTest extends UiServiceTestCase {
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@Mock AudioManager mAudioManager;
@Mock Vibrator mVibrator;
@@ -115,6 +123,8 @@
IAccessibilityManager mAccessibilityService;
@Mock
KeyguardManager mKeyguardManager;
+ @Mock
+ private UserManager mUserManager;
NotificationRecordLoggerFake mNotificationRecordLogger = new NotificationRecordLoggerFake();
private InstanceIdSequence mNotificationInstanceIdSequence = new InstanceIdSequenceFake(
1 << 30);
@@ -134,6 +144,8 @@
private AccessibilityManager mAccessibilityManager;
private static final NotificationAttentionHelper.Signals DEFAULT_SIGNALS =
new NotificationAttentionHelper.Signals(false, 0);
+ private static final NotificationAttentionHelper.Signals WORK_PROFILE_SIGNALS =
+ new NotificationAttentionHelper.Signals(true, 0);
private VibrateRepeatMatcher mVibrateOnceMatcher = new VibrateRepeatMatcher(-1);
private VibrateRepeatMatcher mVibrateLoopMatcher = new VibrateRepeatMatcher(0);
@@ -151,6 +163,7 @@
private static final int CUSTOM_LIGHT_ON = 10000;
private static final int CUSTOM_LIGHT_OFF = 10000;
private static final int MAX_VIBRATION_DELAY = 1000;
+ private static final float DEFAULT_VOLUME = 1.0f;
@Before
public void setUp() throws Exception {
@@ -178,6 +191,8 @@
// TODO (b/291907312): remove feature flag
mTestFlagResolver.setFlagOverride(NotificationFlags.ENABLE_ATTENTION_HELPER_REFACTOR, true);
+ // Disable feature flags by default. Tests should enable as needed.
+ mSetFlagsRule.disableFlags(Flags.FLAG_POLITE_NOTIFICATIONS, Flags.FLAG_EXPIRE_BITMAPS);
mService = spy(new NotificationManagerService(getContext(), mNotificationRecordLogger,
mNotificationInstanceIdSequence));
@@ -189,9 +204,9 @@
private void initAttentionHelper(TestableFlagResolver flagResolver) {
mAttentionHelper = new NotificationAttentionHelper(getContext(), mock(LightsManager.class),
- mAccessibilityManager, getContext().getPackageManager(), mUsageStats,
+ mAccessibilityManager, getContext().getPackageManager(), mUserManager, mUsageStats,
mService.mNotificationManagerPrivate, mock(ZenModeHelper.class), flagResolver);
- mAttentionHelper.setVibratorHelper(new VibratorHelper(getContext()));
+ mAttentionHelper.setVibratorHelper(spy(new VibratorHelper(getContext())));
mAttentionHelper.setAudioManager(mAudioManager);
mAttentionHelper.setSystemReady(true);
mAttentionHelper.setLights(mLight);
@@ -282,6 +297,11 @@
true /* noisy */, true /* buzzy*/, false /* lights */);
}
+ private NotificationRecord getBuzzyBeepyNotification(UserHandle userHandle) {
+ return getNotificationRecord(mId, false /* insistent */, false /* once */,
+ true /* noisy */, true /* buzzy*/, false /* lights */, userHandle);
+ }
+
private NotificationRecord getLightsNotification() {
return getNotificationRecord(mId, false /* insistent */, false /* once */,
false /* noisy */, false /* buzzy*/, true /* lights */);
@@ -312,7 +332,13 @@
private NotificationRecord getNotificationRecord(int id, boolean insistent, boolean once,
boolean noisy, boolean buzzy, boolean lights) {
return getNotificationRecord(id, insistent, once, noisy, buzzy, lights, buzzy, noisy,
- lights, null, Notification.GROUP_ALERT_ALL, false);
+ lights, null, Notification.GROUP_ALERT_ALL, false, mUser);
+ }
+
+ private NotificationRecord getNotificationRecord(int id, boolean insistent, boolean once,
+ boolean noisy, boolean buzzy, boolean lights, UserHandle userHandle) {
+ return getNotificationRecord(id, insistent, once, noisy, buzzy, lights, buzzy, noisy,
+ lights, null, Notification.GROUP_ALERT_ALL, false, userHandle);
}
private NotificationRecord getLeanbackNotificationRecord(int id, boolean insistent,
@@ -320,25 +346,25 @@
boolean noisy, boolean buzzy, boolean lights) {
return getNotificationRecord(id, insistent, once, noisy, buzzy, lights, true, true,
true,
- null, Notification.GROUP_ALERT_ALL, true);
+ null, Notification.GROUP_ALERT_ALL, true, mUser);
}
private NotificationRecord getBeepyNotificationRecord(String groupKey, int groupAlertBehavior) {
return getNotificationRecord(mId, false, false, true, false, false, true, true, true,
- groupKey, groupAlertBehavior, false);
+ groupKey, groupAlertBehavior, false, mUser);
}
private NotificationRecord getLightsNotificationRecord(String groupKey,
int groupAlertBehavior) {
return getNotificationRecord(mId, false, false, false, false, true /*lights*/, true,
- true, true, groupKey, groupAlertBehavior, false);
+ true, true, groupKey, groupAlertBehavior, false, mUser);
}
private NotificationRecord getNotificationRecord(int id,
boolean insistent, boolean once,
boolean noisy, boolean buzzy, boolean lights, boolean defaultVibration,
boolean defaultSound, boolean defaultLights, String groupKey, int groupAlertBehavior,
- boolean isLeanback) {
+ boolean isLeanback, UserHandle userHandle) {
final Builder builder = new Builder(getContext())
.setContentTitle("foo")
@@ -399,7 +425,7 @@
.thenReturn(isLeanback);
StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, id, mTag, mUid,
- mPid, n, mUser, null, System.currentTimeMillis());
+ mPid, n, userHandle, null, System.currentTimeMillis());
NotificationRecord r = new NotificationRecord(context, sbn, mChannel);
mService.addNotification(r);
return r;
@@ -410,19 +436,26 @@
//
private void verifyNeverBeep() throws RemoteException {
- verify(mRingtonePlayer, never()).playAsync(any(), any(), anyBoolean(), any());
+ verify(mRingtonePlayer, never()).playAsync(any(), any(), anyBoolean(), any(), anyFloat());
}
private void verifyBeepUnlooped() throws RemoteException {
- verify(mRingtonePlayer, times(1)).playAsync(any(), any(), eq(false), any());
+ verify(mRingtonePlayer, times(1)).playAsync(any(), any(), eq(false), any(),
+ eq(DEFAULT_VOLUME));
}
private void verifyBeepLooped() throws RemoteException {
- verify(mRingtonePlayer, times(1)).playAsync(any(), any(), eq(true), any());
+ verify(mRingtonePlayer, times(1)).playAsync(any(), any(), eq(true), any(),
+ eq(DEFAULT_VOLUME));
}
private void verifyBeep(int times) throws RemoteException {
- verify(mRingtonePlayer, times(times)).playAsync(any(), any(), anyBoolean(), any());
+ verify(mRingtonePlayer, times(times)).playAsync(any(), any(), anyBoolean(), any(),
+ eq(DEFAULT_VOLUME));
+ }
+
+ private void verifyBeepVolume(float volume) throws RemoteException {
+ verify(mRingtonePlayer, times(1)).playAsync(any(), any(), anyBoolean(), any(), eq(volume));
}
private void verifyNeverStopAudio() throws RemoteException {
@@ -921,7 +954,7 @@
mAttentionHelper.getVibratorHelper().createFallbackVibration(
/* insistent= */ false));
verify(mRingtonePlayer, never()).playAsync(anyObject(), anyObject(), anyBoolean(),
- anyObject());
+ anyObject(), anyFloat());
assertTrue(r.isInterruptive());
assertNotEquals(-1, r.getLastAudiblyAlertedMs());
}
@@ -1948,6 +1981,259 @@
assertEquals(-1, r.getLastAudiblyAlertedMs());
}
+ // TODO b/270456865: Only one of the two strategies will be released.
+ // The other one need to be removed
+ @Test
+ public void testBeepVolume_politeNotif() throws Exception {
+ mSetFlagsRule.enableFlags(Flags.FLAG_POLITE_NOTIFICATIONS);
+ TestableFlagResolver flagResolver = new TestableFlagResolver();
+ flagResolver.setFlagOverride(NotificationFlags.NOTIF_COOLDOWN_RULE, "rule1");
+ flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME1, 50);
+ flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME2, 0);
+ initAttentionHelper(flagResolver);
+
+ NotificationRecord r = getBeepyNotification();
+
+ // set up internal state
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+ Mockito.reset(mRingtonePlayer);
+
+ // update should beep at 50% volume
+ r.isUpdate = true;
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+ verifyBeepVolume(0.5f);
+
+ // 2nd update should beep at 0% volume
+ Mockito.reset(mRingtonePlayer);
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+ verifyBeepVolume(0.0f);
+
+ verify(mAccessibilityService, times(3)).sendAccessibilityEvent(any(), anyInt());
+ assertNotEquals(-1, r.getLastAudiblyAlertedMs());
+ }
+
+ // TODO b/270456865: Only one of the two strategies will be released.
+ // The other one need to be removed
+ @Test
+ public void testBeepVolume_politeNotif_Strategy2() throws Exception {
+ mSetFlagsRule.enableFlags(Flags.FLAG_POLITE_NOTIFICATIONS);
+ TestableFlagResolver flagResolver = new TestableFlagResolver();
+ flagResolver.setFlagOverride(NotificationFlags.NOTIF_COOLDOWN_RULE, "rule2");
+ flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME1, 50);
+ flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME2, 0);
+ initAttentionHelper(flagResolver);
+
+ NotificationRecord r = getBeepyNotification();
+
+ // set up internal state
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+ Mockito.reset(mRingtonePlayer);
+
+ // update should beep at 0% volume
+ r.isUpdate = true;
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+ verifyBeepVolume(0.0f);
+
+ // 2nd update should beep at 50% volume
+ Mockito.reset(mRingtonePlayer);
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+ verifyBeepVolume(0.5f);
+
+ verify(mAccessibilityService, times(3)).sendAccessibilityEvent(any(), anyInt());
+ assertNotEquals(-1, r.getLastAudiblyAlertedMs());
+ }
+
+ @Test
+ public void testVibrationIntensity_politeNotif() throws Exception {
+ mSetFlagsRule.enableFlags(Flags.FLAG_POLITE_NOTIFICATIONS);
+ TestableFlagResolver flagResolver = new TestableFlagResolver();
+ flagResolver.setFlagOverride(NotificationFlags.NOTIF_COOLDOWN_RULE, "rule1");
+ flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME1, 50);
+ flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME2, 0);
+ initAttentionHelper(flagResolver);
+
+ NotificationRecord r = getBuzzyBeepyNotification();
+
+ // set up internal state
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+
+ VibratorHelper vibratorHelper = mAttentionHelper.getVibratorHelper();
+ Mockito.reset(vibratorHelper);
+
+ // update should buzz at 50% intensity
+ r.isUpdate = true;
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+
+ verify(vibratorHelper, times(1)).scale(any(), eq(0.5f));
+ Mockito.reset(vibratorHelper);
+
+ // 2nd update should buzz at 0% intensity
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+ verify(vibratorHelper, times(1)).scale(any(), eq(0.0f));
+ }
+
+ @Test
+ public void testVibrationIntensity_politeNotif_Strategy2() throws Exception {
+ mSetFlagsRule.enableFlags(Flags.FLAG_POLITE_NOTIFICATIONS);
+ TestableFlagResolver flagResolver = new TestableFlagResolver();
+ flagResolver.setFlagOverride(NotificationFlags.NOTIF_COOLDOWN_RULE, "rule2");
+ flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME1, 50);
+ flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME2, 0);
+ initAttentionHelper(flagResolver);
+
+ NotificationRecord r = getBuzzyBeepyNotification();
+
+ // set up internal state
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+
+ VibratorHelper vibratorHelper = mAttentionHelper.getVibratorHelper();
+ Mockito.reset(vibratorHelper);
+
+ // update should buzz at 0% intensity
+ r.isUpdate = true;
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+
+ verify(vibratorHelper, times(1)).scale(any(), eq(0.0f));
+ Mockito.reset(vibratorHelper);
+
+ // 2nd update should buzz at 50% intensity
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+ verify(vibratorHelper, times(1)).scale(any(), eq(0.5f));
+ }
+
+ @Test
+ public void testBuzzOnlyOnScreenUnlock_politeNotif() throws Exception {
+ mSetFlagsRule.enableFlags(Flags.FLAG_POLITE_NOTIFICATIONS);
+ TestableFlagResolver flagResolver = new TestableFlagResolver();
+
+ // When NOTIFICATION_COOLDOWN_VIBRATE_UNLOCKED setting is enabled
+ Settings.System.putInt(getContext().getContentResolver(),
+ Settings.System.NOTIFICATION_COOLDOWN_VIBRATE_UNLOCKED, 1);
+
+ initAttentionHelper(flagResolver);
+ // And screen is unlocked
+ mAttentionHelper.setUserPresent(true);
+
+ NotificationRecord r = getBuzzyBeepyNotification();
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+
+ // The notification attention should only buzz
+ verifyNeverBeep();
+ verifyVibrate();
+ assertNotEquals(-1, r.getLastAudiblyAlertedMs());
+ }
+
+ @Test
+ public void testBeepVolume_politeNotif_disabled() throws Exception {
+ mSetFlagsRule.enableFlags(Flags.FLAG_POLITE_NOTIFICATIONS);
+ TestableFlagResolver flagResolver = new TestableFlagResolver();
+ flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME1, 50);
+ flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME2, 0);
+
+ // When NOTIFICATION_COOLDOWN_ENABLED setting is disabled
+ Settings.System.putInt(getContext().getContentResolver(),
+ Settings.System.NOTIFICATION_COOLDOWN_ENABLED, 0);
+
+ initAttentionHelper(flagResolver);
+
+ NotificationRecord r = getBeepyNotification();
+
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+ Mockito.reset(mRingtonePlayer);
+
+ // update should beep at 100% volume
+ r.isUpdate = true;
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+ verifyBeepVolume(1.0f);
+
+ // 2nd update should beep at 100% volume
+ Mockito.reset(mRingtonePlayer);
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+ verifyBeepVolume(1.0f);
+
+ assertNotEquals(-1, r.getLastAudiblyAlertedMs());
+ }
+
+ @Test
+ public void testBeepVolume_politeNotif_workProfile() throws Exception {
+ mSetFlagsRule.enableFlags(Flags.FLAG_POLITE_NOTIFICATIONS);
+ TestableFlagResolver flagResolver = new TestableFlagResolver();
+ flagResolver.setFlagOverride(NotificationFlags.NOTIF_COOLDOWN_RULE, "rule1");
+ flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME1, 50);
+ flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME2, 0);
+
+ final int workProfileUserId = mUser.getIdentifier() + 1;
+
+ // Enable notifications cooldown for work profile
+ Settings.System.putIntForUser(getContext().getContentResolver(),
+ Settings.System.NOTIFICATION_COOLDOWN_ENABLED, 1, workProfileUserId);
+
+ when(mUserManager.getProfiles(mUser.getIdentifier())).thenReturn(
+ List.of(new UserInfo(workProfileUserId, "work_profile", null,
+ UserInfo.FLAG_PROFILE | UserInfo.FLAG_MANAGED_PROFILE,
+ UserInfo.getDefaultUserType(UserInfo.FLAG_MANAGED_PROFILE))));
+
+ initAttentionHelper(flagResolver);
+
+ final NotificationRecord r = getBuzzyBeepyNotification(UserHandle.of(workProfileUserId));
+
+ // set up internal state
+ mAttentionHelper.buzzBeepBlinkLocked(r, WORK_PROFILE_SIGNALS);
+ Mockito.reset(mRingtonePlayer);
+
+ // update should beep at 50% volume
+ r.isUpdate = true;
+ mAttentionHelper.buzzBeepBlinkLocked(r, WORK_PROFILE_SIGNALS);
+ verifyBeepVolume(0.5f);
+
+ // 2nd update should beep at 0% volume
+ Mockito.reset(mRingtonePlayer);
+ mAttentionHelper.buzzBeepBlinkLocked(r, WORK_PROFILE_SIGNALS);
+ verifyBeepVolume(0.0f);
+
+ verify(mAccessibilityService, times(3)).sendAccessibilityEvent(any(), anyInt());
+ assertNotEquals(-1, r.getLastAudiblyAlertedMs());
+ }
+
+ @Test
+ public void testBeepVolume_politeNotif_workProfile_disabled() throws Exception {
+ mSetFlagsRule.enableFlags(Flags.FLAG_POLITE_NOTIFICATIONS);
+ TestableFlagResolver flagResolver = new TestableFlagResolver();
+ flagResolver.setFlagOverride(NotificationFlags.NOTIF_COOLDOWN_RULE, "rule1");
+ flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME1, 50);
+ flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME2, 0);
+
+ final int workProfileUserId = mUser.getIdentifier() + 1;
+
+ // Disable notifications cooldown for work profile
+ Settings.System.putIntForUser(getContext().getContentResolver(),
+ Settings.System.NOTIFICATION_COOLDOWN_ENABLED, 0, workProfileUserId);
+
+ when(mUserManager.getProfiles(mUser.getIdentifier())).thenReturn(
+ List.of(new UserInfo(workProfileUserId, "work_profile", null,
+ UserInfo.FLAG_PROFILE | UserInfo.FLAG_MANAGED_PROFILE,
+ UserInfo.getDefaultUserType(UserInfo.FLAG_MANAGED_PROFILE))));
+
+ initAttentionHelper(flagResolver);
+
+ final NotificationRecord r = getBuzzyBeepyNotification(UserHandle.of(workProfileUserId));
+
+ mAttentionHelper.buzzBeepBlinkLocked(r, WORK_PROFILE_SIGNALS);
+ Mockito.reset(mRingtonePlayer);
+
+ // update should beep at 100% volume
+ r.isUpdate = true;
+ mAttentionHelper.buzzBeepBlinkLocked(r, WORK_PROFILE_SIGNALS);
+ verifyBeepVolume(1.0f);
+
+ // 2nd update should beep at 100% volume
+ Mockito.reset(mRingtonePlayer);
+ mAttentionHelper.buzzBeepBlinkLocked(r, WORK_PROFILE_SIGNALS);
+ verifyBeepVolume(1.0f);
+
+ assertNotEquals(-1, r.getLastAudiblyAlertedMs());
+ }
+
static class VibrateRepeatMatcher implements ArgumentMatcher<VibrationEffect> {
private final int mRepeatIndex;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
index c05f814..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) {
- detailedAssertEquals(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 91129a1..3d4b4a6 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -687,7 +687,11 @@
mPackageIntentReceiver = broadcastReceivers.get(i);
}
if (filter.hasAction(Intent.ACTION_USER_SWITCHED)) {
- mUserSwitchIntentReceiver = broadcastReceivers.get(i);
+ // There may be multiple receivers, get the NMS one
+ if (broadcastReceivers.get(i).toString().contains(
+ NotificationManagerService.class.getName())) {
+ mUserSwitchIntentReceiver = broadcastReceivers.get(i);
+ }
}
}
assertNotNull("package intent receiver should exist", mPackageIntentReceiver);
diff --git a/services/tests/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/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
index 8e7ba70..dd7dec0 100644
--- a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
@@ -130,12 +130,22 @@
// verify if back animation would start.
assertTrue("Animation scheduled", backNavigationInfo.isPrepareRemoteAnimation());
- // reset drawing status
+ // reset drawing status to test translucent activity
backNavigationInfo.onBackNavigationFinished(false);
mBackNavigationController.clearBackAnimations();
- topTask.forAllWindows(w -> {
- makeWindowVisibleAndDrawn(w);
- }, true);
+ final ActivityRecord topActivity = topTask.getTopMostActivity();
+ makeWindowVisibleAndDrawn(topActivity.findMainWindow());
+ // simulate translucent
+ topActivity.setOccludesParent(false);
+ backNavigationInfo = startBackNavigation();
+ assertThat(typeToString(backNavigationInfo.getType()))
+ .isEqualTo(typeToString(BackNavigationInfo.TYPE_CALLBACK));
+
+ // reset drawing status to test keyguard occludes
+ topActivity.setOccludesParent(true);
+ backNavigationInfo.onBackNavigationFinished(false);
+ mBackNavigationController.clearBackAnimations();
+ makeWindowVisibleAndDrawn(topActivity.findMainWindow());
setupKeyguardOccluded();
backNavigationInfo = startBackNavigation();
assertThat(typeToString(backNavigationInfo.getType()))
@@ -201,9 +211,7 @@
// reset drawing status
backNavigationInfo.onBackNavigationFinished(false);
mBackNavigationController.clearBackAnimations();
- testCase.recordFront.forAllWindows(w -> {
- makeWindowVisibleAndDrawn(w);
- }, true);
+ makeWindowVisibleAndDrawn(testCase.recordFront.findMainWindow());
setupKeyguardOccluded();
backNavigationInfo = startBackNavigation();
assertThat(typeToString(backNavigationInfo.getType()))
diff --git a/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java b/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java
index 233a207..84d42d42 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java
@@ -18,16 +18,20 @@
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.reset;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_DIMMER;
+import static com.android.server.wm.utils.LastCallVerifier.lastCall;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assume.assumeFalse;
+import static org.junit.Assume.assumeTrue;
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.reset;
import static org.mockito.Mockito.when;
import android.graphics.Rect;
@@ -35,8 +39,9 @@
import android.view.SurfaceControl;
import android.view.SurfaceSession;
-import com.android.server.wm.SurfaceAnimator.AnimationType;
import com.android.server.testutils.StubTransaction;
+import com.android.server.wm.utils.MockAnimationAdapter;
+import com.android.window.flags.Flags;
import org.junit.Before;
import org.junit.Test;
@@ -118,102 +123,168 @@
}
}
- private MockSurfaceBuildingContainer mHost;
- private Dimmer mDimmer;
- private SurfaceControl.Transaction mTransaction;
- private Dimmer.SurfaceAnimatorStarter mSurfaceAnimatorStarter;
+ static class MockAnimationAdapterFactory extends SmoothDimmer.AnimationAdapterFactory {
+ public AnimationAdapter get(LocalAnimationAdapter.AnimationSpec alphaAnimationSpec,
+ SurfaceAnimationRunner runner) {
+ return sTestAnimation;
+ }
+ }
- private static class SurfaceAnimatorStarterImpl implements Dimmer.SurfaceAnimatorStarter {
+ private static class SurfaceAnimatorStarterImpl implements LegacyDimmer.SurfaceAnimatorStarter {
@Override
public void startAnimation(SurfaceAnimator surfaceAnimator, SurfaceControl.Transaction t,
- AnimationAdapter anim, boolean hidden, @AnimationType int type) {
+ AnimationAdapter anim, boolean hidden, @SurfaceAnimator.AnimationType int type) {
surfaceAnimator.mStaticAnimationFinishedCallback.onAnimationFinished(type, anim);
}
}
+ private MockSurfaceBuildingContainer mHost;
+ private Dimmer mDimmer;
+ private SurfaceControl.Transaction mTransaction;
+ private TestWindowContainer mChild;
+ private static AnimationAdapter sTestAnimation;
+ private static LegacyDimmer.SurfaceAnimatorStarter sSurfaceAnimatorStarter;
+
@Before
public void setUp() throws Exception {
mHost = new MockSurfaceBuildingContainer(mWm);
- mSurfaceAnimatorStarter = spy(new SurfaceAnimatorStarterImpl());
mTransaction = spy(StubTransaction.class);
- mDimmer = new Dimmer(mHost, mSurfaceAnimatorStarter);
+ mChild = new TestWindowContainer(mWm);
+ if (Flags.dimmerRefactor()) {
+ sTestAnimation = spy(new MockAnimationAdapter());
+ mDimmer = new SmoothDimmer(mHost, new MockAnimationAdapterFactory());
+ } else {
+ sSurfaceAnimatorStarter = spy(new SurfaceAnimatorStarterImpl());
+ mDimmer = new LegacyDimmer(mHost, sSurfaceAnimatorStarter);
+ }
}
@Test
public void testUpdateDimsAppliesCrop() {
- TestWindowContainer child = new TestWindowContainer(mWm);
- mHost.addChild(child, 0);
+ mHost.addChild(mChild, 0);
final float alpha = 0.8f;
- mDimmer.dimAbove(child, alpha);
+ mDimmer.dimAbove(mChild, alpha);
int width = 100;
int height = 300;
- mDimmer.mDimState.mDimBounds.set(0, 0, width, height);
+ mDimmer.getDimBounds().set(0, 0, width, height);
mDimmer.updateDims(mTransaction);
- verify(mTransaction).setWindowCrop(getDimLayer(), width, height);
- verify(mTransaction).show(getDimLayer());
+ verify(mTransaction).setWindowCrop(mDimmer.getDimLayer(), width, height);
+ verify(mTransaction).show(mDimmer.getDimLayer());
}
@Test
- public void testDimAboveWithChildCreatesSurfaceAboveChild() {
- TestWindowContainer child = new TestWindowContainer(mWm);
- mHost.addChild(child, 0);
-
+ public void testDimAboveWithChildCreatesSurfaceAboveChild_Smooth() {
+ assumeTrue(Flags.dimmerRefactor());
final float alpha = 0.8f;
- mDimmer.dimAbove(child, alpha);
- SurfaceControl dimLayer = getDimLayer();
+ mHost.addChild(mChild, 0);
+ mDimmer.dimAbove(mChild, alpha);
+ SurfaceControl dimLayer = mDimmer.getDimLayer();
+
+ assertNotNull("Dimmer should have created a surface", dimLayer);
+
+ mDimmer.updateDims(mTransaction);
+ verify(sTestAnimation).startAnimation(eq(dimLayer), eq(mTransaction),
+ anyInt(), any(SurfaceAnimator.OnAnimationFinishedCallback.class));
+ verify(mTransaction).setRelativeLayer(dimLayer, mChild.mControl, 1);
+ verify(mTransaction, lastCall()).setAlpha(dimLayer, alpha);
+ }
+
+ @Test
+ public void testDimAboveWithChildCreatesSurfaceAboveChild_Legacy() {
+ assumeFalse(Flags.dimmerRefactor());
+ final float alpha = 0.8f;
+ mHost.addChild(mChild, 0);
+ mDimmer.dimAbove(mChild, alpha);
+ SurfaceControl dimLayer = mDimmer.getDimLayer();
assertNotNull("Dimmer should have created a surface", dimLayer);
verify(mHost.getPendingTransaction()).setAlpha(dimLayer, alpha);
- verify(mHost.getPendingTransaction()).setRelativeLayer(dimLayer, child.mControl, 1);
+ verify(mHost.getPendingTransaction()).setRelativeLayer(dimLayer, mChild.mControl, 1);
}
@Test
- public void testDimBelowWithChildSurfaceCreatesSurfaceBelowChild() {
- TestWindowContainer child = new TestWindowContainer(mWm);
- mHost.addChild(child, 0);
+ public void testDimBelowWithChildSurfaceCreatesSurfaceBelowChild_Smooth() {
+ assumeTrue(Flags.dimmerRefactor());
+ final float alpha = 0.7f;
+ mHost.addChild(mChild, 0);
+ mDimmer.dimBelow(mChild, alpha, 50);
+ SurfaceControl dimLayer = mDimmer.getDimLayer();
- final float alpha = 0.8f;
- mDimmer.dimBelow(child, alpha, 0);
- SurfaceControl dimLayer = getDimLayer();
+ assertNotNull("Dimmer should have created a surface", dimLayer);
+
+ mDimmer.updateDims(mTransaction);
+ verify(sTestAnimation).startAnimation(eq(dimLayer), eq(mTransaction),
+ anyInt(), any(SurfaceAnimator.OnAnimationFinishedCallback.class));
+ verify(mTransaction).setRelativeLayer(dimLayer, mChild.mControl, -1);
+ verify(mTransaction, lastCall()).setAlpha(dimLayer, alpha);
+ verify(mTransaction).setBackgroundBlurRadius(dimLayer, 50);
+ }
+
+ @Test
+ public void testDimBelowWithChildSurfaceCreatesSurfaceBelowChild_Legacy() {
+ assumeFalse(Flags.dimmerRefactor());
+ final float alpha = 0.7f;
+ mHost.addChild(mChild, 0);
+ mDimmer.dimBelow(mChild, alpha, 50);
+ SurfaceControl dimLayer = mDimmer.getDimLayer();
assertNotNull("Dimmer should have created a surface", dimLayer);
verify(mHost.getPendingTransaction()).setAlpha(dimLayer, alpha);
- verify(mHost.getPendingTransaction()).setRelativeLayer(dimLayer, child.mControl, -1);
+ verify(mHost.getPendingTransaction()).setRelativeLayer(dimLayer, mChild.mControl, -1);
}
@Test
- public void testDimBelowWithChildSurfaceDestroyedWhenReset() {
- TestWindowContainer child = new TestWindowContainer(mWm);
- mHost.addChild(child, 0);
+ public void testDimBelowWithChildSurfaceDestroyedWhenReset_Smooth() {
+ assumeTrue(Flags.dimmerRefactor());
+ mHost.addChild(mChild, 0);
final float alpha = 0.8f;
- mDimmer.dimAbove(child, alpha);
- SurfaceControl dimLayer = getDimLayer();
+ // Dim once
+ mDimmer.dimBelow(mChild, alpha, 0);
+ SurfaceControl dimLayer = mDimmer.getDimLayer();
+ mDimmer.updateDims(mTransaction);
+ // Reset, and don't dim
+ mDimmer.resetDimStates();
+ mDimmer.updateDims(mTransaction);
+ verify(mTransaction).show(dimLayer);
+ verify(mTransaction).remove(dimLayer);
+ }
+
+ @Test
+ public void testDimBelowWithChildSurfaceDestroyedWhenReset_Legacy() {
+ assumeFalse(Flags.dimmerRefactor());
+ mHost.addChild(mChild, 0);
+
+ final float alpha = 0.8f;
+ mDimmer.dimAbove(mChild, alpha);
+ SurfaceControl dimLayer = mDimmer.getDimLayer();
mDimmer.resetDimStates();
mDimmer.updateDims(mTransaction);
- verify(mSurfaceAnimatorStarter).startAnimation(any(SurfaceAnimator.class), any(
- SurfaceControl.Transaction.class), any(AnimationAdapter.class), anyBoolean(),
+ verify(sSurfaceAnimatorStarter).startAnimation(any(SurfaceAnimator.class),
+ any(SurfaceControl.Transaction.class), any(AnimationAdapter.class),
+ anyBoolean(),
eq(ANIMATION_TYPE_DIMMER));
verify(mHost.getPendingTransaction()).remove(dimLayer);
}
@Test
public void testDimBelowWithChildSurfaceNotDestroyedWhenPersisted() {
- TestWindowContainer child = new TestWindowContainer(mWm);
- mHost.addChild(child, 0);
+ mHost.addChild(mChild, 0);
final float alpha = 0.8f;
- mDimmer.dimAbove(child, alpha);
- SurfaceControl dimLayer = getDimLayer();
+ // Dim once
+ mDimmer.dimBelow(mChild, alpha, 0);
+ SurfaceControl dimLayer = mDimmer.getDimLayer();
+ mDimmer.updateDims(mTransaction);
+ // Reset and dim again
mDimmer.resetDimStates();
- mDimmer.dimAbove(child, alpha);
-
+ mDimmer.dimAbove(mChild, alpha);
mDimmer.updateDims(mTransaction);
verify(mTransaction).show(dimLayer);
verify(mTransaction, never()).remove(dimLayer);
@@ -221,14 +292,12 @@
@Test
public void testDimUpdateWhileDimming() {
- TestWindowContainer child = new TestWindowContainer(mWm);
- mHost.addChild(child, 0);
-
+ mHost.addChild(mChild, 0);
final float alpha = 0.8f;
- mDimmer.dimAbove(child, alpha);
- final Rect bounds = mDimmer.mDimState.mDimBounds;
+ mDimmer.dimAbove(mChild, alpha);
+ final Rect bounds = mDimmer.getDimBounds();
- SurfaceControl dimLayer = getDimLayer();
+ SurfaceControl dimLayer = mDimmer.getDimLayer();
bounds.set(0, 0, 10, 10);
mDimmer.updateDims(mTransaction);
verify(mTransaction).setWindowCrop(dimLayer, bounds.width(), bounds.height());
@@ -242,41 +311,56 @@
}
@Test
- public void testRemoveDimImmediately() {
- TestWindowContainer child = new TestWindowContainer(mWm);
- mHost.addChild(child, 0);
-
- mDimmer.dimAbove(child, 1);
- SurfaceControl dimLayer = getDimLayer();
+ public void testRemoveDimImmediately_Smooth() {
+ assumeTrue(Flags.dimmerRefactor());
+ mHost.addChild(mChild, 0);
+ mDimmer.dimAbove(mChild, 1);
+ SurfaceControl dimLayer = mDimmer.getDimLayer();
mDimmer.updateDims(mTransaction);
verify(mTransaction, times(1)).show(dimLayer);
- reset(mSurfaceAnimatorStarter);
+ reset(sTestAnimation);
mDimmer.dontAnimateExit();
mDimmer.resetDimStates();
mDimmer.updateDims(mTransaction);
- verify(mSurfaceAnimatorStarter, never()).startAnimation(any(SurfaceAnimator.class), any(
- SurfaceControl.Transaction.class), any(AnimationAdapter.class), anyBoolean(),
+ verify(sTestAnimation, never()).startAnimation(
+ any(SurfaceControl.class), any(SurfaceControl.Transaction.class),
+ anyInt(), any(SurfaceAnimator.OnAnimationFinishedCallback.class));
+ verify(mTransaction).remove(dimLayer);
+ }
+
+ @Test
+ public void testRemoveDimImmediately_Legacy() {
+ assumeFalse(Flags.dimmerRefactor());
+ mHost.addChild(mChild, 0);
+ mDimmer.dimAbove(mChild, 1);
+ SurfaceControl dimLayer = mDimmer.getDimLayer();
+ mDimmer.updateDims(mTransaction);
+ verify(mTransaction, times(1)).show(dimLayer);
+
+ reset(sSurfaceAnimatorStarter);
+ mDimmer.dontAnimateExit();
+ mDimmer.resetDimStates();
+ mDimmer.updateDims(mTransaction);
+ verify(sSurfaceAnimatorStarter, never()).startAnimation(any(SurfaceAnimator.class),
+ any(SurfaceControl.Transaction.class), any(AnimationAdapter.class), anyBoolean(),
eq(ANIMATION_TYPE_DIMMER));
verify(mTransaction).remove(dimLayer);
}
@Test
- public void testDimmerWithBlurUpdatesTransaction() {
+ public void testDimmerWithBlurUpdatesTransaction_Legacy() {
+ assumeFalse(Flags.dimmerRefactor());
TestWindowContainer child = new TestWindowContainer(mWm);
mHost.addChild(child, 0);
final int blurRadius = 50;
mDimmer.dimBelow(child, 0, blurRadius);
- SurfaceControl dimLayer = getDimLayer();
+ SurfaceControl dimLayer = mDimmer.getDimLayer();
assertNotNull("Dimmer should have created a surface", dimLayer);
verify(mHost.getPendingTransaction()).setBackgroundBlurRadius(dimLayer, blurRadius);
verify(mHost.getPendingTransaction()).setRelativeLayer(dimLayer, child.mControl, -1);
}
-
- private SurfaceControl getDimLayer() {
- return mDimmer.mDimState.mDimLayer;
- }
}
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/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/TaskFragmentOrganizerControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
index 2bf1385..6235b3b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
@@ -26,7 +26,9 @@
import static android.view.WindowManager.TRANSIT_OPEN;
import static android.window.TaskFragmentOperation.OP_TYPE_CREATE_TASK_FRAGMENT;
import static android.window.TaskFragmentOperation.OP_TYPE_DELETE_TASK_FRAGMENT;
+import static android.window.TaskFragmentOperation.OP_TYPE_REORDER_TO_BOTTOM_OF_TASK;
import static android.window.TaskFragmentOperation.OP_TYPE_REORDER_TO_FRONT;
+import static android.window.TaskFragmentOperation.OP_TYPE_REORDER_TO_TOP_OF_TASK;
import static android.window.TaskFragmentOperation.OP_TYPE_REPARENT_ACTIVITY_TO_TASK_FRAGMENT;
import static android.window.TaskFragmentOperation.OP_TYPE_SET_ADJACENT_TASK_FRAGMENTS;
import static android.window.TaskFragmentOperation.OP_TYPE_SET_ANIMATION_PARAMS;
@@ -1655,6 +1657,127 @@
assertEquals(frontMostTaskFragment, tf0);
}
+ @Test
+ public void testApplyTransaction_reorderToBottomOfTask() {
+ mController.unregisterOrganizer(mIOrganizer);
+ mController.registerOrganizerInternal(mIOrganizer, true /* isSystemOrganizer */);
+ final Task task = createTask(mDisplayContent);
+ // Create a non-embedded Activity at the bottom.
+ final ActivityRecord bottomActivity = new ActivityBuilder(mAtm)
+ .setTask(task)
+ .build();
+ final TaskFragment tf0 = createTaskFragment(task);
+ final TaskFragment tf1 = createTaskFragment(task);
+ // Create a non-embedded Activity at the top.
+ final ActivityRecord topActivity = new ActivityBuilder(mAtm)
+ .setTask(task)
+ .build();
+
+ // Ensure correct order of the children before the operation
+ assertEquals(topActivity, task.getChildAt(3).asActivityRecord());
+ assertEquals(tf1, task.getChildAt(2).asTaskFragment());
+ assertEquals(tf0, task.getChildAt(1).asTaskFragment());
+ assertEquals(bottomActivity, task.getChildAt(0).asActivityRecord());
+
+ // Reorder TaskFragment to bottom
+ final TaskFragmentOperation operation = new TaskFragmentOperation.Builder(
+ OP_TYPE_REORDER_TO_BOTTOM_OF_TASK).build();
+ mTransaction.addTaskFragmentOperation(tf1.getFragmentToken(), operation);
+ assertApplyTransactionAllowed(mTransaction);
+
+ // Ensure correct order of the children after the operation
+ assertEquals(topActivity, task.getChildAt(3).asActivityRecord());
+ assertEquals(tf0, task.getChildAt(2).asTaskFragment());
+ assertEquals(bottomActivity, task.getChildAt(1).asActivityRecord());
+ assertEquals(tf1, task.getChildAt(0).asTaskFragment());
+ }
+
+ @Test
+ public void testApplyTransaction_reorderToTopOfTask() {
+ mController.unregisterOrganizer(mIOrganizer);
+ mController.registerOrganizerInternal(mIOrganizer, true /* isSystemOrganizer */);
+ final Task task = createTask(mDisplayContent);
+ // Create a non-embedded Activity at the bottom.
+ final ActivityRecord bottomActivity = new ActivityBuilder(mAtm)
+ .setTask(task)
+ .build();
+ final TaskFragment tf0 = createTaskFragment(task);
+ final TaskFragment tf1 = createTaskFragment(task);
+ // Create a non-embedded Activity at the top.
+ final ActivityRecord topActivity = new ActivityBuilder(mAtm)
+ .setTask(task)
+ .build();
+
+ // Ensure correct order of the children before the operation
+ assertEquals(topActivity, task.getChildAt(3).asActivityRecord());
+ assertEquals(tf1, task.getChildAt(2).asTaskFragment());
+ assertEquals(tf0, task.getChildAt(1).asTaskFragment());
+ assertEquals(bottomActivity, task.getChildAt(0).asActivityRecord());
+
+ // Reorder TaskFragment to top
+ final TaskFragmentOperation operation = new TaskFragmentOperation.Builder(
+ OP_TYPE_REORDER_TO_TOP_OF_TASK).build();
+ mTransaction.addTaskFragmentOperation(tf0.getFragmentToken(), operation);
+ assertApplyTransactionAllowed(mTransaction);
+
+ // Ensure correct order of the children after the operation
+ assertEquals(tf0, task.getChildAt(3).asTaskFragment());
+ assertEquals(topActivity, task.getChildAt(2).asActivityRecord());
+ assertEquals(tf1, task.getChildAt(1).asTaskFragment());
+ assertEquals(bottomActivity, task.getChildAt(0).asActivityRecord());
+ }
+
+ @Test
+ public void testApplyTransaction_reorderToBottomOfTask_failsIfNotSystemOrganizer() {
+ testApplyTransaction_reorder_failsIfNotSystemOrganizer_common(
+ OP_TYPE_REORDER_TO_BOTTOM_OF_TASK);
+ }
+
+ @Test
+ public void testApplyTransaction_reorderToTopOfTask_failsIfNotSystemOrganizer() {
+ testApplyTransaction_reorder_failsIfNotSystemOrganizer_common(
+ OP_TYPE_REORDER_TO_TOP_OF_TASK);
+ }
+
+ private void testApplyTransaction_reorder_failsIfNotSystemOrganizer_common(
+ @TaskFragmentOperation.OperationType int opType) {
+ final Task task = createTask(mDisplayContent);
+ // Create a non-embedded Activity at the bottom.
+ final ActivityRecord bottomActivity = new ActivityBuilder(mAtm)
+ .setTask(task)
+ .build();
+ final TaskFragment tf0 = createTaskFragment(task);
+ final TaskFragment tf1 = createTaskFragment(task);
+ // Create a non-embedded Activity at the top.
+ final ActivityRecord topActivity = new ActivityBuilder(mAtm)
+ .setTask(task)
+ .build();
+
+ // Ensure correct order of the children before the operation
+ assertEquals(topActivity, task.getChildAt(3).asActivityRecord());
+ assertEquals(tf1, task.getChildAt(2).asTaskFragment());
+ assertEquals(tf0, task.getChildAt(1).asTaskFragment());
+ assertEquals(bottomActivity, task.getChildAt(0).asActivityRecord());
+
+ // Apply reorder transaction, which is expected to fail for non-system organizer.
+ final TaskFragmentOperation operation = new TaskFragmentOperation.Builder(
+ opType).build();
+ mTransaction
+ .addTaskFragmentOperation(tf0.getFragmentToken(), operation)
+ .setErrorCallbackToken(mErrorToken);
+ assertApplyTransactionAllowed(mTransaction);
+ // The pending event will be dispatched on the handler (from requestTraversal).
+ waitHandlerIdle(mWm.mAnimationHandler);
+
+ assertTaskFragmentErrorTransaction(opType, SecurityException.class);
+
+ // Ensure no change to the order of the children after the operation
+ assertEquals(topActivity, task.getChildAt(3).asActivityRecord());
+ assertEquals(tf1, task.getChildAt(2).asTaskFragment());
+ assertEquals(tf0, task.getChildAt(1).asTaskFragment());
+ assertEquals(bottomActivity, task.getChildAt(0).asActivityRecord());
+ }
+
/**
* Creates a {@link TaskFragment} with the {@link WindowContainerTransaction}. Calls
* {@link WindowOrganizerController#applyTransaction(WindowContainerTransaction)} to apply the
@@ -1782,6 +1905,19 @@
assertEquals(activityToken, change.getActivityToken());
}
+ /** Setups an embedded TaskFragment. */
+ private TaskFragment createTaskFragment(Task task) {
+ final IBinder token = new Binder();
+ TaskFragment taskFragment = new TaskFragmentBuilder(mAtm)
+ .setParentTask(task)
+ .setFragmentToken(token)
+ .setOrganizer(mOrganizer)
+ .createActivityCount(1)
+ .build();
+ mWindowOrganizerController.mLaunchTaskFragments.put(token, taskFragment);
+ return taskFragment;
+ }
+
/** Setups an embedded TaskFragment in a PIP Task. */
private void setupTaskFragmentInPip() {
mTaskFragment = new TaskFragmentBuilder(mAtm)
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
index 5205bb0..7822071 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
@@ -37,6 +37,8 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.server.wm.ActivityRecord.State.RESUMED;
+import static com.android.server.wm.TaskFragment.EMBEDDED_DIM_AREA_PARENT_TASK;
+import static com.android.server.wm.TaskFragment.EMBEDDED_DIM_AREA_TASK_FRAGMENT;
import static com.android.server.wm.TaskFragment.EMBEDDING_DISALLOWED_MIN_DIMENSION_VIOLATION;
import static com.android.server.wm.TaskFragment.EMBEDDING_DISALLOWED_UNTRUSTED_HOST;
import static com.android.server.wm.WindowContainer.POSITION_TOP;
@@ -687,4 +689,24 @@
tf0.setIsolatedNav(true);
assertTrue(tf0.isIsolatedNav());
}
+
+ @Test
+ public void testGetDimBounds() {
+ final Task task = mTaskFragment.getTask();
+ final Rect taskBounds = task.getBounds();
+ mTaskFragment.setBounds(taskBounds.left, taskBounds.top, taskBounds.left + 10,
+ taskBounds.top + 10);
+ final Rect taskFragmentBounds = mTaskFragment.getBounds();
+
+ // Return Task bounds if dimming on parent Task.
+ final Rect dimBounds = new Rect();
+ mTaskFragment.setEmbeddedDimArea(EMBEDDED_DIM_AREA_PARENT_TASK);
+ mTaskFragment.getDimBounds(dimBounds);
+ assertEquals(taskBounds, dimBounds);
+
+ // Return TF bounds by default.
+ mTaskFragment.setEmbeddedDimArea(EMBEDDED_DIM_AREA_TASK_FRAGMENT);
+ mTaskFragment.getDimBounds(dimBounds);
+ assertEquals(taskFragmentBounds, dimBounds);
+ }
}
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/WindowOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
index 7168670..0b77fd8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
@@ -40,6 +40,7 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
+import static com.android.server.wm.testing.Assert.assertThrows;
import static com.android.server.wm.ActivityRecord.State.RESUMED;
import static com.android.server.wm.WindowContainer.POSITION_TOP;
import static com.android.server.wm.WindowContainer.SYNC_STATE_READY;
@@ -58,6 +59,7 @@
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.clearInvocations;
+import android.annotation.NonNull;
import android.app.ActivityManager;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.ActivityOptions;
@@ -77,11 +79,13 @@
import android.view.Display;
import android.view.SurfaceControl;
import android.view.WindowInsets;
+import android.window.ITaskFragmentOrganizer;
import android.window.ITaskOrganizer;
import android.window.IWindowContainerTransactionCallback;
import android.window.StartingWindowInfo;
import android.window.StartingWindowRemovalInfo;
import android.window.TaskAppearedInfo;
+import android.window.TaskFragmentOrganizer;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
@@ -579,6 +583,87 @@
}
@Test
+ public void testTaskFragmentHiddenAndFocusableChanges() {
+ removeGlobalMinSizeRestriction();
+ final Task rootTask = new TaskBuilder(mSupervisor).setCreateActivity(true)
+ .setWindowingMode(WINDOWING_MODE_FULLSCREEN).build();
+
+ final WindowContainerTransaction t = new WindowContainerTransaction();
+ final TaskFragmentOrganizer organizer =
+ createTaskFragmentOrganizer(t, true /* isSystemOrganizer */);
+
+ final IBinder token = new Binder();
+ final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm)
+ .setParentTask(rootTask)
+ .setFragmentToken(token)
+ .setOrganizer(organizer)
+ .createActivityCount(1)
+ .build();
+
+ // Should be visible and focusable initially.
+ assertTrue(rootTask.shouldBeVisible(null));
+ assertTrue(taskFragment.shouldBeVisible(null));
+ assertTrue(taskFragment.isFocusable());
+ assertTrue(taskFragment.isTopActivityFocusable());
+
+ // Apply transaction to the TaskFragment hidden and not focusable.
+ t.setHidden(taskFragment.mRemoteToken.toWindowContainerToken(), true);
+ t.setFocusable(taskFragment.mRemoteToken.toWindowContainerToken(), false);
+ mWm.mAtmService.mWindowOrganizerController.applyTaskFragmentTransactionLocked(
+ t, TaskFragmentOrganizer.TASK_FRAGMENT_TRANSIT_CHANGE,
+ false /* shouldApplyIndependently */);
+
+ // Should be not visible and not focusable after the transaction.
+ assertFalse(taskFragment.shouldBeVisible(null));
+ assertFalse(taskFragment.isFocusable());
+ assertFalse(taskFragment.isTopActivityFocusable());
+
+ // Apply transaction to the TaskFragment not hidden and focusable.
+ t.setHidden(taskFragment.mRemoteToken.toWindowContainerToken(), false);
+ t.setFocusable(taskFragment.mRemoteToken.toWindowContainerToken(), true);
+ mWm.mAtmService.mWindowOrganizerController.applyTaskFragmentTransactionLocked(
+ t, TaskFragmentOrganizer.TASK_FRAGMENT_TRANSIT_CHANGE,
+ false /* shouldApplyIndependently */);
+
+ // Should be visible and focusable after the transaction.
+ assertTrue(taskFragment.shouldBeVisible(null));
+ assertTrue(taskFragment.isFocusable());
+ assertTrue(taskFragment.isTopActivityFocusable());
+ }
+
+ @Test
+ public void testTaskFragmentHiddenAndFocusableChanges_throwsWhenNotSystemOrganizer() {
+ removeGlobalMinSizeRestriction();
+ final Task rootTask = new TaskBuilder(mSupervisor).setCreateActivity(true)
+ .setWindowingMode(WINDOWING_MODE_FULLSCREEN).build();
+
+ final WindowContainerTransaction t = new WindowContainerTransaction();
+ final TaskFragmentOrganizer organizer =
+ createTaskFragmentOrganizer(t, false /* isSystemOrganizer */);
+
+ final IBinder token = new Binder();
+ final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm)
+ .setParentTask(rootTask)
+ .setFragmentToken(token)
+ .setOrganizer(organizer)
+ .createActivityCount(1)
+ .build();
+
+ assertTrue(rootTask.shouldBeVisible(null));
+ assertTrue(taskFragment.shouldBeVisible(null));
+
+ t.setHidden(taskFragment.mRemoteToken.toWindowContainerToken(), true);
+ t.setFocusable(taskFragment.mRemoteToken.toWindowContainerToken(), false);
+
+ // Non-system organizers are not allow to update the hidden and focusable states.
+ assertThrows(SecurityException.class, () ->
+ mWm.mAtmService.mWindowOrganizerController.applyTaskFragmentTransactionLocked(
+ t, TaskFragmentOrganizer.TASK_FRAGMENT_TRANSIT_CHANGE,
+ false /* shouldApplyIndependently */)
+ );
+ }
+
+ @Test
public void testContainerTranslucentChanges() {
removeGlobalMinSizeRestriction();
final Task rootTask = new TaskBuilder(mSupervisor).setCreateActivity(true)
@@ -1600,4 +1685,20 @@
assertTrue(taskIds.contains(expectedTasks[i].mTaskId));
}
}
+
+ @NonNull
+ private TaskFragmentOrganizer createTaskFragmentOrganizer(
+ @NonNull WindowContainerTransaction t, boolean isSystemOrganizer) {
+ final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
+ final ITaskFragmentOrganizer organizerInterface =
+ ITaskFragmentOrganizer.Stub.asInterface(organizer.getOrganizerToken().asBinder());
+ mWm.mAtmService.mWindowOrganizerController.mTaskFragmentOrganizerController
+ .registerOrganizerInternal(
+ ITaskFragmentOrganizer.Stub.asInterface(
+ organizer.getOrganizerToken().asBinder()),
+ isSystemOrganizer);
+ t.setTaskFragmentOrganizer(organizerInterface);
+
+ return organizer;
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/utils/LastCallVerifier.java b/services/tests/wmtests/src/com/android/server/wm/utils/LastCallVerifier.java
new file mode 100644
index 0000000..320d094
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/utils/LastCallVerifier.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.utils;
+
+import org.mockito.internal.verification.VerificationModeFactory;
+import org.mockito.internal.verification.api.VerificationData;
+import org.mockito.invocation.Invocation;
+import org.mockito.invocation.MatchableInvocation;
+import org.mockito.verification.VerificationMode;
+
+import java.util.List;
+
+/**
+ * Verifier to check that the last call of a method received the expected argument
+ */
+public class LastCallVerifier implements VerificationMode {
+
+ /**
+ * Allows comparing the expected invocation with the last invocation on the same method
+ */
+ public static LastCallVerifier lastCall() {
+ return new LastCallVerifier();
+ }
+
+ @Override
+ public void verify(VerificationData data) {
+ List<Invocation> invocations = data.getAllInvocations();
+ MatchableInvocation target = data.getTarget();
+ for (int i = invocations.size() - 1; i >= 0; i--) {
+ final Invocation invocation = invocations.get(i);
+ if (target.hasSameMethod(invocation)) {
+ if (target.matches(invocation)) {
+ return;
+ } else {
+ throw new LastCallMismatch(target.getInvocation(), invocation, invocations);
+ }
+ }
+ }
+ throw new RuntimeException(target + " never invoked");
+ }
+
+ @Override
+ public VerificationMode description(String description) {
+ return VerificationModeFactory.description(this, description);
+ }
+
+ static class LastCallMismatch extends RuntimeException {
+ LastCallMismatch(
+ Invocation expected, Invocation received, List<Invocation> allInvocations) {
+ super("Expected invocation " + expected + " but received " + received
+ + " as the last invocation.\nAll registered invocations:\n" + allInvocations);
+ }
+ }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/utils/MockAnimationAdapter.java b/services/tests/wmtests/src/com/android/server/wm/utils/MockAnimationAdapter.java
new file mode 100644
index 0000000..1a66970a
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/utils/MockAnimationAdapter.java
@@ -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.server.wm.utils;
+
+import android.util.proto.ProtoOutputStream;
+import android.view.SurfaceControl;
+
+import androidx.annotation.NonNull;
+
+import com.android.server.wm.AnimationAdapter;
+import com.android.server.wm.SurfaceAnimator;
+
+import java.io.PrintWriter;
+
+/**
+ * An empty animation adapter which just executes the finish callback
+ */
+public class MockAnimationAdapter implements AnimationAdapter {
+
+ @Override
+ public boolean getShowWallpaper() {
+ return false;
+ }
+
+ @Override
+ public void startAnimation(SurfaceControl animationLeash, SurfaceControl.Transaction t,
+ int type, @NonNull SurfaceAnimator.OnAnimationFinishedCallback finishCallback) {
+ // As the animation won't run, finish it immediately
+ finishCallback.onAnimationFinished(0, null);
+ }
+
+ @Override
+ public void onAnimationCancelled(SurfaceControl animationLeash) {}
+
+ @Override
+ public long getDurationHint() {
+ return 0;
+ }
+
+ @Override
+ public long getStatusBarTransitionsStartTime() {
+ return 0;
+ }
+
+ @Override
+ public void dump(PrintWriter pw, String prefix) {}
+
+ @Override
+ public void dumpDebug(ProtoOutputStream proto) {}
+}
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index f3bf026..55b5d11 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -53,6 +53,7 @@
import android.app.usage.BroadcastResponseStatsList;
import android.app.usage.ConfigurationStats;
import android.app.usage.EventStats;
+import android.app.usage.Flags;
import android.app.usage.IUsageStatsManager;
import android.app.usage.UsageEvents;
import android.app.usage.UsageEvents.Event;
@@ -200,6 +201,7 @@
static final int MSG_ON_START = 7;
static final int MSG_HANDLE_LAUNCH_TIME_ON_USER_UNLOCK = 8;
static final int MSG_NOTIFY_ESTIMATED_LAUNCH_TIMES_CHANGED = 9;
+ static final int MSG_UID_REMOVED = 10;
private final Object mLock = new Object();
private Handler mHandler;
@@ -378,11 +380,10 @@
IntentFilter filter = new IntentFilter(Intent.ACTION_USER_REMOVED);
filter.addAction(Intent.ACTION_USER_STARTED);
- getContext().registerReceiverAsUser(new UserActionsReceiver(), UserHandle.ALL, filter,
- null, mHandler);
-
+ getContext().registerReceiverAsUser(new UserActionsReceiver(), UserHandle.ALL,
+ filter, null, /* Handler scheduler */ null);
getContext().registerReceiverAsUser(new UidRemovedReceiver(), UserHandle.ALL,
- new IntentFilter(ACTION_UID_REMOVED), null, mHandler);
+ new IntentFilter(ACTION_UID_REMOVED), null, /* Handler scheduler */ null);
mRealTimeSnapshot = SystemClock.elapsedRealtime();
mSystemTimeSnapshot = System.currentTimeMillis();
@@ -614,7 +615,6 @@
if (Intent.ACTION_USER_REMOVED.equals(action)) {
if (userId >= 0) {
mHandler.obtainMessage(MSG_REMOVE_USER, userId, 0).sendToTarget();
- mResponseStatsTracker.onUserRemoved(userId);
}
} else if (Intent.ACTION_USER_STARTED.equals(action)) {
if (userId >= 0) {
@@ -632,9 +632,7 @@
return;
}
- synchronized (mLock) {
- mResponseStatsTracker.onUidRemoved(uid);
- }
+ mHandler.obtainMessage(MSG_UID_REMOVED, uid, 0).sendToTarget();
}
}
@@ -1301,6 +1299,8 @@
mPendingLaunchTimeChangePackages.remove(userId);
}
mAppStandby.onUserRemoved(userId);
+ mResponseStatsTracker.onUserRemoved(userId);
+
// Cancel any scheduled jobs for this user since the user is being removed.
UsageStatsIdleService.cancelPruneJob(getContext(), userId);
UsageStatsIdleService.cancelUpdateMappingsJob(getContext(), userId);
@@ -2037,6 +2037,9 @@
case MSG_REMOVE_USER:
onUserRemoved(msg.arg1);
break;
+ case MSG_UID_REMOVED:
+ mResponseStatsTracker.onUidRemoved(msg.arg1);
+ break;
case MSG_PACKAGE_REMOVED:
onPackageRemoved(msg.arg1, (String) msg.obj);
break;
@@ -2125,7 +2128,8 @@
private boolean canReportUsageStats() {
if (isCallingUidSystem()) {
- return true; // System UID can always report UsageStats
+ // System UID can always report UsageStats
+ return true;
}
return getContext().checkCallingPermission(Manifest.permission.REPORT_USAGE_STATS)
@@ -2621,9 +2625,12 @@
return;
}
- if (!canReportUsageStats()) {
- throw new SecurityException("Only the system or holders of the REPORT_USAGE_STATS"
- + " permission are allowed to call reportChooserSelection");
+ if (Flags.reportUsageStatsPermission()) {
+ if (!canReportUsageStats()) {
+ throw new SecurityException(
+ "Only the system or holders of the REPORT_USAGE_STATS"
+ + " permission are allowed to call reportChooserSelection");
+ }
}
// Verify if this package exists before reporting an event for it.
@@ -2643,9 +2650,17 @@
@Override
public void reportUserInteraction(String packageName, int userId) {
Objects.requireNonNull(packageName);
- if (!canReportUsageStats()) {
- throw new SecurityException("Only the system or holders of the REPORT_USAGE_STATS"
- + " permission are allowed to call reportUserInteraction");
+ if (Flags.reportUsageStatsPermission()) {
+ if (!canReportUsageStats()) {
+ throw new SecurityException(
+ "Only the system or holders of the REPORT_USAGE_STATS"
+ + " permission are allowed to call reportUserInteraction");
+ }
+ } else {
+ if (!isCallingUidSystem()) {
+ throw new SecurityException("Only system is allowed to call"
+ + " reportUserInteraction");
+ }
}
final Event event = new Event(USER_INTERACTION, SystemClock.elapsedRealtime());
diff --git a/services/usb/java/com/android/server/usb/UsbService.java b/services/usb/java/com/android/server/usb/UsbService.java
index 5d2f27d..35e2fcf 100644
--- a/services/usb/java/com/android/server/usb/UsbService.java
+++ b/services/usb/java/com/android/server/usb/UsbService.java
@@ -37,8 +37,8 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
-import android.hardware.usb.IUsbManager;
import android.hardware.usb.IDisplayPortAltModeInfoListener;
+import android.hardware.usb.IUsbManager;
import android.hardware.usb.IUsbOperationInternal;
import android.hardware.usb.ParcelableUsbPort;
import android.hardware.usb.UsbAccessory;
@@ -46,7 +46,6 @@
import android.hardware.usb.UsbManager;
import android.hardware.usb.UsbPort;
import android.hardware.usb.UsbPortStatus;
-import android.hardware.usb.DisplayPortAltModeInfo;
import android.os.Binder;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
@@ -1215,6 +1214,20 @@
mPortManager.dump(new DualDumpOutputStream(new IndentingPrintWriter(pw, " ")),
"", 0);
}
+ } else if ("enable-usb-data".equals(args[0]) && args.length == 3) {
+ final String portId = args[1];
+ final boolean enable = Boolean.parseBoolean(args[2]);
+
+ if (mPortManager != null) {
+ for (UsbPort p : mPortManager.getPorts()) {
+ if (p.getId().equals(portId)) {
+ int res = p.enableUsbData(enable);
+ Slog.i(TAG, "enableUsbData " + portId + " status " + res);
+ break;
+ }
+ }
+ }
+
} else if ("ports".equals(args[0]) && args.length == 1) {
if (mPortManager != null) {
mPortManager.dump(new DualDumpOutputStream(new IndentingPrintWriter(pw, " ")),
@@ -1293,6 +1306,11 @@
pw.println("reset-displayport-status can also be used in order to set");
pw.println("the DisplayPortInfo to default values.");
pw.println();
+ pw.println("Example enableUsbData");
+ pw.println("This dumpsys command functions for both simulated and real ports.");
+ pw.println(" dumpsys usb enable-usb-data \"matrix\" true");
+ pw.println(" dumpsys usb enable-usb-data \"matrix\" false");
+ pw.println();
pw.println("Example USB device descriptors:");
pw.println(" dumpsys usb dump-descriptors -dump-short");
pw.println(" dumpsys usb dump-descriptors -dump-tree");
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index a7675d6..def52a5 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -942,6 +942,7 @@
* @return the Telecom identifier associated with this {@link Call} . This is not a stable
* identifier and is not guaranteed to be unique across device reboots.
*/
+ @FlaggedApi(Flags.FLAG_CALL_DETAILS_ID_CHANGES)
public @NonNull String getId() { return mTelecomCallId; }
/** {@hide} */
diff --git a/telecomm/java/android/telecom/StreamingCall.java b/telecomm/java/android/telecom/StreamingCall.java
index 29f436d..ad1b6f9 100644
--- a/telecomm/java/android/telecom/StreamingCall.java
+++ b/telecomm/java/android/telecom/StreamingCall.java
@@ -16,6 +16,7 @@
package android.telecom;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.SystemApi;
@@ -25,6 +26,8 @@
import android.os.Parcel;
import android.os.Parcelable;
+import com.android.server.telecom.flags.Flags;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -57,6 +60,7 @@
/**
* The ID associated with this call. This is the same value as {@link CallControl#getCallId()}.
*/
+ @FlaggedApi(Flags.FLAG_CALL_DETAILS_ID_CHANGES)
public static final String EXTRA_CALL_ID = "android.telecom.extra.CALL_ID";
/**
diff --git a/telephony/java/android/telephony/BarringInfo.java b/telephony/java/android/telephony/BarringInfo.java
index 971fc78..e20e4d2 100644
--- a/telephony/java/android/telephony/BarringInfo.java
+++ b/telephony/java/android/telephony/BarringInfo.java
@@ -202,6 +202,24 @@
&& mConditionalBarringTimeSeconds == other.mConditionalBarringTimeSeconds;
}
+ private static String barringTypeToString(@BarringType int barringType) {
+ return switch (barringType) {
+ case BARRING_TYPE_NONE -> "NONE";
+ case BARRING_TYPE_CONDITIONAL -> "CONDITIONAL";
+ case BARRING_TYPE_UNCONDITIONAL -> "UNCONDITIONAL";
+ case BARRING_TYPE_UNKNOWN -> "UNKNOWN";
+ default -> "UNKNOWN(" + barringType + ")";
+ };
+ }
+
+ @Override
+ public String toString() {
+ return "BarringServiceInfo {mBarringType=" + barringTypeToString(mBarringType)
+ + ", mIsConditionallyBarred=" + mIsConditionallyBarred
+ + ", mConditionalBarringFactor=" + mConditionalBarringFactor
+ + ", mConditionalBarringTimeSeconds=" + mConditionalBarringTimeSeconds + "}";
+ }
+
/** @hide */
public BarringServiceInfo(Parcel p) {
mBarringType = p.readInt();
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 63e91ad..7a0bf90 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -10523,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 cb4a6e7..7ccc27e 100644
--- a/telephony/java/android/telephony/NetworkRegistrationInfo.java
+++ b/telephony/java/android/telephony/NetworkRegistrationInfo.java
@@ -16,6 +16,7 @@
package android.telephony;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -30,6 +31,8 @@
import android.telephony.Annotation.NetworkType;
import android.text.TextUtils;
+import com.android.internal.telephony.flags.Flags;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
@@ -206,6 +209,7 @@
/**
* MMS service
*/
+ @FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG)
public static final int SERVICE_TYPE_MMS = 6;
/** @hide */
@@ -702,6 +706,7 @@
*
* @return {@code true} if network is a non-terrestrial network else {@code false}.
*/
+ @FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG)
public boolean isNonTerrestrialNetwork() {
return mIsNonTerrestrialNetwork;
}
@@ -1186,6 +1191,7 @@
* else {@code false}.
* @return The builder.
*/
+ @FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG)
public @NonNull Builder setIsNonTerrestrialNetwork(boolean isNonTerrestrialNetwork) {
mIsNonTerrestrialNetwork = isNonTerrestrialNetwork;
return this;
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index 5b8848c..85a85c6 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -16,6 +16,7 @@
package android.telephony;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -35,6 +36,7 @@
import android.telephony.NetworkRegistrationInfo.NRState;
import android.text.TextUtils;
+import com.android.internal.telephony.flags.Flags;
import com.android.telephony.Rlog;
import java.lang.annotation.Retention;
@@ -2256,6 +2258,7 @@
*
* @return {@code true} if device is connected to a non-terrestrial network else {@code false}.
*/
+ @FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG)
public boolean isUsingNonTerrestrialNetwork() {
synchronized (mNetworkRegistrationInfos) {
for (NetworkRegistrationInfo nri : mNetworkRegistrationInfos) {
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 548fa97..73220c3 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";
@@ -15030,14 +15033,6 @@
@TestApi
public static final int HAL_SERVICE_IMS = 7;
- /**
- * HAL service type that supports the HAL APIs implementation of IRadioSatellite
- * {@link RadioSatelliteProxy}
- * @hide
- */
- @TestApi
- public static final int HAL_SERVICE_SATELLITE = 8;
-
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = {"HAL_SERVICE_"},
@@ -15050,7 +15045,6 @@
HAL_SERVICE_SIM,
HAL_SERVICE_VOICE,
HAL_SERVICE_IMS,
- HAL_SERVICE_SATELLITE
})
public @interface HalService {}
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/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java
index 72e4389..a20f26c 100644
--- a/telephony/java/com/android/internal/telephony/RILConstants.java
+++ b/telephony/java/com/android/internal/telephony/RILConstants.java
@@ -120,31 +120,6 @@
int BLOCKED_DUE_TO_CALL = 69; /* SMS is blocked due to call control */
int RF_HARDWARE_ISSUE = 70; /* RF HW issue is detected */
int NO_RF_CALIBRATION_INFO = 71; /* No RF calibration in device */
- int ENCODING_NOT_SUPPORTED = 72; /* The encoding scheme is not supported by
- either the network or the MS. */
- int FEATURE_NOT_SUPPORTED = 73; /* The requesting feature is not supported by
- the service provider. */
- int INVALID_CONTACT = 74; /* The contact to be added is either not
- existing or not valid. */
- int MODEM_INCOMPATIBLE = 75; /* The modem of the MS is not compatible with
- the service provider. */
- int NETWORK_TIMEOUT = 76; /* Modem timeout to receive ACK or response from
- network after sending a request to it. */
- int NO_SATELLITE_SIGNAL = 77; /* Modem fails to communicate with the satellite
- network since there is no satellite signal.*/
- int NOT_SUFFICIENT_ACCOUNT_BALANCE = 78; /* The request cannot be performed since the
- subscriber's account balance is not
- sufficient. */
- int RADIO_TECHNOLOGY_NOT_SUPPORTED = 79; /* The radio technology is not supported by the
- service provider. */
- int SUBSCRIBER_NOT_AUTHORIZED = 80; /* The subscription is not authorized to
- register with the service provider. */
- int SWITCHED_FROM_SATELLITE_TO_TERRESTRIAL = 81; /* While processing a request from the
- Framework the satellite modem detects
- terrestrial signal, aborts the request, and
- switches to the terrestrial network. */
- int UNIDENTIFIED_SUBSCRIBER = 82; /* The subscriber is not registered with the
- service provider */
// Below is list of OEM specific error codes which can by used by OEMs in case they don't want to
// reveal particular replacement for Generic failure
@@ -568,22 +543,7 @@
int RIL_REQUEST_IS_N1_MODE_ENABLED = 242;
int RIL_REQUEST_SET_LOCATION_PRIVACY_SETTING = 243;
int RIL_REQUEST_GET_LOCATION_PRIVACY_SETTING = 244;
- int RIL_REQUEST_GET_SATELLITE_CAPABILITIES = 245;
- int RIL_REQUEST_SET_SATELLITE_POWER = 246;
- int RIL_REQUEST_GET_SATELLITE_POWER = 247;
- int RIL_REQUEST_PROVISION_SATELLITE_SERVICE = 248;
- int RIL_REQUEST_ADD_ALLOWED_SATELLITE_CONTACTS = 249;
- int RIL_REQUEST_REMOVE_ALLOWED_SATELLITE_CONTACTS = 250;
- int RIL_REQUEST_SEND_SATELLITE_MESSAGES = 251;
- int RIL_REQUEST_GET_PENDING_SATELLITE_MESSAGES = 252;
- int RIL_REQUEST_GET_SATELLITE_MODE = 253;
- int RIL_REQUEST_SET_SATELLITE_INDICATION_FILTER = 254;
- int RIL_REQUEST_START_SENDING_SATELLITE_POINTING_INFO = 255;
- int RIL_REQUEST_STOP_SENDING_SATELLITE_POINTING_INFO = 256;
- int RIL_REQUEST_GET_MAX_CHARACTERS_PER_SATELLITE_TEXT_MESSAGE = 257;
- int RIL_REQUEST_GET_TIME_FOR_NEXT_SATELLITE_VISIBILITY = 258;
- int RIL_REQUEST_IS_NULL_CIPHER_AND_INTEGRITY_ENABLED = 259;
- int RIL_REQUEST_SET_SATELLITE_PLMN = 260;
+ int RIL_REQUEST_IS_NULL_CIPHER_AND_INTEGRITY_ENABLED = 245;
/* Responses begin */
int RIL_RESPONSE_ACKNOWLEDGEMENT = 800;
@@ -645,13 +605,6 @@
int RIL_UNSOL_RESPONSE_SIM_PHONEBOOK_CHANGED = 1053;
int RIL_UNSOL_RESPONSE_SIM_PHONEBOOK_RECORDS_RECEIVED = 1054;
int RIL_UNSOL_SLICING_CONFIG_CHANGED = 1055;
- int RIL_UNSOL_PENDING_SATELLITE_MESSAGE_COUNT = 1056;
- int RIL_UNSOL_NEW_SATELLITE_MESSAGES = 1057;
- int RIL_UNSOL_SATELLITE_MESSAGES_TRANSFER_COMPLETE = 1058;
- int RIL_UNSOL_SATELLITE_POINTING_INFO_CHANGED = 1059;
- int RIL_UNSOL_SATELLITE_MODE_CHANGED = 1060;
- int RIL_UNSOL_SATELLITE_RADIO_TECHNOLOGY_CHANGED = 1061;
- int RIL_UNSOL_SATELLITE_PROVISION_STATE_CHANGED = 1062;
/* The following unsols are not defined in RIL.h */
int RIL_UNSOL_HAL_NON_RIL_BASE = 1100;
diff --git a/test-mock/api/current.txt b/test-mock/api/current.txt
index d1a68d4..f61cce6 100644
--- a/test-mock/api/current.txt
+++ b/test-mock/api/current.txt
@@ -200,7 +200,7 @@
method @Deprecated public int checkPermission(String, String);
method @Deprecated public int checkSignatures(String, String);
method @Deprecated public int checkSignatures(int, int);
- method public void clearInstantAppCookie();
+ method @Deprecated public void clearInstantAppCookie();
method @Deprecated public void clearPackagePreferredActivities(String);
method @Deprecated public String[] currentToCanonicalPackageNames(String[]);
method @Deprecated public void extendVerificationTimeout(int, int, long);
@@ -222,15 +222,15 @@
method @Deprecated public CharSequence getApplicationLabel(android.content.pm.ApplicationInfo);
method @Deprecated public android.graphics.drawable.Drawable getApplicationLogo(android.content.pm.ApplicationInfo);
method @Deprecated public android.graphics.drawable.Drawable getApplicationLogo(String) throws android.content.pm.PackageManager.NameNotFoundException;
- method public android.content.pm.ChangedPackages getChangedPackages(int);
+ method @Deprecated public android.content.pm.ChangedPackages getChangedPackages(int);
method @Deprecated public int getComponentEnabledSetting(android.content.ComponentName);
method @Deprecated public android.graphics.drawable.Drawable getDefaultActivityIcon();
method @Deprecated public android.graphics.drawable.Drawable getDrawable(String, int, android.content.pm.ApplicationInfo);
method @Deprecated public java.util.List<android.content.pm.ApplicationInfo> getInstalledApplications(int);
method @Deprecated public java.util.List<android.content.pm.PackageInfo> getInstalledPackages(int);
method @Deprecated public String getInstallerPackageName(String);
- method public byte[] getInstantAppCookie();
- method public int getInstantAppCookieMaxBytes();
+ method @Deprecated public byte[] getInstantAppCookie();
+ method @Deprecated public int getInstantAppCookieMaxBytes();
method @Deprecated public android.content.pm.InstrumentationInfo getInstrumentationInfo(android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
method @Deprecated public android.content.Intent getLaunchIntentForPackage(String);
method @Deprecated public android.content.Intent getLeanbackLaunchIntentForPackage(String);
@@ -239,7 +239,7 @@
method @Deprecated public int[] getPackageGids(String, int) throws android.content.pm.PackageManager.NameNotFoundException;
method @Deprecated public android.content.pm.PackageInfo getPackageInfo(String, int) throws android.content.pm.PackageManager.NameNotFoundException;
method @Deprecated public android.content.pm.PackageInfo getPackageInfo(android.content.pm.VersionedPackage, int) throws android.content.pm.PackageManager.NameNotFoundException;
- method public android.content.pm.PackageInstaller getPackageInstaller();
+ method @Deprecated public android.content.pm.PackageInstaller getPackageInstaller();
method @Deprecated public int getPackageUid(String, int) throws android.content.pm.PackageManager.NameNotFoundException;
method @Deprecated public String[] getPackagesForUid(int);
method @Deprecated public java.util.List<android.content.pm.PackageInfo> getPackagesHoldingPermissions(String[], int);
@@ -263,8 +263,8 @@
method @Deprecated public android.content.res.XmlResourceParser getXml(String, int, android.content.pm.ApplicationInfo);
method @Deprecated public boolean hasSystemFeature(String);
method @Deprecated public boolean hasSystemFeature(String, int);
- method public boolean isInstantApp();
- method public boolean isInstantApp(String);
+ method @Deprecated public boolean isInstantApp();
+ method @Deprecated public boolean isInstantApp(String);
method @Deprecated public boolean isPermissionRevokedByPolicy(String, String);
method @Deprecated public boolean isSafeMode();
method @Deprecated public java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceivers(android.content.Intent, int);
@@ -281,11 +281,12 @@
method @Deprecated public android.content.pm.ProviderInfo resolveContentProvider(String, int);
method @Deprecated public android.content.pm.ResolveInfo resolveService(android.content.Intent, int);
method @Deprecated public android.content.pm.ResolveInfo resolveServiceAsUser(android.content.Intent, int, int);
- method public void setApplicationCategoryHint(String, int);
+ method @Deprecated public void setApplicationCategoryHint(String, int);
method @Deprecated public void setApplicationEnabledSetting(String, int, int);
method @Deprecated public void setComponentEnabledSetting(android.content.ComponentName, int, int);
method @Deprecated public void setInstallerPackageName(String, String);
- method public void updateInstantAppCookie(@NonNull byte[]);
+ method @Deprecated public boolean setInstantAppCookie(@NonNull byte[]);
+ method @Deprecated public void updateInstantAppCookie(@NonNull byte[]);
method @Deprecated public void verifyPendingInstall(int, int);
}
diff --git a/test-mock/api/removed.txt b/test-mock/api/removed.txt
index 1496c35..0c800b6 100644
--- a/test-mock/api/removed.txt
+++ b/test-mock/api/removed.txt
@@ -9,7 +9,7 @@
@Deprecated public class MockPackageManager extends android.content.pm.PackageManager {
method @Deprecated public String getDefaultBrowserPackageName(int);
method @Deprecated public boolean setDefaultBrowserPackageName(String, int);
- method public boolean setInstantAppCookie(@NonNull byte[]);
+ method @Deprecated public boolean setInstantAppCookie(@NonNull byte[]);
}
}
diff --git a/test-mock/api/system-current.txt b/test-mock/api/system-current.txt
index 9e022f0..f350957 100644
--- a/test-mock/api/system-current.txt
+++ b/test-mock/api/system-current.txt
@@ -9,30 +9,30 @@
}
@Deprecated public class MockPackageManager extends android.content.pm.PackageManager {
- method public void addOnPermissionsChangeListener(android.content.pm.PackageManager.OnPermissionsChangedListener);
- method public boolean arePermissionsIndividuallyControlled();
+ method @Deprecated public void addOnPermissionsChangeListener(android.content.pm.PackageManager.OnPermissionsChangedListener);
+ method @Deprecated public boolean arePermissionsIndividuallyControlled();
method @Deprecated public java.util.List<android.content.IntentFilter> getAllIntentFilters(String);
- method public String getDefaultBrowserPackageNameAsUser(int);
- method public java.util.List<android.content.pm.PackageInfo> getInstalledPackagesAsUser(int, int);
- method public android.graphics.drawable.Drawable getInstantAppIcon(String);
- method public android.content.ComponentName getInstantAppInstallerComponent();
- method public android.content.ComponentName getInstantAppResolverSettingsComponent();
- method public java.util.List<android.content.pm.InstantAppInfo> getInstantApps();
- method public java.util.List<android.content.pm.IntentFilterVerificationInfo> getIntentFilterVerifications(String);
- method public int getIntentVerificationStatusAsUser(String, int);
- method public int getPermissionFlags(String, String, android.os.UserHandle);
- method public void grantRuntimePermission(String, String, android.os.UserHandle);
- method public int installExistingPackage(String) throws android.content.pm.PackageManager.NameNotFoundException;
- method public int installExistingPackage(String, int) throws android.content.pm.PackageManager.NameNotFoundException;
- method public void registerDexModule(String, @Nullable android.content.pm.PackageManager.DexModuleRegisterCallback);
- method public void removeOnPermissionsChangeListener(android.content.pm.PackageManager.OnPermissionsChangedListener);
- method public void revokeRuntimePermission(String, String, android.os.UserHandle);
- method public boolean setDefaultBrowserPackageNameAsUser(String, int);
+ method @Deprecated public String getDefaultBrowserPackageNameAsUser(int);
+ method @Deprecated public java.util.List<android.content.pm.PackageInfo> getInstalledPackagesAsUser(int, int);
+ method @Deprecated public android.graphics.drawable.Drawable getInstantAppIcon(String);
+ method @Deprecated public android.content.ComponentName getInstantAppInstallerComponent();
+ method @Deprecated public android.content.ComponentName getInstantAppResolverSettingsComponent();
+ method @Deprecated public java.util.List<android.content.pm.InstantAppInfo> getInstantApps();
+ method @Deprecated public java.util.List<android.content.pm.IntentFilterVerificationInfo> getIntentFilterVerifications(String);
+ method @Deprecated public int getIntentVerificationStatusAsUser(String, int);
+ method @Deprecated public int getPermissionFlags(String, String, android.os.UserHandle);
+ method @Deprecated public void grantRuntimePermission(String, String, android.os.UserHandle);
+ method @Deprecated public int installExistingPackage(String) throws android.content.pm.PackageManager.NameNotFoundException;
+ method @Deprecated public int installExistingPackage(String, int) throws android.content.pm.PackageManager.NameNotFoundException;
+ method @Deprecated public void registerDexModule(String, @Nullable android.content.pm.PackageManager.DexModuleRegisterCallback);
+ method @Deprecated public void removeOnPermissionsChangeListener(android.content.pm.PackageManager.OnPermissionsChangedListener);
+ method @Deprecated public void revokeRuntimePermission(String, String, android.os.UserHandle);
+ method @Deprecated public boolean setDefaultBrowserPackageNameAsUser(String, int);
method public String[] setPackagesSuspended(String[], boolean, android.os.PersistableBundle, android.os.PersistableBundle, String);
- method public void setUpdateAvailable(String, boolean);
- method public boolean updateIntentVerificationStatusAsUser(String, int, int);
- method public void updatePermissionFlags(String, String, int, int, android.os.UserHandle);
- method public void verifyIntentFilter(int, int, java.util.List<java.lang.String>);
+ method @Deprecated public void setUpdateAvailable(String, boolean);
+ method @Deprecated public boolean updateIntentVerificationStatusAsUser(String, int, int);
+ method @Deprecated public void updatePermissionFlags(String, String, int, int, android.os.UserHandle);
+ method @Deprecated public void verifyIntentFilter(int, int, java.util.List<java.lang.String>);
}
}
diff --git a/test-mock/api/test-current.txt b/test-mock/api/test-current.txt
index 35f076f..9ed0108 100644
--- a/test-mock/api/test-current.txt
+++ b/test-mock/api/test-current.txt
@@ -6,13 +6,13 @@
}
@Deprecated public class MockPackageManager extends android.content.pm.PackageManager {
- method public void addCrossProfileIntentFilter(android.content.IntentFilter, int, int, int);
- method public void clearCrossProfileIntentFilters(int);
- method public int getInstallReason(String, android.os.UserHandle);
- method public java.util.List<android.content.pm.ApplicationInfo> getInstalledApplicationsAsUser(int, int);
- method public String[] getNamesForUids(int[]);
- method @NonNull public String getServicesSystemSharedLibraryPackageName();
- method @NonNull public String getSharedSystemSharedLibraryPackageName();
+ method @Deprecated public void addCrossProfileIntentFilter(android.content.IntentFilter, int, int, int);
+ method @Deprecated public void clearCrossProfileIntentFilters(int);
+ method @Deprecated public int getInstallReason(String, android.os.UserHandle);
+ method @Deprecated public java.util.List<android.content.pm.ApplicationInfo> getInstalledApplicationsAsUser(int, int);
+ method @Deprecated public String[] getNamesForUids(int[]);
+ method @Deprecated @NonNull public String getServicesSystemSharedLibraryPackageName();
+ method @Deprecated @NonNull public String getSharedSystemSharedLibraryPackageName();
}
}
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/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/FlickerTests/src/com/android/server/wm/flicker/activityembedding/pip/SecondaryActivityEnterPipTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/pip/SecondaryActivityEnterPipTest.kt
index 359845d..47d6d23 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/pip/SecondaryActivityEnterPipTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/pip/SecondaryActivityEnterPipTest.kt
@@ -36,7 +36,8 @@
/**
* Test launching a secondary Activity into Picture-In-Picture mode.
*
- * Setup: Start from a split A|B. Transition: B enters PIP, observe the window shrink to the bottom
+ * Setup: Start from a split A|B.
+ * Transition: B enters PIP, observe the window first goes fullscreen then shrink to the bottom
* right corner on screen.
*
* To run this test: `atest FlickerTestsOther:SecondaryActivityEnterPipTest`
@@ -63,7 +64,16 @@
}
}
- /** Main and secondary activity start from a split each taking half of the screen. */
+ /**
+ * We expect the background layer to be visible during this transition.
+ */
+ @Presubmit
+ @Test
+ override fun backgroundLayerNeverVisible(): Unit {}
+
+ /**
+ * Main and secondary activity start from a split each taking half of the screen.
+ */
@Presubmit
@Test
fun layersStartFromEqualSplit() {
@@ -109,7 +119,7 @@
.isVisible(TRANSITION_SNAPSHOT)
.isInvisible(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT)
.then()
- .isVisible(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT)
+ .isVisible(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT, isOptional = true)
}
flicker.assertLayersEnd {
visibleRegion(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT)
diff --git a/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt b/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt
index fa86e9c..44de6a6 100644
--- a/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt
+++ b/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt
@@ -100,6 +100,7 @@
const val RECEIVER_NAME = "DummyReceiver"
private const val ENGLISH_US_LAYOUT_NAME = "keyboard_layout_english_us"
private const val ENGLISH_UK_LAYOUT_NAME = "keyboard_layout_english_uk"
+ private const val GERMAN_LAYOUT_NAME = "keyboard_layout_german"
private const val VENDOR_SPECIFIC_LAYOUT_NAME = "keyboard_layout_vendorId:1,productId:1"
const val LAYOUT_TYPE_QWERTZ = 2
const val LAYOUT_TYPE_QWERTY = 1
@@ -108,6 +109,7 @@
private val ENGLISH_US_LAYOUT_DESCRIPTOR = createLayoutDescriptor(ENGLISH_US_LAYOUT_NAME)
private val ENGLISH_UK_LAYOUT_DESCRIPTOR = createLayoutDescriptor(ENGLISH_UK_LAYOUT_NAME)
+ private val GERMAN_LAYOUT_DESCRIPTOR = createLayoutDescriptor(GERMAN_LAYOUT_NAME)
private val VENDOR_SPECIFIC_LAYOUT_DESCRIPTOR =
createLayoutDescriptor(VENDOR_SPECIFIC_LAYOUT_NAME)
@@ -710,7 +712,7 @@
assertCorrectLayout(
keyboardDevice,
createImeSubtypeForLanguageTag("de"),
- createLayoutDescriptor("keyboard_layout_german")
+ GERMAN_LAYOUT_DESCRIPTOR
)
assertCorrectLayout(
keyboardDevice,
@@ -775,13 +777,13 @@
assertCorrectLayout(
keyboardDevice,
createImeSubtypeForLanguageTagAndLayoutType("de", "qwertz"),
- createLayoutDescriptor("keyboard_layout_german")
+ GERMAN_LAYOUT_DESCRIPTOR
)
// Wrong layout type should match with language if provided layout type not available
assertCorrectLayout(
keyboardDevice,
createImeSubtypeForLanguageTagAndLayoutType("de", "qwerty"),
- createLayoutDescriptor("keyboard_layout_german")
+ GERMAN_LAYOUT_DESCRIPTOR
)
assertCorrectLayout(
keyboardDevice,
@@ -856,7 +858,7 @@
ArgumentMatchers.eq(createByteArray(
KeyboardMetricsCollector.DEFAULT_LANGUAGE_TAG,
LAYOUT_TYPE_DEFAULT,
- "German",
+ GERMAN_LAYOUT_NAME,
KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD,
"de-Latn",
LAYOUT_TYPE_QWERTZ))
@@ -882,7 +884,7 @@
ArgumentMatchers.eq(createByteArray(
"en",
LAYOUT_TYPE_QWERTY,
- "English (US)",
+ ENGLISH_US_LAYOUT_NAME,
KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_DEVICE,
"de-Latn",
LAYOUT_TYPE_QWERTZ))
diff --git a/tests/Input/src/com/android/server/input/KeyboardMetricsCollectorTests.kt b/tests/Input/src/com/android/server/input/KeyboardMetricsCollectorTests.kt
index b39c932..33ff09b 100644
--- a/tests/Input/src/com/android/server/input/KeyboardMetricsCollectorTests.kt
+++ b/tests/Input/src/com/android/server/input/KeyboardMetricsCollectorTests.kt
@@ -113,7 +113,7 @@
)
val event = builder.addLayoutSelection(
createImeSubtype(1, ULocale.forLanguageTag("en-US"), "qwerty"),
- KeyboardLayout(null, "English(US)(Qwerty)", null, 0, null, 0, 0, 0),
+ "English(US)(Qwerty)",
KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD
).addLayoutSelection(
createImeSubtype(2, ULocale.forLanguageTag("en-US"), "azerty"),
@@ -121,7 +121,7 @@
KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_USER
).addLayoutSelection(
createImeSubtype(3, ULocale.forLanguageTag("en-US"), "qwerty"),
- KeyboardLayout(null, "German", null, 0, null, 0, 0, 0),
+ "German",
KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_DEVICE
).setIsFirstTimeConfiguration(true).build()
@@ -184,7 +184,7 @@
)
val event = builder.addLayoutSelection(
createImeSubtype(4, null, "qwerty"), // Default language tag
- KeyboardLayout(null, "German", null, 0, null, 0, 0, 0),
+ "German",
KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_DEVICE
).build()
diff --git a/tests/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/testables/src/com/android/internal/config/sysui/TestableFlagResolver.java b/tests/testables/src/com/android/internal/config/sysui/TestableFlagResolver.java
index 6a6ab00..a43e1b0 100644
--- a/tests/testables/src/com/android/internal/config/sysui/TestableFlagResolver.java
+++ b/tests/testables/src/com/android/internal/config/sysui/TestableFlagResolver.java
@@ -21,15 +21,39 @@
public class TestableFlagResolver implements SystemUiSystemPropertiesFlags.FlagResolver {
private Map<String, Boolean> mOverrides = new HashMap<>();
+ private Map<String, Integer> mOverridesInt = new HashMap<>();
+ private Map<String, String> mOverridesString = new HashMap<>();
@Override
public boolean isEnabled(SystemUiSystemPropertiesFlags.Flag flag) {
return mOverrides.getOrDefault(flag.mSysPropKey, flag.mDefaultValue);
}
+ @Override
+ public int getIntValue(SystemUiSystemPropertiesFlags.Flag flag) {
+ return mOverridesInt.getOrDefault(flag.mSysPropKey, flag.mDefaultIntValue);
+ }
+
+ @Override
+ public String getStringValue(SystemUiSystemPropertiesFlags.Flag flag) {
+ return mOverridesString.getOrDefault(flag.mSysPropKey, flag.mDefaultStringValue);
+ }
+
public TestableFlagResolver setFlagOverride(SystemUiSystemPropertiesFlags.Flag flag,
boolean isEnabled) {
mOverrides.put(flag.mSysPropKey, isEnabled);
return this;
}
+
+ public TestableFlagResolver setFlagOverride(SystemUiSystemPropertiesFlags.Flag flag,
+ int value) {
+ mOverridesInt.put(flag.mSysPropKey, value);
+ return this;
+ }
+
+ public TestableFlagResolver setFlagOverride(SystemUiSystemPropertiesFlags.Flag flag,
+ String value) {
+ mOverridesString.put(flag.mSysPropKey, value);
+ return this;
+ }
}
diff --git a/tools/hoststubgen/hoststubgen/Android.bp b/tools/hoststubgen/hoststubgen/Android.bp
index a617876..226e2fad 100644
--- a/tools/hoststubgen/hoststubgen/Android.bp
+++ b/tools/hoststubgen/hoststubgen/Android.bp
@@ -7,9 +7,38 @@
default_applicable_licenses: ["frameworks_base_license"],
}
+// Visibility only for ravenwood prototype uses.
+genrule_defaults {
+ name: "hoststubgen-for-prototype-only-genrule",
+ visibility: [
+ ":__subpackages__",
+ "//frameworks/base/ravenwood:__subpackages__",
+ ],
+}
+
+// Visibility only for ravenwood prototype uses.
+java_defaults {
+ name: "hoststubgen-for-prototype-only-java",
+ visibility: [
+ ":__subpackages__",
+ "//frameworks/base/ravenwood:__subpackages__",
+ ],
+}
+
+// Visibility only for ravenwood prototype uses.
+filegroup_defaults {
+ name: "hoststubgen-for-prototype-only-filegroup",
+ visibility: [
+ ":__subpackages__",
+ "//frameworks/base/ravenwood:__subpackages__",
+ ],
+}
+
// This library contains the standard hoststubgen annotations.
+// This is only for the prototype. The productionized version is "ravenwood-annotations".
java_library {
name: "hoststubgen-annotations",
+ defaults: ["hoststubgen-for-prototype-only-java"],
srcs: [
"annotations-src/**/*.java",
],
@@ -18,7 +47,6 @@
// Seems like we need it to avoid circular deps.
// Copied it from "app-compat-annotations".
sdk_version: "core_current",
- visibility: ["//visibility:public"],
}
// This library contains helper classes used in the host side test environment at runtime.
@@ -30,11 +58,6 @@
],
libs: [
"junit",
- "ow2-asm",
- "ow2-asm-analysis",
- "ow2-asm-commons",
- "ow2-asm-tree",
- "ow2-asm-util",
],
static_libs: [
"guava",
@@ -60,12 +83,13 @@
}
// File that contains the standard command line argumetns to hoststubgen.
+// This is only for the prototype. The productionized version is "ravenwood-standard-options".
filegroup {
name: "hoststubgen-standard-options",
+ defaults: ["hoststubgen-for-prototype-only-filegroup"],
srcs: [
"hoststubgen-standard-options.txt",
],
- visibility: ["//visibility:public"],
}
hoststubgen_common_options = "$(location hoststubgen) " +
@@ -98,7 +122,6 @@
"hoststubgen_keep_all.txt",
"hoststubgen_dump.txt",
],
- // visibility: ["//visibility:public"],
}
// Generate the stub/impl from framework-all, with hidden APIs.
@@ -116,8 +139,10 @@
}
// Extract the stub jar from "framework-all-host" for subsequent build rules.
+// This is only for the prototype. Do not use it in "productionized" build rules.
java_genrule_host {
name: "framework-all-hidden-api-host-stub",
+ defaults: ["hoststubgen-for-prototype-only-genrule"],
cmd: "cp $(in) $(out)",
srcs: [
":framework-all-hidden-api-host{host_stub.jar}",
@@ -125,12 +150,13 @@
out: [
"host_stub.jar",
],
- visibility: ["//visibility:public"],
}
// Extract the impl jar from "framework-all-host" for subsequent build rules.
+// This is only for the prototype. Do not use it in "productionized" build rules.
java_genrule_host {
name: "framework-all-hidden-api-host-impl",
+ defaults: ["hoststubgen-for-prototype-only-genrule"],
cmd: "cp $(in) $(out)",
srcs: [
":framework-all-hidden-api-host{host_impl.jar}",
@@ -138,11 +164,11 @@
out: [
"host_impl.jar",
],
- visibility: ["//visibility:public"],
}
// Generate the stub/impl from framework-all, with only public/system/test APIs, without
// hidden APIs.
+// This is only for the prototype. Do not use it in "productionized" build rules.
java_genrule_host {
name: "framework-all-test-api-host",
defaults: ["hoststubgen-command-defaults"],
@@ -159,8 +185,10 @@
}
// Extract the stub jar from "framework-all-test-api-host" for subsequent build rules.
+// This is only for the prototype. Do not use it in "productionized" build rules.
java_genrule_host {
name: "framework-all-test-api-host-stub",
+ defaults: ["hoststubgen-for-prototype-only-genrule"],
cmd: "cp $(in) $(out)",
srcs: [
":framework-all-test-api-host{host_stub.jar}",
@@ -168,12 +196,13 @@
out: [
"host_stub.jar",
],
- visibility: ["//visibility:public"],
}
// Extract the impl jar from "framework-all-test-api-host" for subsequent build rules.
+// This is only for the prototype. Do not use it in "productionized" build rules.
java_genrule_host {
name: "framework-all-test-api-host-impl",
+ defaults: ["hoststubgen-for-prototype-only-genrule"],
cmd: "cp $(in) $(out)",
srcs: [
":framework-all-test-api-host{host_impl.jar}",
@@ -181,7 +210,6 @@
out: [
"host_impl.jar",
],
- visibility: ["//visibility:public"],
}
// This library contains helper classes to build hostside tests/targets.
@@ -191,6 +219,7 @@
// Ideally this library should be empty.
java_library_host {
name: "hoststubgen-helper-framework-buildtime",
+ defaults: ["hoststubgen-for-prototype-only-java"],
srcs: [
"helper-framework-buildtime-src/**/*.java",
],
@@ -200,13 +229,13 @@
"framework-all-hidden-api-host-impl",
"junit",
],
- visibility: ["//visibility:public"],
}
// This module contains "fake" libcore/dalvik classes, framework native substitution, etc,
// that are needed at runtime.
java_library_host {
name: "hoststubgen-helper-framework-runtime",
+ defaults: ["hoststubgen-for-prototype-only-java"],
srcs: [
"helper-framework-runtime-src/**/*.java",
],
@@ -214,7 +243,6 @@
"hoststubgen-helper-runtime",
"framework-all-hidden-api-host-impl",
],
- visibility: ["//visibility:public"],
}
// Defaults for host side test modules.
diff --git a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedKeepClass.java b/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedKeepClass.java
index 4c37579..7ada961 100644
--- a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedKeepClass.java
+++ b/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedKeepClass.java
@@ -17,8 +17,6 @@
import static java.lang.annotation.ElementType.TYPE;
-import org.objectweb.asm.Type;
-
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@@ -29,6 +27,6 @@
@Target({TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface HostStubGenProcessedKeepClass {
- String CLASS_INTERNAL_NAME = Type.getInternalName(HostStubGenProcessedKeepClass.class);
+ String CLASS_INTERNAL_NAME = HostTestUtils.getInternalName(HostStubGenProcessedKeepClass.class);
String CLASS_DESCRIPTOR = "L" + CLASS_INTERNAL_NAME + ";";
}
diff --git a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedStubClass.java b/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedStubClass.java
index 34e0030..e598da0a 100644
--- a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedStubClass.java
+++ b/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedStubClass.java
@@ -17,8 +17,6 @@
import static java.lang.annotation.ElementType.TYPE;
-import org.objectweb.asm.Type;
-
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@@ -29,6 +27,6 @@
@Target({TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface HostStubGenProcessedStubClass {
- String CLASS_INTERNAL_NAME = Type.getInternalName(HostStubGenProcessedStubClass.class);
+ String CLASS_INTERNAL_NAME = HostTestUtils.getInternalName(HostStubGenProcessedStubClass.class);
String CLASS_DESCRIPTOR = "L" + CLASS_INTERNAL_NAME + ";";
}
diff --git a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestUtils.java b/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestUtils.java
index d176b5e..9f83597 100644
--- a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestUtils.java
+++ b/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestUtils.java
@@ -15,8 +15,6 @@
*/
package com.android.hoststubgen.hosthelper;
-import org.objectweb.asm.Type;
-
import java.io.PrintStream;
import java.lang.StackWalker.Option;
import java.lang.reflect.Method;
@@ -32,7 +30,15 @@
private HostTestUtils() {
}
- public static final String CLASS_INTERNAL_NAME = Type.getInternalName(HostTestUtils.class);
+ /**
+ * Same as ASM's Type.getInternalName(). Copied here, to avoid having a reference to ASM
+ * in this JAR.
+ */
+ public static String getInternalName(final Class<?> clazz) {
+ return clazz.getName().replace('.', '/');
+ }
+
+ public static final String CLASS_INTERNAL_NAME = getInternalName(HostTestUtils.class);
/** If true, we won't print method call log. */
private static final boolean SKIP_METHOD_LOG = "1".equals(System.getenv(
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ConstantFilter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ConstantFilter.kt
index 33010ba..678e6ea 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ConstantFilter.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ConstantFilter.kt
@@ -46,8 +46,11 @@
}
methodPolicy = policy
- // TODO: Need to think about the realistic default behavior.
- classPolicy = if (policy != FilterPolicy.Throw) policy else FilterPolicy.Remove
+ // If the default policy is "throw", we convert it to "keep" for classes and fields.
+ classPolicy = when (policy) {
+ FilterPolicy.Throw -> FilterPolicy.Keep
+ else -> policy
+ }
fieldPolicy = classPolicy
}
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ImplicitOutputFilter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ImplicitOutputFilter.kt
index 9c372ff..c6334c4 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ImplicitOutputFilter.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ImplicitOutputFilter.kt
@@ -17,6 +17,8 @@
import com.android.hoststubgen.HostStubGenErrors
import com.android.hoststubgen.HostStubGenInternalException
+import com.android.hoststubgen.asm.CLASS_INITIALIZER_DESC
+import com.android.hoststubgen.asm.CLASS_INITIALIZER_NAME
import com.android.hoststubgen.asm.isAnonymousInnerClass
import com.android.hoststubgen.log
import com.android.hoststubgen.asm.ClassNodes
@@ -81,6 +83,16 @@
}
}
+ // If we throw from the static initializer, the class would be useless, so we convert it
+ // "keep" instead.
+ if (methodName == CLASS_INITIALIZER_NAME && descriptor == CLASS_INITIALIZER_DESC &&
+ fallback.policy == FilterPolicy.Throw) {
+ // TODO Maybe show a warning?? But that'd be too noisy with --default-throw.
+ return FilterPolicy.Keep.withReason(
+ "'throw' on static initializer is handled as 'keep'" +
+ " [original throw reason: ${fallback.reason}]")
+ }
+
return fallback
}
}
\ No newline at end of file
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/ImplGeneratingAdapter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/ImplGeneratingAdapter.kt
index 57b6689..ce72a8e 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/ImplGeneratingAdapter.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/ImplGeneratingAdapter.kt
@@ -79,7 +79,7 @@
// StaticInitMerger will merge it with the existing one, if any.
visitMethod(
Opcodes.ACC_PRIVATE or Opcodes.ACC_STATIC,
- "<clinit>",
+ CLASS_INITIALIZER_NAME,
"()V",
null,
null
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt
index 43ceec4..0761edc 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt
@@ -372,7 +372,7 @@
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializer
super_class: #x // java/lang/Object
- interfaces: 0, fields: 1, methods: 2, attributes: 3
+ interfaces: 0, fields: 1, methods: 1, attributes: 3
public static boolean sInitialized;
descriptor: Z
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
@@ -387,17 +387,6 @@
x: ldc #x // String Stub!
x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
x: athrow
-
- static {};
- descriptor: ()V
- flags: (0x0008) ACC_STATIC
- Code:
- stack=3, locals=0, args_size=0
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
}
SourceFile: "TinyFrameworkClassWithInitializer.java"
RuntimeVisibleAnnotations:
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt
index 43ceec4..0761edc 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt
@@ -372,7 +372,7 @@
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializer
super_class: #x // java/lang/Object
- interfaces: 0, fields: 1, methods: 2, attributes: 3
+ interfaces: 0, fields: 1, methods: 1, attributes: 3
public static boolean sInitialized;
descriptor: Z
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
@@ -387,17 +387,6 @@
x: ldc #x // String Stub!
x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
x: athrow
-
- static {};
- descriptor: ()V
- flags: (0x0008) ACC_STATIC
- Code:
- stack=3, locals=0, args_size=0
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
}
SourceFile: "TinyFrameworkClassWithInitializer.java"
RuntimeVisibleAnnotations:
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt
index 079d2a8..8fcd2fb 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt
@@ -15,3 +15,8 @@
# Class load hook
class com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy ~com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded
+
+
+class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWithInitializer stubclass
+ # Testing 'throw' on a static initializer. This should be handled as 'keep'.
+ method <clinit> ()V throw
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/run-test-manually.sh b/tools/hoststubgen/hoststubgen/test-tiny-framework/run-test-manually.sh
index fd48646..6bf074b 100755
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/run-test-manually.sh
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/run-test-manually.sh
@@ -18,8 +18,6 @@
# 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
-
debug=0
while getopts "d" opt; do
@@ -57,7 +55,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_HOST_OUT/framework/truth-prebuilt.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/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java
index 246d065..cd604ff 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java
@@ -23,6 +23,10 @@
import org.junit.Test;
import org.junit.rules.ExpectedException;
+import java.io.FileDescriptor;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+
public class TinyFrameworkClassTest {
@Rule
public ExpectedException thrown = ExpectedException.none();
@@ -140,4 +144,42 @@
assertThat(TinyFrameworkClassLoadHook.sLoadedClasses)
.doesNotContain(TinyFrameworkNestedClasses.class);
}
+
+ /**
+ * Test to try accessing JDK private fields using reflections + setAccessible(true),
+ * which is now disallowed due to Java Modules, unless you run the javacommand with.
+ * --add-opens=java.base/java.io=ALL-UNNAMED
+ *
+ * You can try it from the command line, like:
+ * $ JAVA_OPTS="--add-opens=java.base/java.io=ALL-UNNAMED" ./run-test-manually.sh
+ *
+ * @throws Exception
+ */
+ @Test
+ public void testFileDescriptor() throws Exception {
+ var fd = FileDescriptor.out;
+
+ // Get the FD value directly from the private field.
+ // This is now prohibited due to Java Modules.
+ // It throws:
+ // java.lang.reflect.InaccessibleObjectException: Unable to make field private int java.io.FileDescriptor.fd accessible: module java.base does not "opens java.io" to unnamed module @3bb50eaa
+
+ thrown.expect(java.lang.reflect.InaccessibleObjectException.class);
+
+ // Access the private field.
+ final Field f = FileDescriptor.class.getDeclaredField("fd");
+ final Method m = FileDescriptor.class.getDeclaredMethod("set", int.class);
+ f.setAccessible(true);
+ m.setAccessible(true);
+
+ assertThat(f.get(fd)).isEqualTo(1);
+
+ // Set
+ f.set(fd, 2);
+ assertThat(f.get(fd)).isEqualTo(2);
+
+ // Call the package private method, set(int).
+ m.invoke(fd, 0);
+ assertThat(f.get(fd)).isEqualTo(0);
+ }
}
diff --git a/tools/hoststubgen/scripts/run-all-tests.sh b/tools/hoststubgen/scripts/run-all-tests.sh
index 7600942..8bc88de 100755
--- a/tools/hoststubgen/scripts/run-all-tests.sh
+++ b/tools/hoststubgen/scripts/run-all-tests.sh
@@ -33,6 +33,7 @@
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
run atest tiny-framework-dump-test
run ./scripts/build-framework-hostside-jars-and-extract.sh
diff --git a/tools/lint/global/integration_tests/Android.bp b/tools/lint/global/integration_tests/Android.bp
index ca96559..40281d2 100644
--- a/tools/lint/global/integration_tests/Android.bp
+++ b/tools/lint/global/integration_tests/Android.bp
@@ -12,25 +12,58 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-java_library {
- name: "AndroidGlobalLintTestNoAidl",
- srcs: ["TestNoAidl/**/*.java"],
+// Integration tests for @EnforcePermission linters.
+// Each test defines its own java_library. The XML lint report from this
+// java_library is wrapped under a Python library with a unique pkg_path (this
+// is to avoid a name conflict for the report file). All the tests are
+// referenced and executed by AndroidGlobalLintCheckerIntegrationTest.
+
+java_defaults {
+ name: "AndroidGlobalLintIntegrationTestDefault",
libs: [
"framework-annotations-lib",
],
lint: {
- // It is expected that lint returns an error when processing this
+ // It is expected that lint returns an error when processing the
// library. Silence it here, the lint output is verified in tests.py.
suppress_exit_code: true,
},
}
+java_library {
+ name: "AndroidGlobalLintTestNoAidl",
+ srcs: ["TestNoAidl/**/*.java"],
+ defaults: ["AndroidGlobalLintIntegrationTestDefault"],
+}
+
+python_library_host {
+ name: "AndroidGlobalLintTestNoAidl_py",
+ data: [":AndroidGlobalLintTestNoAidl{.lint}"],
+ pkg_path: "no_aidl",
+}
+
+java_library {
+ name: "AndroidGlobalLintTestMissingAnnotation",
+ srcs: [
+ "TestMissingAnnotation/**/*.java",
+ "TestMissingAnnotation/**/*.aidl",
+ ],
+ defaults: ["AndroidGlobalLintIntegrationTestDefault"],
+}
+
+python_library_host {
+ name: "AndroidGlobalLintTestMissingAnnotation_py",
+ data: [":AndroidGlobalLintTestMissingAnnotation{.lint}"],
+ pkg_path: "missing_annotation",
+}
+
python_test_host {
name: "AndroidGlobalLintCheckerIntegrationTest",
srcs: ["tests.py"],
main: "tests.py",
- data: [
- ":AndroidGlobalLintTestNoAidl{.lint}",
+ libs: [
+ "AndroidGlobalLintTestNoAidl_py",
+ "AndroidGlobalLintTestMissingAnnotation_py",
],
version: {
py3: {
diff --git a/tools/lint/global/integration_tests/TestMissingAnnotation/TestMissingAnnotation.java b/tools/lint/global/integration_tests/TestMissingAnnotation/TestMissingAnnotation.java
new file mode 100644
index 0000000..9e4854c
--- /dev/null
+++ b/tools/lint/global/integration_tests/TestMissingAnnotation/TestMissingAnnotation.java
@@ -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 com.google.android.lint.integration_tests;
+
+/**
+ * A class that implements an AIDL interface, but is missing the @EnforcePermission annotation.
+ */
+class TestMissingAnnotation extends IFoo.Stub {
+
+ @Override
+ public void Method() {
+ }
+
+}
diff --git a/tools/lint/global/integration_tests/TestMissingAnnotation/com/google/android/lint/integration_tests/IFoo.aidl b/tools/lint/global/integration_tests/TestMissingAnnotation/com/google/android/lint/integration_tests/IFoo.aidl
new file mode 100644
index 0000000..95ec2c2
--- /dev/null
+++ b/tools/lint/global/integration_tests/TestMissingAnnotation/com/google/android/lint/integration_tests/IFoo.aidl
@@ -0,0 +1,7 @@
+package com.google.android.lint.integration_tests;
+
+interface IFoo {
+
+ @EnforcePermission("INTERNET")
+ void Method();
+}
diff --git a/tools/lint/global/integration_tests/tests.py b/tools/lint/global/integration_tests/tests.py
index fc3eeb4..cdb16b8 100644
--- a/tools/lint/global/integration_tests/tests.py
+++ b/tools/lint/global/integration_tests/tests.py
@@ -19,16 +19,28 @@
class TestLinterReports(unittest.TestCase):
"""Integration tests for the linters used by @EnforcePermission."""
- def test_no_aidl(self):
- report = pkgutil.get_data("lint", "lint-report.xml").decode()
+ def _read_report(self, pkg_path):
+ report = pkgutil.get_data(pkg_path, "lint/lint-report.xml").decode()
issues = xml.etree.ElementTree.fromstring(report)
self.assertEqual(issues.tag, "issues")
+ return issues
+
+ def test_no_aidl(self):
+ issues = self._read_report("no_aidl")
self.assertEqual(len(issues), 1)
issue = issues[0]
self.assertEqual(issue.attrib["id"], "MisusingEnforcePermissionAnnotation")
self.assertEqual(issue.attrib["severity"], "Error")
+ def test_missing_annotation(self):
+ issues = self._read_report("missing_annotation")
+ self.assertEqual(len(issues), 1)
+
+ issue = issues[0]
+ self.assertEqual(issue.attrib["id"], "MissingEnforcePermissionAnnotation")
+ self.assertEqual(issue.attrib["severity"], "Error")
+
if __name__ == '__main__':
unittest.main(verbosity=2)