Merge "Set background color of more actions pill" into main
diff --git a/Android.bp b/Android.bp
index 48f0928..49a6a2b 100644
--- a/Android.bp
+++ b/Android.bp
@@ -103,11 +103,10 @@
":android.hardware.gnss-V2-java-source",
":android.hardware.graphics.common-V3-java-source",
":android.hardware.keymaster-V4-java-source",
- ":android.hardware.radio-V3-java-source",
- ":android.hardware.radio.data-V3-java-source",
- ":android.hardware.radio.network-V3-java-source",
- ":android.hardware.radio.voice-V3-java-source",
- ":android.hardware.security.keymint-V3-java-source",
+ ":android.hardware.radio-V4-java-source",
+ ":android.hardware.radio.data-V4-java-source",
+ ":android.hardware.radio.network-V4-java-source",
+ ":android.hardware.radio.voice-V4-java-source",
":android.hardware.security.secureclock-V1-java-source",
":android.hardware.thermal-V3-java-source",
":android.hardware.tv.tuner-V3-java-source",
@@ -116,7 +115,6 @@
":android.security.legacykeystore-java-source",
":android.security.maintenance-java-source",
":android.security.metrics-java-source",
- ":android.system.keystore2-V4-java-source",
":android.hardware.cas-V1-java-source",
":credstore_aidl",
":dumpstate_aidl",
@@ -149,7 +147,16 @@
":statslog-framework-java-gen", // FrameworkStatsLog.java
":statslog-hwui-java-gen", // HwuiStatsLog.java
":audio_policy_configuration_V7_0",
- ],
+ ] + select(release_flag("RELEASE_ATTEST_MODULES"), {
+ true: [
+ ":android.hardware.security.keymint-V4-java-source",
+ ":android.system.keystore2-V5-java-source",
+ ],
+ default: [
+ ":android.hardware.security.keymint-V3-java-source",
+ ":android.system.keystore2-V4-java-source",
+ ],
+ }),
}
java_library {
@@ -225,13 +232,13 @@
"android.hardware.gnss-V2.1-java",
"android.hardware.health-V1.0-java-constants",
"android.hardware.radio-V1.6-java",
- "android.hardware.radio.data-V3-java",
- "android.hardware.radio.ims-V2-java",
- "android.hardware.radio.messaging-V3-java",
- "android.hardware.radio.modem-V3-java",
- "android.hardware.radio.network-V3-java",
- "android.hardware.radio.sim-V3-java",
- "android.hardware.radio.voice-V3-java",
+ "android.hardware.radio.data-V4-java",
+ "android.hardware.radio.ims-V3-java",
+ "android.hardware.radio.messaging-V4-java",
+ "android.hardware.radio.modem-V4-java",
+ "android.hardware.radio.network-V4-java",
+ "android.hardware.radio.sim-V4-java",
+ "android.hardware.radio.voice-V4-java",
"android.hardware.thermal-V1.0-java-constants",
"android.hardware.thermal-V1.0-java",
"android.hardware.thermal-V1.1-java",
@@ -398,6 +405,7 @@
"bouncycastle-repackaged-unbundled",
"com.android.sysprop.foldlockbehavior",
"com.android.sysprop.view",
+ "configinfra_framework_flags_java_lib",
"framework-internal-utils",
"dynamic_instrumentation_manager_aidl-java",
// If MimeMap ever becomes its own APEX, then this dependency would need to be removed
diff --git a/FF_LEADS_OWNERS b/FF_LEADS_OWNERS
new file mode 100644
index 0000000..a650c6b
--- /dev/null
+++ b/FF_LEADS_OWNERS
@@ -0,0 +1,10 @@
+bills@google.com
+carmenjackson@google.com
+nalini@google.com
+nosh@google.com
+olilan@google.com
+philipcuadra@google.com
+rajekumar@google.com
+shayba@google.com
+timmurray@google.com
+zezeozue@google.com
diff --git a/apex/jobscheduler/service/aconfig/alarm.aconfig b/apex/jobscheduler/service/aconfig/alarm.aconfig
index d3068d7..a6e9807 100644
--- a/apex/jobscheduler/service/aconfig/alarm.aconfig
+++ b/apex/jobscheduler/service/aconfig/alarm.aconfig
@@ -2,16 +2,6 @@
container: "system"
flag {
- name: "use_frozen_state_to_drop_listener_alarms"
- namespace: "backstage_power"
- description: "Use frozen state callback to drop listener alarms for cached apps"
- bug: "324470945"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
name: "start_user_before_scheduled_alarms"
namespace: "multiuser"
description: "Persist list of users with alarms scheduled and wakeup stopped users before alarms are due"
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 033da2d..60ba3b8 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
@@ -282,7 +282,6 @@
private final Injector mInjector;
int mBroadcastRefCount = 0;
- boolean mUseFrozenStateToDropListenerAlarms;
MetricsHelper mMetricsHelper;
PowerManager.WakeLock mWakeLock;
SparseIntArray mAlarmsPerUid = new SparseIntArray();
@@ -1784,40 +1783,37 @@
mMetricsHelper = new MetricsHelper(getContext(), mLock);
mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
- mUseFrozenStateToDropListenerAlarms = Flags.useFrozenStateToDropListenerAlarms();
mStartUserBeforeScheduledAlarms = Flags.startUserBeforeScheduledAlarms()
&& UserManager.supportsMultipleUsers();
if (mStartUserBeforeScheduledAlarms) {
mUserWakeupStore = new UserWakeupStore();
mUserWakeupStore.init();
}
- if (mUseFrozenStateToDropListenerAlarms) {
- final ActivityManager.UidFrozenStateChangedCallback callback = (uids, frozenStates) -> {
- final int size = frozenStates.length;
- if (uids.length != size) {
- Slog.wtf(TAG, "Got different length arrays in frozen state callback!"
- + " uids.length: " + uids.length + " frozenStates.length: " + size);
- // Cannot process received data in any meaningful way.
- return;
+ final ActivityManager.UidFrozenStateChangedCallback callback = (uids, frozenStates) -> {
+ final int size = frozenStates.length;
+ if (uids.length != size) {
+ Slog.wtf(TAG, "Got different length arrays in frozen state callback!"
+ + " uids.length: " + uids.length + " frozenStates.length: " + size);
+ // Cannot process received data in any meaningful way.
+ return;
+ }
+ final IntArray affectedUids = new IntArray();
+ for (int i = 0; i < size; i++) {
+ if (frozenStates[i] != UID_FROZEN_STATE_FROZEN) {
+ continue;
}
- final IntArray affectedUids = new IntArray();
- for (int i = 0; i < size; i++) {
- if (frozenStates[i] != UID_FROZEN_STATE_FROZEN) {
- continue;
- }
- if (!CompatChanges.isChangeEnabled(EXACT_LISTENER_ALARMS_DROPPED_ON_CACHED,
- uids[i])) {
- continue;
- }
- affectedUids.add(uids[i]);
+ if (!CompatChanges.isChangeEnabled(EXACT_LISTENER_ALARMS_DROPPED_ON_CACHED,
+ uids[i])) {
+ continue;
}
- if (affectedUids.size() > 0) {
- removeExactListenerAlarms(affectedUids.toArray());
- }
- };
- final ActivityManager am = getContext().getSystemService(ActivityManager.class);
- am.registerUidFrozenStateChangedCallback(new HandlerExecutor(mHandler), callback);
- }
+ affectedUids.add(uids[i]);
+ }
+ if (affectedUids.size() > 0) {
+ removeExactListenerAlarms(affectedUids.toArray());
+ }
+ };
+ final ActivityManager am = getContext().getSystemService(ActivityManager.class);
+ am.registerUidFrozenStateChangedCallback(new HandlerExecutor(mHandler), callback);
mListenerDeathRecipient = new IBinder.DeathRecipient() {
@Override
@@ -2994,13 +2990,10 @@
pw.println("Feature Flags:");
pw.increaseIndent();
- pw.print(Flags.FLAG_USE_FROZEN_STATE_TO_DROP_LISTENER_ALARMS,
- mUseFrozenStateToDropListenerAlarms);
- pw.println();
pw.print(Flags.FLAG_START_USER_BEFORE_SCHEDULED_ALARMS,
Flags.startUserBeforeScheduledAlarms());
- pw.decreaseIndent();
pw.println();
+ pw.decreaseIndent();
pw.println();
pw.println("App Standby Parole: " + mAppStandbyParole);
@@ -5146,38 +5139,6 @@
removeForStoppedLocked(uid);
}
}
-
- @Override
- public void handleUidCachedChanged(int uid, boolean cached) {
- if (mUseFrozenStateToDropListenerAlarms) {
- // Use ActivityManager#UidFrozenStateChangedCallback instead.
- return;
- }
- if (!CompatChanges.isChangeEnabled(EXACT_LISTENER_ALARMS_DROPPED_ON_CACHED, uid)) {
- return;
- }
- // Apps can quickly get frozen after being cached, breaking the exactness guarantee on
- // listener alarms. So going forward, the contract of exact listener alarms explicitly
- // states that they will be removed as soon as the app goes out of lifecycle. We still
- // allow a short grace period for quick shuffling of proc-states that may happen
- // unexpectedly when switching between different lifecycles and is generally hard for
- // apps to avoid.
-
- final long delay;
- synchronized (mLock) {
- delay = mConstants.CACHED_LISTENER_REMOVAL_DELAY;
- }
- final Integer uidObj = uid;
-
- if (cached && !mHandler.hasEqualMessages(REMOVE_EXACT_LISTENER_ALARMS_ON_CACHED,
- uidObj)) {
- mHandler.sendMessageDelayed(
- mHandler.obtainMessage(REMOVE_EXACT_LISTENER_ALARMS_ON_CACHED, uidObj),
- delay);
- } else {
- mHandler.removeEqualMessages(REMOVE_EXACT_LISTENER_ALARMS_ON_CACHED, uidObj);
- }
- }
};
private final BroadcastStats getStatsLocked(PendingIntent pi) {
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
index 8bd3ef4..637c726 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
@@ -36,10 +36,14 @@
import android.app.ActivityManager;
import android.app.AlarmManager;
import android.app.UidObserver;
+import android.app.compat.CompatChanges;
import android.app.job.JobInfo;
import android.app.usage.UsageEvents;
import android.app.usage.UsageStatsManagerInternal;
import android.app.usage.UsageStatsManagerInternal.UsageEventListener;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.Disabled;
+import android.compat.annotation.Overridable;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
@@ -132,6 +136,27 @@
return (int) (val ^ (val >>> 32));
}
+ /**
+ * When enabled this change id overrides the default quota policy enforcement to the jobs
+ * running in the foreground process state.
+ */
+ // TODO: b/379681266 - Might need some refactoring for a better app-compat strategy.
+ @VisibleForTesting
+ @ChangeId
+ @Disabled // Disabled by default
+ @Overridable // The change can be overridden in user build
+ static final long OVERRIDE_QUOTA_ENFORCEMENT_TO_FGS_JOBS = 341201311L;
+
+ /**
+ * When enabled this change id overrides the default quota policy enforcement policy
+ * the jobs started when app was in the TOP state.
+ */
+ @VisibleForTesting
+ @ChangeId
+ @Disabled // Disabled by default
+ @Overridable // The change can be overridden in user build.
+ static final long OVERRIDE_QUOTA_ENFORCEMENT_TO_TOP_STARTED_JOBS = 374323858L;
+
@VisibleForTesting
static class ExecutionStats {
/**
@@ -622,7 +647,9 @@
}
final int uid = jobStatus.getSourceUid();
- if (!Flags.enforceQuotaPolicyToTopStartedJobs() && mTopAppCache.get(uid)) {
+ if ((!Flags.enforceQuotaPolicyToTopStartedJobs()
+ || CompatChanges.isChangeEnabled(OVERRIDE_QUOTA_ENFORCEMENT_TO_TOP_STARTED_JOBS,
+ uid)) && mTopAppCache.get(uid)) {
if (DEBUG) {
Slog.d(TAG, jobStatus.toShortString() + " is top started job");
}
@@ -659,7 +686,9 @@
timer.stopTrackingJob(jobStatus);
}
}
- if (!Flags.enforceQuotaPolicyToTopStartedJobs()) {
+ if (!Flags.enforceQuotaPolicyToTopStartedJobs()
+ || CompatChanges.isChangeEnabled(OVERRIDE_QUOTA_ENFORCEMENT_TO_TOP_STARTED_JOBS,
+ jobStatus.getSourceUid())) {
mTopStartedJobs.remove(jobStatus);
}
}
@@ -772,7 +801,13 @@
/** @return true if the job was started while the app was in the TOP state. */
private boolean isTopStartedJobLocked(@NonNull final JobStatus jobStatus) {
- return !Flags.enforceQuotaPolicyToTopStartedJobs() && mTopStartedJobs.contains(jobStatus);
+ if (!Flags.enforceQuotaPolicyToTopStartedJobs()
+ || CompatChanges.isChangeEnabled(OVERRIDE_QUOTA_ENFORCEMENT_TO_TOP_STARTED_JOBS,
+ jobStatus.getSourceUid())) {
+ return mTopStartedJobs.contains(jobStatus);
+ }
+
+ return false;
}
/** Returns the maximum amount of time this job could run for. */
@@ -2634,9 +2669,13 @@
}
@VisibleForTesting
- int getProcessStateQuotaFreeThreshold() {
- return Flags.enforceQuotaPolicyToFgsJobs() ? ActivityManager.PROCESS_STATE_BOUND_TOP :
- ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
+ int getProcessStateQuotaFreeThreshold(int uid) {
+ if (Flags.enforceQuotaPolicyToFgsJobs()
+ && !CompatChanges.isChangeEnabled(OVERRIDE_QUOTA_ENFORCEMENT_TO_FGS_JOBS, uid)) {
+ return ActivityManager.PROCESS_STATE_BOUND_TOP;
+ }
+
+ return ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
}
private class QcHandler extends Handler {
@@ -2776,7 +2815,7 @@
isQuotaFree = true;
} else {
final boolean reprocess;
- if (procState <= getProcessStateQuotaFreeThreshold()) {
+ if (procState <= getProcessStateQuotaFreeThreshold(uid)) {
reprocess = !mForegroundUids.get(uid);
mForegroundUids.put(uid, true);
isQuotaFree = true;
diff --git a/api/ApiDocs.bp b/api/ApiDocs.bp
index 1ebe0cd..89351fd 100644
--- a/api/ApiDocs.bp
+++ b/api/ApiDocs.bp
@@ -61,6 +61,7 @@
":framework-bluetooth-sources",
":framework-connectivity-tiramisu-updatable-sources",
":framework-graphics-srcs",
+ ":framework-healthfitness-sources",
":framework-mediaprovider-sources",
":framework-nearby-sources",
":framework-nfc-updatable-sources",
diff --git a/cmds/uiautomator/library/core-src/com/android/uiautomator/core/AccessibilityNodeInfoDumper.java b/cmds/uiautomator/library/core-src/com/android/uiautomator/core/AccessibilityNodeInfoDumper.java
index f726361..a88796c3 100644
--- a/cmds/uiautomator/library/core-src/com/android/uiautomator/core/AccessibilityNodeInfoDumper.java
+++ b/cmds/uiautomator/library/core-src/com/android/uiautomator/core/AccessibilityNodeInfoDumper.java
@@ -217,6 +217,9 @@
serializer.attribute("", "selected", Boolean.toString(node.isSelected()));
serializer.attribute("", "bounds", AccessibilityNodeInfoHelper.getVisibleBoundsInScreen(
node, width, height).toShortString());
+ serializer.attribute("", "drawing-order", Integer.toString(node.getDrawingOrder()));
+ serializer.attribute("", "hint", safeCharSeqToString(node.getHintText()));
+
int count = node.getChildCount();
for (int i = 0; i < count; i++) {
AccessibilityNodeInfo child = node.getChild(i);
diff --git a/core/api/current.txt b/core/api/current.txt
index acbd1e6..76f3e5a 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -23,6 +23,7 @@
field public static final String ACTIVITY_RECOGNITION = "android.permission.ACTIVITY_RECOGNITION";
field public static final String ADD_VOICEMAIL = "com.android.voicemail.permission.ADD_VOICEMAIL";
field public static final String ANSWER_PHONE_CALLS = "android.permission.ANSWER_PHONE_CALLS";
+ field @FlaggedApi("android.media.tv.flags.apply_picture_profiles") public static final String APPLY_PICTURE_PROFILE = "android.permission.APPLY_PICTURE_PROFILE";
field public static final String BATTERY_STATS = "android.permission.BATTERY_STATS";
field public static final String BIND_ACCESSIBILITY_SERVICE = "android.permission.BIND_ACCESSIBILITY_SERVICE";
field public static final String BIND_APPWIDGET = "android.permission.BIND_APPWIDGET";
@@ -336,7 +337,7 @@
field public static final String WRITE_SECURE_SETTINGS = "android.permission.WRITE_SECURE_SETTINGS";
field public static final String WRITE_SETTINGS = "android.permission.WRITE_SETTINGS";
field public static final String WRITE_SYNC_SETTINGS = "android.permission.WRITE_SYNC_SETTINGS";
- field @FlaggedApi("com.android.settingslib.flags.settings_catalyst") public static final String WRITE_SYSTEM_PREFERENCES = "android.permission.WRITE_SYSTEM_PREFERENCES";
+ field @FlaggedApi("com.android.settingslib.flags.write_system_preference_permission_enabled") public static final String WRITE_SYSTEM_PREFERENCES = "android.permission.WRITE_SYSTEM_PREFERENCES";
field public static final String WRITE_VOICEMAIL = "com.android.voicemail.permission.WRITE_VOICEMAIL";
}
@@ -1276,6 +1277,7 @@
field public static final int paddingStart = 16843699; // 0x10103b3
field public static final int paddingTop = 16842967; // 0x10100d7
field public static final int paddingVertical = 16844094; // 0x101053e
+ field @FlaggedApi("android.content.pm.app_compat_option_16kb") public static final int pageSizeCompat;
field public static final int panelBackground = 16842846; // 0x101005e
field public static final int panelColorBackground = 16842849; // 0x1010061
field public static final int panelColorForeground = 16842848; // 0x1010060
@@ -2043,6 +2045,12 @@
field public static final int system_error_container_light = 17170554; // 0x106007a
field public static final int system_error_dark = 17170595; // 0x10600a3
field public static final int system_error_light = 17170552; // 0x1060078
+ field @FlaggedApi("android.os.material_colors_10_2024") public static final int system_inverse_on_surface_dark;
+ field @FlaggedApi("android.os.material_colors_10_2024") public static final int system_inverse_on_surface_light;
+ field @FlaggedApi("android.os.material_colors_10_2024") public static final int system_inverse_primary_dark;
+ field @FlaggedApi("android.os.material_colors_10_2024") public static final int system_inverse_primary_light;
+ field @FlaggedApi("android.os.material_colors_10_2024") public static final int system_inverse_surface_dark;
+ field @FlaggedApi("android.os.material_colors_10_2024") public static final int system_inverse_surface_light;
field public static final int system_neutral1_0 = 17170461; // 0x106001d
field public static final int system_neutral1_10 = 17170462; // 0x106001e
field public static final int system_neutral1_100 = 17170464; // 0x1060020
@@ -2119,12 +2127,16 @@
field public static final int system_primary_fixed = 17170612; // 0x10600b4
field public static final int system_primary_fixed_dim = 17170613; // 0x10600b5
field public static final int system_primary_light = 17170528; // 0x1060060
+ field @FlaggedApi("android.os.material_colors_10_2024") public static final int system_scrim_dark;
+ field @FlaggedApi("android.os.material_colors_10_2024") public static final int system_scrim_light;
field public static final int system_secondary_container_dark = 17170573; // 0x106008d
field public static final int system_secondary_container_light = 17170530; // 0x1060062
field public static final int system_secondary_dark = 17170575; // 0x106008f
field public static final int system_secondary_fixed = 17170616; // 0x10600b8
field public static final int system_secondary_fixed_dim = 17170617; // 0x10600b9
field public static final int system_secondary_light = 17170532; // 0x1060064
+ field @FlaggedApi("android.os.material_colors_10_2024") public static final int system_shadow_dark;
+ field @FlaggedApi("android.os.material_colors_10_2024") public static final int system_shadow_light;
field public static final int system_surface_bright_dark = 17170590; // 0x106009e
field public static final int system_surface_bright_light = 17170547; // 0x1060073
field public static final int system_surface_container_dark = 17170587; // 0x106009b
@@ -2142,6 +2154,8 @@
field public static final int system_surface_dim_light = 17170548; // 0x1060074
field public static final int system_surface_disabled = 17170626; // 0x10600c2
field public static final int system_surface_light = 17170540; // 0x106006c
+ field @FlaggedApi("android.os.material_colors_10_2024") public static final int system_surface_tint_dark;
+ field @FlaggedApi("android.os.material_colors_10_2024") public static final int system_surface_tint_light;
field public static final int system_surface_variant_dark = 17170592; // 0x10600a0
field public static final int system_surface_variant_light = 17170549; // 0x1060075
field public static final int system_tertiary_container_dark = 17170577; // 0x1060091
@@ -3816,7 +3830,7 @@
method @RequiresPermission(value="android.permission.AUTHENTICATE_ACCOUNTS", apis="..22") public boolean notifyAccountAuthenticated(android.accounts.Account);
method @RequiresPermission(value="android.permission.AUTHENTICATE_ACCOUNTS", apis="..22") public String peekAuthToken(android.accounts.Account, String);
method @Deprecated @RequiresPermission(value="android.permission.MANAGE_ACCOUNTS", apis="..22") public android.accounts.AccountManagerFuture<java.lang.Boolean> removeAccount(android.accounts.Account, android.accounts.AccountManagerCallback<java.lang.Boolean>, android.os.Handler);
- method @RequiresPermission(value="android.permission.MANAGE_ACCOUNTS", apis="..22") public android.accounts.AccountManagerFuture<android.os.Bundle> removeAccount(android.accounts.Account, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler);
+ method @FlaggedApi("android.app.admin.flags.split_create_managed_profile_enabled") @RequiresPermission(value="android.permission.REMOVE_ACCOUNTS", conditional=true) public android.accounts.AccountManagerFuture<android.os.Bundle> removeAccount(android.accounts.Account, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler);
method @RequiresPermission(value="android.permission.AUTHENTICATE_ACCOUNTS", apis="..22") public boolean removeAccountExplicitly(android.accounts.Account);
method public void removeOnAccountsUpdatedListener(android.accounts.OnAccountsUpdateListener);
method @RequiresPermission(value="android.permission.AUTHENTICATE_ACCOUNTS", apis="..22") public android.accounts.AccountManagerFuture<android.accounts.Account> renameAccount(android.accounts.Account, @Size(min=1) String, android.accounts.AccountManagerCallback<android.accounts.Account>, android.os.Handler);
@@ -4600,6 +4614,7 @@
method public void reportFullyDrawn();
method public android.view.DragAndDropPermissions requestDragAndDropPermissions(android.view.DragEvent);
method public void requestFullscreenMode(int, @Nullable android.os.OutcomeReceiver<java.lang.Void,java.lang.Throwable>);
+ method @FlaggedApi("com.android.window.flags.enable_desktop_windowing_app_to_web_education") public final void requestOpenInBrowserEducation();
method public final void requestPermissions(@NonNull String[], int);
method @FlaggedApi("android.permission.flags.device_aware_permission_apis_enabled") public final void requestPermissions(@NonNull String[], int, int);
method public final void requestShowKeyboardShortcuts();
@@ -4625,7 +4640,6 @@
method public void setInheritShowWhenLocked(boolean);
method public void setIntent(android.content.Intent);
method @FlaggedApi("android.security.content_uri_permission_apis") public void setIntent(@Nullable android.content.Intent, @Nullable android.app.ComponentCaller);
- method @FlaggedApi("com.android.window.flags.enable_desktop_windowing_app_to_web_education") public final void setLimitSystemEducationDialogs(boolean);
method public void setLocusContext(@Nullable android.content.LocusId, @Nullable android.os.Bundle);
method public final void setMediaController(android.media.session.MediaController);
method public void setPictureInPictureParams(@NonNull android.app.PictureInPictureParams);
@@ -5096,8 +5110,11 @@
}
public class AppOpsManager {
- method @Deprecated public int checkOp(@NonNull String, int, @NonNull String);
- method @Deprecated public int checkOpNoThrow(@NonNull String, int, @NonNull String);
+ method @FlaggedApi("android.permission.flags.check_op_overload_api_enabled") public int checkOp(@NonNull String, int, @NonNull String);
+ method @FlaggedApi("android.permission.flags.check_op_overload_api_enabled") public int checkOp(@NonNull String, int, @NonNull String, @Nullable String);
+ method @FlaggedApi("android.permission.flags.check_op_overload_api_enabled") public int checkOpNoThrow(@NonNull String, int, @NonNull String, @Nullable String);
+ method @FlaggedApi("android.permission.flags.check_op_overload_api_enabled") public int checkOpNoThrow(@NonNull String, int, @NonNull String);
+ method @FlaggedApi("android.permission.flags.check_op_overload_api_enabled") public int checkOpRawNoThrow(@NonNull String, int, @NonNull String, @Nullable String);
method @Deprecated public void checkPackage(int, @NonNull String);
method @Deprecated public void finishOp(@NonNull String, int, @NonNull String);
method public void finishOp(@NonNull String, int, @NonNull String, @Nullable String);
@@ -5126,10 +5143,10 @@
method public void startWatchingMode(@NonNull String, @Nullable String, int, @NonNull android.app.AppOpsManager.OnOpChangedListener);
method public void stopWatchingActive(@NonNull android.app.AppOpsManager.OnOpActiveChangedListener);
method public void stopWatchingMode(@NonNull android.app.AppOpsManager.OnOpChangedListener);
- method public int unsafeCheckOp(@NonNull String, int, @NonNull String);
- method public int unsafeCheckOpNoThrow(@NonNull String, int, @NonNull String);
- method public int unsafeCheckOpRaw(@NonNull String, int, @NonNull String);
- method public int unsafeCheckOpRawNoThrow(@NonNull String, int, @NonNull String);
+ method @Deprecated @FlaggedApi("android.permission.flags.check_op_overload_api_enabled") public int unsafeCheckOp(@NonNull String, int, @NonNull String);
+ method @Deprecated @FlaggedApi("android.permission.flags.check_op_overload_api_enabled") public int unsafeCheckOpNoThrow(@NonNull String, int, @NonNull String);
+ method @Deprecated @FlaggedApi("android.permission.flags.check_op_overload_api_enabled") public int unsafeCheckOpRaw(@NonNull String, int, @NonNull String);
+ method @Deprecated @FlaggedApi("android.permission.flags.check_op_overload_api_enabled") public int unsafeCheckOpRawNoThrow(@NonNull String, int, @NonNull String);
field public static final int MODE_ALLOWED = 0; // 0x0
field public static final int MODE_DEFAULT = 3; // 0x3
field public static final int MODE_ERRORED = 2; // 0x2
@@ -6221,6 +6238,7 @@
}
public class KeyguardManager {
+ method @FlaggedApi("android.app.device_unlock_listener") @RequiresPermission(android.Manifest.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE) public void addDeviceLockedStateListener(@NonNull java.util.concurrent.Executor, @NonNull android.app.KeyguardManager.DeviceLockedStateListener);
method @RequiresPermission(android.Manifest.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE) public void addKeyguardLockedStateListener(@NonNull java.util.concurrent.Executor, @NonNull android.app.KeyguardManager.KeyguardLockedStateListener);
method @Deprecated public android.content.Intent createConfirmDeviceCredentialIntent(CharSequence, CharSequence);
method @Deprecated @RequiresPermission(android.Manifest.permission.DISABLE_KEYGUARD) public void exitKeyguardSecurely(android.app.KeyguardManager.OnKeyguardExitResult);
@@ -6230,10 +6248,15 @@
method public boolean isKeyguardLocked();
method public boolean isKeyguardSecure();
method @Deprecated public android.app.KeyguardManager.KeyguardLock newKeyguardLock(String);
+ method @FlaggedApi("android.app.device_unlock_listener") @RequiresPermission(android.Manifest.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE) public void removeDeviceLockedStateListener(@NonNull android.app.KeyguardManager.DeviceLockedStateListener);
method @RequiresPermission(android.Manifest.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE) public void removeKeyguardLockedStateListener(@NonNull android.app.KeyguardManager.KeyguardLockedStateListener);
method public void requestDismissKeyguard(@NonNull android.app.Activity, @Nullable android.app.KeyguardManager.KeyguardDismissCallback);
}
+ @FlaggedApi("android.app.device_unlock_listener") @java.lang.FunctionalInterface public static interface KeyguardManager.DeviceLockedStateListener {
+ method public void onDeviceLockedStateChanged(boolean);
+ }
+
public abstract static class KeyguardManager.KeyguardDismissCallback {
ctor public KeyguardManager.KeyguardDismissCallback();
method public void onDismissCancelled();
@@ -13376,6 +13399,7 @@
field public static final String FEATURE_BACKUP = "android.software.backup";
field public static final String FEATURE_BLUETOOTH = "android.hardware.bluetooth";
field public static final String FEATURE_BLUETOOTH_LE = "android.hardware.bluetooth_le";
+ field @FlaggedApi("com.android.ranging.flags.ranging_cs_enabled") public static final String FEATURE_BLUETOOTH_LE_CHANNEL_SOUNDING = "android.hardware.bluetooth_le.channel_sounding";
field public static final String FEATURE_CAMERA = "android.hardware.camera";
field public static final String FEATURE_CAMERA_ANY = "android.hardware.camera.any";
field public static final String FEATURE_CAMERA_AR = "android.hardware.camera.ar";
@@ -13567,6 +13591,7 @@
field public static final String PROPERTY_MEDIA_CAPABILITIES = "android.media.PROPERTY_MEDIA_CAPABILITIES";
field public static final String PROPERTY_SELF_CERTIFIED_NETWORK_CAPABILITIES = "android.net.PROPERTY_SELF_CERTIFIED_NETWORK_CAPABILITIES";
field public static final String PROPERTY_SPECIAL_USE_FGS_SUBTYPE = "android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE";
+ field @FlaggedApi("com.android.server.backup.enable_restricted_mode_changes") public static final String PROPERTY_USE_RESTRICTED_BACKUP_MODE = "android.app.backup.PROPERTY_USE_RESTRICTED_BACKUP_MODE";
field public static final int SIGNATURE_FIRST_NOT_SIGNED = -1; // 0xffffffff
field public static final int SIGNATURE_MATCH = 0; // 0x0
field public static final int SIGNATURE_NEITHER_SIGNED = 1; // 0x1
@@ -13784,7 +13809,7 @@
field @RequiresPermission(allOf={android.Manifest.permission.FOREGROUND_SERVICE_CAMERA}, anyOf={android.Manifest.permission.CAMERA}, conditional=true) public static final int FOREGROUND_SERVICE_TYPE_CAMERA = 64; // 0x40
field @RequiresPermission(allOf={android.Manifest.permission.FOREGROUND_SERVICE_CONNECTED_DEVICE}, anyOf={android.Manifest.permission.BLUETOOTH_ADVERTISE, android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_SCAN, android.Manifest.permission.CHANGE_NETWORK_STATE, android.Manifest.permission.CHANGE_WIFI_STATE, android.Manifest.permission.CHANGE_WIFI_MULTICAST_STATE, android.Manifest.permission.NFC, android.Manifest.permission.TRANSMIT_IR, android.Manifest.permission.UWB_RANGING, android.Manifest.permission.RANGING}, conditional=true) public static final int FOREGROUND_SERVICE_TYPE_CONNECTED_DEVICE = 16; // 0x10
field @RequiresPermission(value=android.Manifest.permission.FOREGROUND_SERVICE_DATA_SYNC, conditional=true) public static final int FOREGROUND_SERVICE_TYPE_DATA_SYNC = 1; // 0x1
- field @RequiresPermission(allOf={android.Manifest.permission.FOREGROUND_SERVICE_HEALTH}, anyOf={android.Manifest.permission.ACTIVITY_RECOGNITION, android.Manifest.permission.BODY_SENSORS, android.Manifest.permission.HIGH_SAMPLING_RATE_SENSORS}) public static final int FOREGROUND_SERVICE_TYPE_HEALTH = 256; // 0x100
+ field @FlaggedApi("android.permission.flags.replace_body_sensor_permission_enabled") @RequiresPermission(allOf={android.Manifest.permission.FOREGROUND_SERVICE_HEALTH}, anyOf={android.Manifest.permission.ACTIVITY_RECOGNITION, android.Manifest.permission.HIGH_SAMPLING_RATE_SENSORS, android.health.connect.HealthPermissions.READ_HEART_RATE, android.health.connect.HealthPermissions.READ_SKIN_TEMPERATURE, android.health.connect.HealthPermissions.READ_OXYGEN_SATURATION}) public static final int FOREGROUND_SERVICE_TYPE_HEALTH = 256; // 0x100
field @RequiresPermission(allOf={android.Manifest.permission.FOREGROUND_SERVICE_LOCATION}, anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}, conditional=true) public static final int FOREGROUND_SERVICE_TYPE_LOCATION = 8; // 0x8
field public static final int FOREGROUND_SERVICE_TYPE_MANIFEST = -1; // 0xffffffff
field @RequiresPermission(value=android.Manifest.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK, conditional=true) public static final int FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK = 2; // 0x2
@@ -13803,6 +13828,7 @@
public final class SharedLibraryInfo implements android.os.Parcelable {
method public int describeContents();
+ method @FlaggedApi("android.content.pm.sdk_dependency_installer") @NonNull public java.util.List<java.lang.String> getCertDigests();
method @NonNull public android.content.pm.VersionedPackage getDeclaringPackage();
method @NonNull public java.util.List<android.content.pm.VersionedPackage> getDependentPackages();
method @IntRange(from=0xffffffff) public long getLongVersion();
@@ -17470,6 +17496,7 @@
method public void setFloatUniform(@NonNull String, @NonNull float[]);
method public void setInputColorFilter(@NonNull String, @NonNull android.graphics.ColorFilter);
method public void setInputShader(@NonNull String, @NonNull android.graphics.Shader);
+ method public void setInputXfermode(@NonNull String, @NonNull android.graphics.RuntimeXfermode);
method public void setIntUniform(@NonNull String, int);
method public void setIntUniform(@NonNull String, int, int);
method public void setIntUniform(@NonNull String, int, int, int);
@@ -17488,7 +17515,29 @@
method public void setFloatUniform(@NonNull String, float, float, float, float);
method public void setFloatUniform(@NonNull String, @NonNull float[]);
method public void setInputBuffer(@NonNull String, @NonNull android.graphics.BitmapShader);
+ method @FlaggedApi("com.android.graphics.hwui.flags.runtime_color_filters_blenders") public void setInputColorFilter(@NonNull String, @NonNull android.graphics.ColorFilter);
method public void setInputShader(@NonNull String, @NonNull android.graphics.Shader);
+ method @FlaggedApi("com.android.graphics.hwui.flags.runtime_color_filters_blenders") public void setInputXfermode(@NonNull String, @NonNull android.graphics.RuntimeXfermode);
+ method public void setIntUniform(@NonNull String, int);
+ method public void setIntUniform(@NonNull String, int, int);
+ method public void setIntUniform(@NonNull String, int, int, int);
+ method public void setIntUniform(@NonNull String, int, int, int, int);
+ method public void setIntUniform(@NonNull String, @NonNull int[]);
+ }
+
+ @FlaggedApi("com.android.graphics.hwui.flags.runtime_color_filters_blenders") public class RuntimeXfermode extends android.graphics.Xfermode {
+ ctor public RuntimeXfermode(@NonNull String);
+ method public void setColorUniform(@NonNull String, @ColorInt int);
+ method public void setColorUniform(@NonNull String, @ColorLong long);
+ method public void setColorUniform(@NonNull String, @NonNull android.graphics.Color);
+ method public void setFloatUniform(@NonNull String, float);
+ method public void setFloatUniform(@NonNull String, float, float);
+ method public void setFloatUniform(@NonNull String, float, float, float);
+ method public void setFloatUniform(@NonNull String, float, float, float, float);
+ method public void setFloatUniform(@NonNull String, @NonNull float[]);
+ method public void setInputColorFilter(@NonNull String, @NonNull android.graphics.ColorFilter);
+ method public void setInputShader(@NonNull String, @NonNull android.graphics.Shader);
+ method public void setInputXfermode(@NonNull String, @NonNull android.graphics.RuntimeXfermode);
method public void setIntUniform(@NonNull String, int);
method public void setIntUniform(@NonNull String, int, int);
method public void setIntUniform(@NonNull String, int, int, int);
@@ -18826,6 +18875,19 @@
field public static final int TRANSFER_UNSPECIFIED = 0; // 0x0
}
+ @FlaggedApi("android.hardware.flags.luts_api") public final class DisplayLuts {
+ ctor @FlaggedApi("android.hardware.flags.luts_api") public DisplayLuts();
+ method @FlaggedApi("android.hardware.flags.luts_api") public void set(@NonNull android.hardware.DisplayLuts.Entry);
+ method @FlaggedApi("android.hardware.flags.luts_api") public void set(@NonNull android.hardware.DisplayLuts.Entry, @NonNull android.hardware.DisplayLuts.Entry);
+ }
+
+ @FlaggedApi("android.hardware.flags.luts_api") public static class DisplayLuts.Entry {
+ ctor @FlaggedApi("android.hardware.flags.luts_api") public DisplayLuts.Entry(@NonNull float[], int, int);
+ method @FlaggedApi("android.hardware.flags.luts_api") @NonNull public float[] getBuffer();
+ method @FlaggedApi("android.hardware.flags.luts_api") public int getDimension();
+ method @FlaggedApi("android.hardware.flags.luts_api") public int getSamplingKey();
+ }
+
public class GeomagneticField {
ctor public GeomagneticField(float, float, float, long);
method public float getDeclination();
@@ -18887,8 +18949,19 @@
field @FlaggedApi("android.media.codec.p210_format_support") public static final int YCBCR_P210 = 60; // 0x3c
}
+ @FlaggedApi("android.hardware.flags.luts_api") public final class LutProperties {
+ method @FlaggedApi("android.hardware.flags.luts_api") public int getDimension();
+ method @FlaggedApi("android.hardware.flags.luts_api") @NonNull public int[] getSamplingKeys();
+ method @FlaggedApi("android.hardware.flags.luts_api") public int getSize();
+ field @FlaggedApi("android.hardware.flags.luts_api") public static final int ONE_DIMENSION = 1; // 0x1
+ field @FlaggedApi("android.hardware.flags.luts_api") public static final int SAMPLING_KEY_MAX_RGB = 1; // 0x1
+ field @FlaggedApi("android.hardware.flags.luts_api") public static final int SAMPLING_KEY_RGB = 0; // 0x0
+ field @FlaggedApi("android.hardware.flags.luts_api") public static final int THREE_DIMENSION = 3; // 0x3
+ }
+
@FlaggedApi("android.hardware.flags.overlayproperties_class_api") public final class OverlayProperties implements android.os.Parcelable {
method @FlaggedApi("android.hardware.flags.overlayproperties_class_api") public int describeContents();
+ method @FlaggedApi("android.hardware.flags.luts_api") @NonNull public android.hardware.LutProperties[] getLutProperties();
method @FlaggedApi("android.hardware.flags.overlayproperties_class_api") public boolean isCombinationSupported(int, int);
method @FlaggedApi("android.hardware.flags.overlayproperties_class_api") public boolean isMixedColorSpacesSupported();
method @FlaggedApi("android.hardware.flags.overlayproperties_class_api") public void writeToParcel(@NonNull android.os.Parcel, int);
@@ -20628,8 +20701,14 @@
method @NonNull public android.hardware.display.HdrConversionMode getHdrConversionMode();
method public int getMatchContentFrameRateUserPreference();
method public void registerDisplayListener(android.hardware.display.DisplayManager.DisplayListener, android.os.Handler);
+ method @FlaggedApi("com.android.server.display.feature.flags.display_listener_performance_improvements") public void registerDisplayListener(@NonNull java.util.concurrent.Executor, long, @NonNull android.hardware.display.DisplayManager.DisplayListener);
method public void unregisterDisplayListener(android.hardware.display.DisplayManager.DisplayListener);
field public static final String DISPLAY_CATEGORY_PRESENTATION = "android.hardware.display.category.PRESENTATION";
+ field @FlaggedApi("com.android.server.display.feature.flags.display_listener_performance_improvements") public static final long EVENT_FLAG_DISPLAY_ADDED = 1L; // 0x1L
+ field @FlaggedApi("com.android.server.display.feature.flags.display_listener_performance_improvements") public static final long EVENT_FLAG_DISPLAY_CHANGED = 4L; // 0x4L
+ field @FlaggedApi("com.android.server.display.feature.flags.display_listener_performance_improvements") public static final long EVENT_FLAG_DISPLAY_REFRESH_RATE = 8L; // 0x8L
+ field @FlaggedApi("com.android.server.display.feature.flags.display_listener_performance_improvements") public static final long EVENT_FLAG_DISPLAY_REMOVED = 2L; // 0x2L
+ field @FlaggedApi("com.android.server.display.feature.flags.display_listener_performance_improvements") public static final long EVENT_FLAG_DISPLAY_STATE = 16L; // 0x10L
field public static final int MATCH_CONTENT_FRAMERATE_ALWAYS = 2; // 0x2
field public static final int MATCH_CONTENT_FRAMERATE_NEVER = 0; // 0x0
field public static final int MATCH_CONTENT_FRAMERATE_SEAMLESSS_ONLY = 1; // 0x1
@@ -21135,6 +21214,7 @@
method public void setExtractView(android.view.View);
method public void setExtractViewShown(boolean);
method public void setInputView(android.view.View);
+ method @FlaggedApi("android.view.inputmethod.adaptive_handwriting_bounds") public final void setStylusHandwritingRegion(@NonNull android.graphics.Region);
method public final void setStylusHandwritingSessionTimeout(@NonNull java.time.Duration);
method public final boolean shouldOfferSwitchingToNextInputMethod();
method public void showStatusIcon(@DrawableRes int);
@@ -21488,6 +21568,7 @@
method public int getId();
method public CharSequence getProductName();
method @NonNull public int[] getSampleRates();
+ method @FlaggedApi("android.media.audio.speaker_layout_api") public int getSpeakerLayoutChannelMask();
method public int getType();
method public boolean isSink();
method public boolean isSource();
@@ -21976,7 +22057,7 @@
public final class AudioPlaybackConfiguration implements android.os.Parcelable {
method public int describeContents();
method public android.media.AudioAttributes getAudioAttributes();
- method @Nullable public android.media.AudioDeviceInfo getAudioDeviceInfo();
+ method @Deprecated @FlaggedApi("android.media.audio.routed_device_ids") @Nullable public android.media.AudioDeviceInfo getAudioDeviceInfo();
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.media.AudioPlaybackConfiguration> CREATOR;
}
@@ -22059,6 +22140,7 @@
method public android.media.AudioDeviceInfo getPreferredDevice();
method public int getRecordingState();
method public android.media.AudioDeviceInfo getRoutedDevice();
+ method @FlaggedApi("android.media.audio.routed_device_ids") @NonNull public java.util.List<android.media.AudioDeviceInfo> getRoutedDevices();
method public int getSampleRate();
method public int getState();
method public int getTimestamp(@NonNull android.media.AudioTimestamp, int);
@@ -22153,6 +22235,7 @@
method public void addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
method public android.media.AudioDeviceInfo getPreferredDevice();
method public android.media.AudioDeviceInfo getRoutedDevice();
+ method @FlaggedApi("android.media.audio.routed_device_ids") @NonNull public default java.util.List<android.media.AudioDeviceInfo> getRoutedDevices();
method public void removeOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener);
method public boolean setPreferredDevice(android.media.AudioDeviceInfo);
}
@@ -22211,6 +22294,7 @@
method public int getPositionNotificationPeriod();
method public android.media.AudioDeviceInfo getPreferredDevice();
method public android.media.AudioDeviceInfo getRoutedDevice();
+ method @FlaggedApi("android.media.audio.routed_device_ids") @NonNull public java.util.List<android.media.AudioDeviceInfo> getRoutedDevices();
method public int getSampleRate();
method @IntRange(from=1) public int getStartThresholdInFrames();
method public int getState();
@@ -23107,7 +23191,6 @@
method public boolean isVendor();
field @FlaggedApi("android.media.codec.in_process_sw_audio_codec") public static final int SECURITY_MODEL_MEMORY_SAFE = 1; // 0x1
field @FlaggedApi("android.media.codec.in_process_sw_audio_codec") public static final int SECURITY_MODEL_SANDBOXED = 0; // 0x0
- field @FlaggedApi("android.media.codec.in_process_sw_audio_codec") public static final int SECURITY_MODEL_TRUSTED_CONTENT_ONLY = 2; // 0x2
}
public static final class MediaCodecInfo.AudioCapabilities {
@@ -23998,7 +24081,6 @@
field public static final int COLOR_TRANSFER_ST2084 = 6; // 0x6
field @FlaggedApi("android.media.codec.in_process_sw_audio_codec") public static final int FLAG_SECURITY_MODEL_MEMORY_SAFE = 2; // 0x2
field @FlaggedApi("android.media.codec.in_process_sw_audio_codec") public static final int FLAG_SECURITY_MODEL_SANDBOXED = 1; // 0x1
- field @FlaggedApi("android.media.codec.in_process_sw_audio_codec") public static final int FLAG_SECURITY_MODEL_TRUSTED_CONTENT_ONLY = 4; // 0x4
field public static final String KEY_AAC_DRC_ALBUM_MODE = "aac-drc-album-mode";
field public static final String KEY_AAC_DRC_ATTENUATION_FACTOR = "aac-drc-cut-level";
field public static final String KEY_AAC_DRC_BOOST_FACTOR = "aac-drc-boost-level";
@@ -24379,6 +24461,7 @@
method @NonNull public android.media.PlaybackParams getPlaybackParams();
method public android.media.AudioDeviceInfo getPreferredDevice();
method public android.media.AudioDeviceInfo getRoutedDevice();
+ method @FlaggedApi("android.media.audio.routed_device_ids") @NonNull public java.util.List<android.media.AudioDeviceInfo> getRoutedDevices();
method public int getSelectedTrack(int) throws java.lang.IllegalStateException;
method @NonNull public android.media.SyncParams getSyncParams();
method @Nullable public android.media.MediaTimestamp getTimestamp();
@@ -24592,6 +24675,7 @@
method public android.os.PersistableBundle getMetrics();
method public android.media.AudioDeviceInfo getPreferredDevice();
method public android.media.AudioDeviceInfo getRoutedDevice();
+ method @FlaggedApi("android.media.audio.routed_device_ids") @NonNull public java.util.List<android.media.AudioDeviceInfo> getRoutedDevices();
method public android.view.Surface getSurface();
method public boolean isPrivacySensitive();
method public void pause() throws java.lang.IllegalStateException;
@@ -26974,6 +27058,86 @@
}
+package android.media.quality {
+
+ @FlaggedApi("android.media.tv.flags.media_quality_fw") public class MediaQualityContract {
+ }
+
+ public static final class MediaQualityContract.PictureQuality {
+ field public static final String PARAMETER_BRIGHTNESS = "brightness";
+ field public static final String PARAMETER_CONTRAST = "contrast";
+ field public static final String PARAMETER_SATURATION = "saturation";
+ field public static final String PARAMETER_SHARPNESS = "sharpness";
+ }
+
+ @FlaggedApi("android.media.tv.flags.media_quality_fw") public final class MediaQualityManager {
+ method public void createPictureProfile(@NonNull android.media.quality.PictureProfile);
+ method @NonNull public java.util.List<android.media.quality.PictureProfile> getAvailablePictureProfiles();
+ method @NonNull public java.util.List<android.media.quality.ParamCapability> getParamCapabilities(@NonNull java.util.List<java.lang.String>);
+ method @Nullable public android.media.quality.PictureProfile getPictureProfile(int, @NonNull String);
+ method public boolean isAutoPictureQualityEnabled();
+ method public boolean isSuperResolutionEnabled();
+ method public void registerPictureProfileCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.quality.MediaQualityManager.PictureProfileCallback);
+ method public void removePictureProfile(@NonNull String);
+ method public void unregisterPictureProfileCallback(@NonNull android.media.quality.MediaQualityManager.PictureProfileCallback);
+ method public void updatePictureProfile(@NonNull String, @NonNull android.media.quality.PictureProfile);
+ }
+
+ public abstract static class MediaQualityManager.PictureProfileCallback {
+ ctor public MediaQualityManager.PictureProfileCallback();
+ method public void onError(int);
+ method public void onParamCapabilitiesChanged(@Nullable String, @NonNull java.util.List<android.media.quality.ParamCapability>);
+ method public void onPictureProfileAdded(@NonNull String, @NonNull android.media.quality.PictureProfile);
+ method public void onPictureProfileRemoved(@NonNull String, @NonNull android.media.quality.PictureProfile);
+ method public void onPictureProfileUpdated(@NonNull String, @NonNull android.media.quality.PictureProfile);
+ }
+
+ @FlaggedApi("android.media.tv.flags.media_quality_fw") public final class ParamCapability implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public android.os.Bundle getCapabilities();
+ method @NonNull public String getParamName();
+ method public int getParamType();
+ method public boolean isSupported();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field public static final String CAPABILITY_DEFAULT = "default";
+ field public static final String CAPABILITY_ENUM = "enum";
+ field public static final String CAPABILITY_MAX = "max";
+ field public static final String CAPABILITY_MIN = "min";
+ field @NonNull public static final android.os.Parcelable.Creator<android.media.quality.ParamCapability> CREATOR;
+ field public static final int TYPE_DOUBLE = 3; // 0x3
+ field public static final int TYPE_INT = 1; // 0x1
+ field public static final int TYPE_LONG = 2; // 0x2
+ field public static final int TYPE_STRING = 4; // 0x4
+ }
+
+ @FlaggedApi("android.media.tv.flags.media_quality_fw") public final class PictureProfile implements android.os.Parcelable {
+ method public int describeContents();
+ method @Nullable public String getInputId();
+ method @NonNull public String getName();
+ method @Nullable public String getPackageName();
+ method @NonNull public android.os.PersistableBundle getParameters();
+ method @Nullable public String getProfileId();
+ method public int getProfileType();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.media.quality.PictureProfile> CREATOR;
+ field public static final int ERROR_DUPLICATE = 2; // 0x2
+ field public static final int ERROR_INVALID_ARGUMENT = 3; // 0x3
+ field public static final int ERROR_NOT_ALLOWLISTED = 4; // 0x4
+ field public static final int ERROR_NO_PERMISSION = 1; // 0x1
+ field public static final int ERROR_UNKNOWN = 0; // 0x0
+ field public static final int TYPE_APPLICATION = 2; // 0x2
+ field public static final int TYPE_SYSTEM = 1; // 0x1
+ }
+
+ public static final class PictureProfile.Builder {
+ ctor public PictureProfile.Builder(@NonNull String);
+ ctor public PictureProfile.Builder(@NonNull android.media.quality.PictureProfile);
+ method @NonNull public android.media.quality.PictureProfile build();
+ method @NonNull public android.media.quality.PictureProfile.Builder setParameters(@NonNull android.os.PersistableBundle);
+ }
+
+}
+
package android.media.session {
public final class MediaController {
@@ -28523,6 +28687,8 @@
method public void setCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.tv.ad.TvAdView.TvAdCallback);
method public void setOnUnhandledInputEventListener(@NonNull android.media.tv.ad.TvAdView.OnUnhandledInputEventListener);
method public boolean setTvView(@Nullable android.media.tv.TvView);
+ method @FlaggedApi("android.media.tv.flags.enable_ad_service_fw") public void setZOrderMediaOverlay(boolean);
+ method @FlaggedApi("android.media.tv.flags.enable_ad_service_fw") public void setZOrderOnTop(boolean);
method public void startAdService();
method public void stopAdService();
field public static final String ERROR_KEY_ERROR_CODE = "error_code";
@@ -28795,6 +28961,8 @@
method public void setOnUnhandledInputEventListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.tv.interactive.TvInteractiveAppView.OnUnhandledInputEventListener);
method public void setTeletextAppEnabled(boolean);
method public int setTvView(@Nullable android.media.tv.TvView);
+ method @FlaggedApi("android.media.tv.flags.tiaf_v_apis") public void setZOrderMediaOverlay(boolean);
+ method @FlaggedApi("android.media.tv.flags.tiaf_v_apis") public void setZOrderOnTop(boolean);
method public void startInteractiveApp();
method public void stopInteractiveApp();
field public static final String BI_INTERACTIVE_APP_KEY_ALIAS = "alias";
@@ -29834,6 +30002,7 @@
method @NonNull public java.util.List<android.net.vcn.VcnUnderlyingNetworkTemplate> getVcnUnderlyingNetworkPriorities();
method public boolean hasGatewayOption(int);
method @FlaggedApi("android.net.vcn.safe_mode_config") public boolean isSafeModeEnabled();
+ field @FlaggedApi("android.net.vcn.mainline_vcn_module_api") public static final int MIN_UDP_PORT_4500_NAT_TIMEOUT_UNSET = -1; // 0xffffffff
field public static final int VCN_GATEWAY_OPTION_ENABLE_DATA_STALL_RECOVERY_WITH_MOBILITY = 0; // 0x0
}
@@ -33307,6 +33476,14 @@
method public final android.os.CountDownTimer start();
}
+ @FlaggedApi("android.os.cpu_gpu_headrooms") public final class CpuHeadroomParams {
+ ctor public CpuHeadroomParams();
+ method public int getCalculationType();
+ method public void setCalculationType(int);
+ field public static final int CPU_HEADROOM_CALCULATION_TYPE_AVERAGE = 1; // 0x1
+ field public static final int CPU_HEADROOM_CALCULATION_TYPE_MIN = 0; // 0x0
+ }
+
public final class CpuUsageInfo implements android.os.Parcelable {
method public int describeContents();
method public long getActive();
@@ -33554,6 +33731,14 @@
method public void onProgress(long);
}
+ @FlaggedApi("android.os.cpu_gpu_headrooms") public final class GpuHeadroomParams {
+ ctor public GpuHeadroomParams();
+ method public int getCalculationType();
+ method public void setCalculationType(int);
+ field public static final int GPU_HEADROOM_CALCULATION_TYPE_AVERAGE = 1; // 0x1
+ field public static final int GPU_HEADROOM_CALCULATION_TYPE_MIN = 0; // 0x0
+ }
+
public class Handler {
ctor @Deprecated public Handler();
ctor @Deprecated public Handler(@Nullable android.os.Handler.Callback);
@@ -33624,7 +33809,7 @@
}
public interface IBinder {
- method @FlaggedApi("android.os.binder_frozen_state_change_callback") public default void addFrozenStateChangeCallback(@NonNull android.os.IBinder.FrozenStateChangeCallback) throws android.os.RemoteException;
+ method @FlaggedApi("android.os.binder_frozen_state_change_callback") public default void addFrozenStateChangeCallback(@NonNull java.util.concurrent.Executor, @NonNull android.os.IBinder.FrozenStateChangeCallback) throws android.os.RemoteException;
method public void dump(@NonNull java.io.FileDescriptor, @Nullable String[]) throws android.os.RemoteException;
method public void dumpAsync(@NonNull java.io.FileDescriptor, @Nullable String[]) throws android.os.RemoteException;
method @Nullable public String getInterfaceDescriptor() throws android.os.RemoteException;
@@ -34255,6 +34440,7 @@
method public void finishBroadcast();
method public Object getBroadcastCookie(int);
method public E getBroadcastItem(int);
+ method @FlaggedApi("android.os.binder_frozen_state_change_callback") @Nullable public java.util.concurrent.Executor getExecutor();
method @FlaggedApi("android.os.binder_frozen_state_change_callback") public int getFrozenCalleePolicy();
method @FlaggedApi("android.os.binder_frozen_state_change_callback") public int getMaxQueueSize();
method public Object getRegisteredCallbackCookie(int);
@@ -34275,6 +34461,7 @@
@FlaggedApi("android.os.binder_frozen_state_change_callback") public static final class RemoteCallbackList.Builder<E extends android.os.IInterface> {
ctor public RemoteCallbackList.Builder(int);
method @NonNull public android.os.RemoteCallbackList<E> build();
+ method @NonNull public android.os.RemoteCallbackList.Builder setExecutor(@NonNull java.util.concurrent.Executor);
method @NonNull public android.os.RemoteCallbackList.Builder setInterfaceDiedCallback(@NonNull android.os.RemoteCallbackList.Builder.InterfaceDiedCallback<E>);
method @NonNull public android.os.RemoteCallbackList.Builder setMaxQueueSize(int);
}
@@ -34654,8 +34841,6 @@
method public static android.os.VibrationEffect createWaveform(long[], int[], int);
method public int describeContents();
method @NonNull public static android.os.VibrationEffect.Composition startComposition();
- method @FlaggedApi("android.os.vibrator.normalized_pwle_effects") @NonNull public static android.os.VibrationEffect.WaveformEnvelopeBuilder startWaveformEnvelope();
- method @FlaggedApi("android.os.vibrator.normalized_pwle_effects") @NonNull public static android.os.VibrationEffect.WaveformEnvelopeBuilder startWaveformEnvelope(@FloatRange(from=0) float);
field @NonNull public static final android.os.Parcelable.Creator<android.os.VibrationEffect> CREATOR;
field public static final int DEFAULT_AMPLITUDE = -1; // 0xffffffff
field public static final int EFFECT_CLICK = 0; // 0x0
@@ -34683,8 +34868,10 @@
}
@FlaggedApi("android.os.vibrator.normalized_pwle_effects") public static final class VibrationEffect.WaveformEnvelopeBuilder {
+ ctor public VibrationEffect.WaveformEnvelopeBuilder();
method @FlaggedApi("android.os.vibrator.normalized_pwle_effects") @NonNull public android.os.VibrationEffect.WaveformEnvelopeBuilder addControlPoint(@FloatRange(from=0, to=1) float, @FloatRange(from=0) float, int);
method @FlaggedApi("android.os.vibrator.normalized_pwle_effects") @NonNull public android.os.VibrationEffect build();
+ method @FlaggedApi("android.os.vibrator.normalized_pwle_effects") @NonNull public android.os.VibrationEffect.WaveformEnvelopeBuilder setInitialFrequencyHz(@FloatRange(from=0) float);
}
public abstract class Vibrator {
@@ -34694,12 +34881,9 @@
method @FlaggedApi("android.os.vibrator.normalized_pwle_effects") public boolean areEnvelopeEffectsSupported();
method @NonNull public boolean[] arePrimitivesSupported(@NonNull int...);
method @RequiresPermission(android.Manifest.permission.VIBRATE) public abstract void cancel();
+ method @FlaggedApi("android.os.vibrator.normalized_pwle_effects") @NonNull public android.os.vibrator.VibratorEnvelopeEffectInfo getEnvelopeEffectInfo();
method @FlaggedApi("android.os.vibrator.normalized_pwle_effects") @Nullable public android.os.vibrator.VibratorFrequencyProfile getFrequencyProfile();
method public int getId();
- method @FlaggedApi("android.os.vibrator.normalized_pwle_effects") public int getMaxEnvelopeEffectControlPointDurationMillis();
- method @FlaggedApi("android.os.vibrator.normalized_pwle_effects") public int getMaxEnvelopeEffectDurationMillis();
- method @FlaggedApi("android.os.vibrator.normalized_pwle_effects") public int getMaxEnvelopeEffectSize();
- method @FlaggedApi("android.os.vibrator.normalized_pwle_effects") public int getMinEnvelopeEffectControlPointDurationMillis();
method @NonNull public int[] getPrimitiveDurations(@NonNull int...);
method public float getQFactor();
method public float getResonantFrequency();
@@ -34807,6 +34991,10 @@
}
public class SystemHealthManager {
+ method @FlaggedApi("android.os.cpu_gpu_headrooms") @FloatRange(from=0.0f, to=100.0f) public float getCpuHeadroom(@Nullable android.os.CpuHeadroomParams);
+ method @FlaggedApi("android.os.cpu_gpu_headrooms") public long getCpuHeadroomMinIntervalMillis();
+ method @FlaggedApi("android.os.cpu_gpu_headrooms") @FloatRange(from=0.0f, to=100.0f) public float getGpuHeadroom(@Nullable android.os.GpuHeadroomParams);
+ method @FlaggedApi("android.os.cpu_gpu_headrooms") public long getGpuHeadroomMinIntervalMillis();
method @FlaggedApi("com.android.server.power.optimization.power_monitor_api") public void getPowerMonitorReadings(@NonNull java.util.List<android.os.PowerMonitor>, @Nullable java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.os.PowerMonitorReadings,java.lang.RuntimeException>);
method @FlaggedApi("com.android.server.power.optimization.power_monitor_api") public void getSupportedPowerMonitors(@Nullable java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.util.List<android.os.PowerMonitor>>);
method public android.os.health.HealthStats takeMyUidSnapshot();
@@ -35050,6 +35238,16 @@
package android.os.vibrator {
+ @FlaggedApi("android.os.vibrator.normalized_pwle_effects") public final class VibratorEnvelopeEffectInfo implements android.os.Parcelable {
+ method public int describeContents();
+ method public long getMaxControlPointDurationMillis();
+ method public long getMaxDurationMillis();
+ method public int getMaxSize();
+ method public long getMinControlPointDurationMillis();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.os.vibrator.VibratorEnvelopeEffectInfo> CREATOR;
+ }
+
@FlaggedApi("android.os.vibrator.normalized_pwle_effects") public final class VibratorFrequencyProfile {
method @FlaggedApi("android.os.vibrator.normalized_pwle_effects") @NonNull public android.util.SparseArray<java.lang.Float> getFrequenciesOutputAcceleration();
method @FlaggedApi("android.os.vibrator.normalized_pwle_effects") @Nullable public android.util.Range<java.lang.Float> getFrequencyRange(float);
@@ -40668,6 +40866,7 @@
method public int describeContents();
method @Deprecated @Nullable public android.os.Bundle getClientState();
method @Nullable public java.util.List<android.service.autofill.FillEventHistory.Event> getEvents();
+ method @FlaggedApi("android.service.autofill.autofill_w_metrics") public int getSessionId();
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.service.autofill.FillEventHistory> CREATOR;
}
@@ -40677,10 +40876,12 @@
method @Nullable public android.os.Bundle getClientState();
method @Nullable public String getDatasetId();
method @NonNull public java.util.Map<android.view.autofill.AutofillId,android.service.autofill.FieldClassification> getFieldsClassification();
+ method @FlaggedApi("android.service.autofill.autofill_w_metrics") @Nullable public android.view.autofill.AutofillId getFocusedId();
method @NonNull public java.util.Set<java.lang.String> getIgnoredDatasetIds();
method @NonNull public java.util.Map<android.view.autofill.AutofillId,java.util.Set<java.lang.String>> getManuallyEnteredField();
method public int getNoSaveUiReason();
method @NonNull public java.util.Set<java.lang.String> getSelectedDatasetIds();
+ method @FlaggedApi("android.service.autofill.autofill_w_metrics") @NonNull public java.util.Set<java.lang.String> getShownDatasetIds();
method public int getType();
method public int getUiType();
field public static final int NO_SAVE_UI_REASON_DATASET_MATCH = 6; // 0x6
@@ -40689,6 +40890,7 @@
field public static final int NO_SAVE_UI_REASON_NONE = 0; // 0x0
field public static final int NO_SAVE_UI_REASON_NO_SAVE_INFO = 1; // 0x1
field public static final int NO_SAVE_UI_REASON_NO_VALUE_CHANGED = 4; // 0x4
+ field @FlaggedApi("android.service.autofill.autofill_w_metrics") public static final int NO_SAVE_UI_REASON_USING_CREDMAN = 7; // 0x7
field public static final int NO_SAVE_UI_REASON_WITH_DELAY_SAVE_FLAG = 2; // 0x2
field public static final int TYPE_AUTHENTICATION_SELECTED = 2; // 0x2
field public static final int TYPE_CONTEXT_COMMITTED = 4; // 0x4
@@ -40697,6 +40899,7 @@
field public static final int TYPE_DATASET_SELECTED = 0; // 0x0
field public static final int TYPE_SAVE_SHOWN = 3; // 0x3
field public static final int TYPE_VIEW_REQUESTED_AUTOFILL = 6; // 0x6
+ field @FlaggedApi("android.service.autofill.autofill_w_metrics") public static final int UI_TYPE_CREDMAN = 4; // 0x4
field public static final int UI_TYPE_DIALOG = 3; // 0x3
field public static final int UI_TYPE_INLINE = 2; // 0x2
field public static final int UI_TYPE_MENU = 1; // 0x1
@@ -40978,6 +41181,18 @@
method @Deprecated public void onSendTextSms(@NonNull String, int, @NonNull String, @NonNull android.service.carrier.CarrierMessagingService.ResultCallback<android.service.carrier.CarrierMessagingService.SendSmsResult>);
method public void onSendTextSms(@NonNull String, int, @NonNull String, int, @NonNull android.service.carrier.CarrierMessagingService.ResultCallback<android.service.carrier.CarrierMessagingService.SendSmsResult>);
field public static final int DOWNLOAD_STATUS_ERROR = 2; // 0x2
+ field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int DOWNLOAD_STATUS_MMS_ERROR_CONFIGURATION_ERROR = 606; // 0x25e
+ field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int DOWNLOAD_STATUS_MMS_ERROR_DATA_DISABLED = 610; // 0x262
+ field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int DOWNLOAD_STATUS_MMS_ERROR_HTTP_FAILURE = 603; // 0x25b
+ field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int DOWNLOAD_STATUS_MMS_ERROR_INACTIVE_SUBSCRIPTION = 609; // 0x261
+ field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int DOWNLOAD_STATUS_MMS_ERROR_INVALID_APN = 601; // 0x259
+ field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int DOWNLOAD_STATUS_MMS_ERROR_INVALID_SUBSCRIPTION_ID = 608; // 0x260
+ field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int DOWNLOAD_STATUS_MMS_ERROR_IO_ERROR = 604; // 0x25c
+ field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int DOWNLOAD_STATUS_MMS_ERROR_MMS_DISABLED_BY_CARRIER = 611; // 0x263
+ field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int DOWNLOAD_STATUS_MMS_ERROR_NO_DATA_NETWORK = 607; // 0x25f
+ field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int DOWNLOAD_STATUS_MMS_ERROR_RETRY = 605; // 0x25d
+ field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int DOWNLOAD_STATUS_MMS_ERROR_UNABLE_CONNECT_MMS = 602; // 0x25a
+ field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int DOWNLOAD_STATUS_MMS_ERROR_UNSPECIFIED = 600; // 0x258
field public static final int DOWNLOAD_STATUS_OK = 0; // 0x0
field public static final int DOWNLOAD_STATUS_RETRY_ON_CARRIER_NETWORK = 1; // 0x1
field public static final int RECEIVE_OPTIONS_DEFAULT = 0; // 0x0
@@ -40985,7 +41200,38 @@
field public static final int RECEIVE_OPTIONS_SKIP_NOTIFY_WHEN_CREDENTIAL_PROTECTED_STORAGE_UNAVAILABLE = 2; // 0x2
field public static final int SEND_FLAG_REQUEST_DELIVERY_STATUS = 1; // 0x1
field public static final int SEND_STATUS_ERROR = 2; // 0x2
+ field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int SEND_STATUS_MMS_ERROR_CONFIGURATION_ERROR = 406; // 0x196
+ field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int SEND_STATUS_MMS_ERROR_DATA_DISABLED = 410; // 0x19a
+ field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int SEND_STATUS_MMS_ERROR_HTTP_FAILURE = 403; // 0x193
+ field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int SEND_STATUS_MMS_ERROR_INACTIVE_SUBSCRIPTION = 409; // 0x199
+ field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int SEND_STATUS_MMS_ERROR_INVALID_APN = 401; // 0x191
+ field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int SEND_STATUS_MMS_ERROR_INVALID_SUBSCRIPTION_ID = 408; // 0x198
+ field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int SEND_STATUS_MMS_ERROR_IO_ERROR = 404; // 0x194
+ field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int SEND_STATUS_MMS_ERROR_MMS_DISABLED_BY_CARRIER = 411; // 0x19b
+ field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int SEND_STATUS_MMS_ERROR_NO_DATA_NETWORK = 407; // 0x197
+ field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int SEND_STATUS_MMS_ERROR_RETRY = 405; // 0x195
+ field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int SEND_STATUS_MMS_ERROR_UNABLE_CONNECT_MMS = 402; // 0x192
+ field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int SEND_STATUS_MMS_ERROR_UNSPECIFIED = 400; // 0x190
field public static final int SEND_STATUS_OK = 0; // 0x0
+ field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int SEND_STATUS_RESULT_CANCELLED = 215; // 0xd7
+ field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int SEND_STATUS_RESULT_ENCODING_ERROR = 212; // 0xd4
+ field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int SEND_STATUS_RESULT_ERROR_FDN_CHECK_FAILURE = 204; // 0xcc
+ field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int SEND_STATUS_RESULT_ERROR_GENERIC_FAILURE = 200; // 0xc8
+ field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int SEND_STATUS_RESULT_ERROR_LIMIT_EXCEEDED = 203; // 0xcb
+ field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int SEND_STATUS_RESULT_ERROR_NO_SERVICE = 202; // 0xca
+ field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int SEND_STATUS_RESULT_ERROR_NULL_PDU = 201; // 0xc9
+ field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int SEND_STATUS_RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED = 206; // 0xce
+ field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int SEND_STATUS_RESULT_ERROR_SHORT_CODE_NOT_ALLOWED = 205; // 0xcd
+ field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int SEND_STATUS_RESULT_INVALID_ARGUMENTS = 208; // 0xd0
+ field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int SEND_STATUS_RESULT_INVALID_SMSC_ADDRESS = 213; // 0xd5
+ field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int SEND_STATUS_RESULT_INVALID_SMS_FORMAT = 210; // 0xd2
+ field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int SEND_STATUS_RESULT_INVALID_STATE = 209; // 0xd1
+ field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int SEND_STATUS_RESULT_NETWORK_ERROR = 211; // 0xd3
+ field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int SEND_STATUS_RESULT_NETWORK_REJECT = 207; // 0xcf
+ field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int SEND_STATUS_RESULT_OPERATION_NOT_ALLOWED = 214; // 0xd6
+ field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int SEND_STATUS_RESULT_REQUEST_NOT_SUPPORTED = 216; // 0xd8
+ field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int SEND_STATUS_RESULT_SMS_BLOCKED_DURING_EMERGENCY = 217; // 0xd9
+ field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int SEND_STATUS_RESULT_SMS_SEND_RETRY_FAILED = 218; // 0xda
field public static final int SEND_STATUS_RETRY_ON_CARRIER_NETWORK = 1; // 0x1
field public static final String SERVICE_INTERFACE = "android.service.carrier.CarrierMessagingService";
}
@@ -41982,6 +42228,7 @@
public abstract class QuickAccessWalletService extends android.app.Service {
ctor public QuickAccessWalletService();
+ method @FlaggedApi("android.service.quickaccesswallet.launch_wallet_option_on_power_double_tap") @Nullable public android.app.PendingIntent getGestureTargetActivityPendingIntent();
method @Nullable public android.app.PendingIntent getTargetActivityPendingIntent();
method @Nullable public android.os.IBinder onBind(@NonNull android.content.Intent);
method public abstract void onWalletCardSelected(@NonNull android.service.quickaccesswallet.SelectWalletCardRequest);
@@ -46659,6 +46906,8 @@
method public long getDataUsageBytes();
method public long getDataUsageTime();
method @NonNull public int[] getNetworkTypes();
+ method @FlaggedApi("com.android.internal.telephony.flags.subscription_plan_allow_status_and_end_date") @Nullable public java.time.ZonedDateTime getPlanEndDate();
+ method @FlaggedApi("com.android.internal.telephony.flags.subscription_plan_allow_status_and_end_date") public int getSubscriptionStatus();
method @Nullable public CharSequence getSummary();
method @Nullable public CharSequence getTitle();
method public void writeToParcel(android.os.Parcel, int);
@@ -46669,6 +46918,11 @@
field public static final int LIMIT_BEHAVIOR_DISABLED = 0; // 0x0
field public static final int LIMIT_BEHAVIOR_THROTTLED = 2; // 0x2
field public static final int LIMIT_BEHAVIOR_UNKNOWN = -1; // 0xffffffff
+ field @FlaggedApi("com.android.internal.telephony.flags.subscription_plan_allow_status_and_end_date") public static final int SUBSCRIPTION_STATUS_ACTIVE = 1; // 0x1
+ field @FlaggedApi("com.android.internal.telephony.flags.subscription_plan_allow_status_and_end_date") public static final int SUBSCRIPTION_STATUS_INACTIVE = 2; // 0x2
+ field @FlaggedApi("com.android.internal.telephony.flags.subscription_plan_allow_status_and_end_date") public static final int SUBSCRIPTION_STATUS_SUSPENDED = 4; // 0x4
+ field @FlaggedApi("com.android.internal.telephony.flags.subscription_plan_allow_status_and_end_date") public static final int SUBSCRIPTION_STATUS_TRIAL = 3; // 0x3
+ field @FlaggedApi("com.android.internal.telephony.flags.subscription_plan_allow_status_and_end_date") public static final int SUBSCRIPTION_STATUS_UNKNOWN = 0; // 0x0
field public static final long TIME_UNKNOWN = -1L; // 0xffffffffffffffffL
}
@@ -46680,6 +46934,7 @@
method public android.telephony.SubscriptionPlan.Builder setDataLimit(long, int);
method public android.telephony.SubscriptionPlan.Builder setDataUsage(long, long);
method @NonNull public android.telephony.SubscriptionPlan.Builder setNetworkTypes(@NonNull int[]);
+ method @FlaggedApi("com.android.internal.telephony.flags.subscription_plan_allow_status_and_end_date") @NonNull public android.telephony.SubscriptionPlan.Builder setSubscriptionStatus(int);
method public android.telephony.SubscriptionPlan.Builder setSummary(@Nullable CharSequence);
method public android.telephony.SubscriptionPlan.Builder setTitle(@Nullable CharSequence);
}
@@ -49659,6 +49914,14 @@
method public abstract void updateMeasureState(@NonNull android.text.TextPaint);
}
+ @FlaggedApi("android.view.inputmethod.writing_tools") public final class NoWritingToolsSpan implements android.text.ParcelableSpan {
+ ctor public NoWritingToolsSpan();
+ method public int describeContents();
+ method public int getSpanTypeId();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.text.style.NoWritingToolsSpan> CREATOR;
+ }
+
public interface ParagraphStyle {
}
@@ -53155,6 +53418,7 @@
method @FlaggedApi("com.android.window.flags.sdk_desired_present_time") @NonNull public android.view.SurfaceControl.Transaction setFrameTimeline(long);
method @Deprecated @NonNull public android.view.SurfaceControl.Transaction setGeometry(@NonNull android.view.SurfaceControl, @Nullable android.graphics.Rect, @Nullable android.graphics.Rect, int);
method @NonNull public android.view.SurfaceControl.Transaction setLayer(@NonNull android.view.SurfaceControl, @IntRange(from=java.lang.Integer.MIN_VALUE, to=java.lang.Integer.MAX_VALUE) int);
+ method @FlaggedApi("android.hardware.flags.luts_api") @NonNull public android.view.SurfaceControl.Transaction setLuts(@NonNull android.view.SurfaceControl, @Nullable android.hardware.DisplayLuts);
method @NonNull public android.view.SurfaceControl.Transaction setOpaque(@NonNull android.view.SurfaceControl, boolean);
method @NonNull public android.view.SurfaceControl.Transaction setPosition(@NonNull android.view.SurfaceControl, float, float);
method @NonNull public android.view.SurfaceControl.Transaction setScale(@NonNull android.view.SurfaceControl, float, float);
@@ -54829,6 +55093,8 @@
method public abstract void setTransformation(android.graphics.Matrix);
method public abstract void setVisibility(int);
method public abstract void setWebDomain(@Nullable String);
+ field @FlaggedApi("android.service.autofill.autofill_w_metrics") public static final String EXTRA_VIRTUAL_STRUCTURE_TYPE = "android.view.ViewStructure.extra.VIRTUAL_STRUCTURE_TYPE";
+ field @FlaggedApi("android.service.autofill.autofill_w_metrics") public static final String EXTRA_VIRTUAL_STRUCTURE_VERSION_NUMBER = "android.view.ViewStructure.extra.VIRTUAL_STRUCTURE_VERSION_NUMBER";
}
public abstract static class ViewStructure.HtmlInfo {
@@ -56551,6 +56817,11 @@
public final class AutofillId implements android.os.Parcelable {
method @NonNull public static android.view.autofill.AutofillId create(@NonNull android.view.View, int);
method public int describeContents();
+ method @FlaggedApi("android.service.autofill.autofill_w_metrics") public int getAutofillVirtualId();
+ method @FlaggedApi("android.service.autofill.autofill_w_metrics") public int getSessionId();
+ method @FlaggedApi("android.service.autofill.autofill_w_metrics") public int getViewId();
+ method @FlaggedApi("android.service.autofill.autofill_w_metrics") public boolean isInAutofillSession();
+ method @FlaggedApi("android.service.autofill.autofill_w_metrics") public boolean isVirtual();
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.view.autofill.AutofillId> CREATOR;
}
@@ -56947,6 +57218,7 @@
method @NonNull public java.util.Set<java.lang.Class<? extends android.view.inputmethod.PreviewableHandwritingGesture>> getSupportedHandwritingGesturePreviews();
method @NonNull public java.util.List<java.lang.Class<? extends android.view.inputmethod.HandwritingGesture>> getSupportedHandwritingGestures();
method @FlaggedApi("android.view.inputmethod.editorinfo_handwriting_enabled") public boolean isStylusHandwritingEnabled();
+ method @FlaggedApi("android.view.inputmethod.writing_tools") public boolean isWritingToolsEnabled();
method public final void makeCompatible(int);
method @FlaggedApi("android.view.inputmethod.public_autofill_id_in_editorinfo") public void setAutofillId(@Nullable android.view.autofill.AutofillId);
method public void setInitialSurroundingSubText(@NonNull CharSequence, int);
@@ -56955,6 +57227,7 @@
method @FlaggedApi("android.view.inputmethod.editorinfo_handwriting_enabled") public void setStylusHandwritingEnabled(boolean);
method public void setSupportedHandwritingGesturePreviews(@NonNull java.util.Set<java.lang.Class<? extends android.view.inputmethod.PreviewableHandwritingGesture>>);
method public void setSupportedHandwritingGestures(@NonNull java.util.List<java.lang.Class<? extends android.view.inputmethod.HandwritingGesture>>);
+ method @FlaggedApi("android.view.inputmethod.writing_tools") public void setWritingToolsEnabled(boolean);
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.view.inputmethod.EditorInfo> CREATOR;
field public static final int IME_ACTION_DONE = 6; // 0x6
diff --git a/core/api/lint-baseline.txt b/core/api/lint-baseline.txt
index 4ada53e..ad5bd31 100644
--- a/core/api/lint-baseline.txt
+++ b/core/api/lint-baseline.txt
@@ -1,4 +1,10 @@
// Baseline format: 1.0
+ActionValue: android.view.ViewStructure#EXTRA_VIRTUAL_STRUCTURE_TYPE:
+ Inconsistent extra value; expected `android.view.extra.VIRTUAL_STRUCTURE_TYPE`, was `android.view.ViewStructure.extra.VIRTUAL_STRUCTURE_TYPE`
+ActionValue: android.view.ViewStructure#EXTRA_VIRTUAL_STRUCTURE_VERSION_NUMBER:
+ Inconsistent extra value; expected `android.view.extra.VIRTUAL_STRUCTURE_VERSION_NUMBER`, was `android.view.ViewStructure.extra.VIRTUAL_STRUCTURE_VERSION_NUMBER`
+
+
BroadcastBehavior: android.app.AlarmManager#ACTION_NEXT_ALARM_CLOCK_CHANGED:
Field 'ACTION_NEXT_ALARM_CLOCK_CHANGED' is missing @BroadcastBehavior
BroadcastBehavior: android.app.AlarmManager#ACTION_SCHEDULE_EXACT_ALARM_PERMISSION_STATE_CHANGED:
@@ -1185,6 +1191,10 @@
New API must be flagged with @FlaggedApi: field android.R.dimen.system_corner_radius_xlarge
UnflaggedApi: android.R.dimen#system_corner_radius_xsmall:
New API must be flagged with @FlaggedApi: field android.R.dimen.system_corner_radius_xsmall
+UnflaggedApi: android.R.integer#status_bar_notification_info_maxnum:
+ Changes from not deprecated to deprecated must be flagged with @FlaggedApi: field android.R.integer.status_bar_notification_info_maxnum
+UnflaggedApi: android.R.string#status_bar_notification_info_overflow:
+ Changes from not deprecated to deprecated must be flagged with @FlaggedApi: field android.R.string.status_bar_notification_info_overflow
UnflaggedApi: android.accessibilityservice.AccessibilityService#OVERLAY_RESULT_INTERNAL_ERROR:
New API must be flagged with @FlaggedApi: field android.accessibilityservice.AccessibilityService.OVERLAY_RESULT_INTERNAL_ERROR
UnflaggedApi: android.accessibilityservice.AccessibilityService#OVERLAY_RESULT_INVALID:
@@ -1477,7 +1487,6 @@
New API must be flagged with @FlaggedApi: method android.graphics.text.PositionedGlyphs.getItalicOverride(int)
UnflaggedApi: android.graphics.text.PositionedGlyphs#getWeightOverride(int):
New API must be flagged with @FlaggedApi: method android.graphics.text.PositionedGlyphs.getWeightOverride(int)
-
UnflaggedApi: android.media.MediaRoute2Info#TYPE_REMOTE_CAR:
New API must be flagged with @FlaggedApi: field android.media.MediaRoute2Info.TYPE_REMOTE_CAR
UnflaggedApi: android.media.MediaRoute2Info#TYPE_REMOTE_COMPUTER:
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index bc73220..1a949d84 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -251,6 +251,10 @@
package android.net {
+ @FlaggedApi("android.net.vcn.mainline_vcn_module_api") public final class ConnectivityFrameworkInitializerBaklava {
+ method @FlaggedApi("android.net.vcn.mainline_vcn_module_api") public static void registerServiceWrappers();
+ }
+
public class LocalSocket implements java.io.Closeable {
ctor public LocalSocket(@NonNull java.io.FileDescriptor);
}
@@ -310,6 +314,25 @@
}
+package android.net.vcn {
+
+ @FlaggedApi("android.net.vcn.mainline_vcn_module_api") public final class VcnTransportInfo implements android.os.Parcelable android.net.TransportInfo {
+ method @FlaggedApi("android.net.vcn.mainline_vcn_module_api") public int describeContents();
+ method @FlaggedApi("android.net.vcn.mainline_vcn_module_api") public long getApplicableRedactions();
+ method @FlaggedApi("android.net.vcn.mainline_vcn_module_api") public int getMinUdpPort4500NatTimeoutSeconds();
+ method @FlaggedApi("android.net.vcn.mainline_vcn_module_api") @NonNull public android.net.TransportInfo makeCopy(long);
+ method @FlaggedApi("android.net.vcn.mainline_vcn_module_api") public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @FlaggedApi("android.net.vcn.mainline_vcn_module_api") @NonNull public static final android.os.Parcelable.Creator<android.net.vcn.VcnTransportInfo> CREATOR;
+ }
+
+ @FlaggedApi("android.net.vcn.mainline_vcn_module_api") public static final class VcnTransportInfo.Builder {
+ ctor @FlaggedApi("android.net.vcn.mainline_vcn_module_api") public VcnTransportInfo.Builder();
+ method @FlaggedApi("android.net.vcn.mainline_vcn_module_api") @NonNull public android.net.vcn.VcnTransportInfo build();
+ method @FlaggedApi("android.net.vcn.mainline_vcn_module_api") @NonNull public android.net.vcn.VcnTransportInfo.Builder setMinUdpPort4500NatTimeoutSeconds(@IntRange(from=0x78) int);
+ }
+
+}
+
package android.net.wifi {
public final class WifiMigration {
@@ -379,6 +402,13 @@
field public static final int DEVICE_INITIAL_SDK_INT;
}
+ public final class Bundle extends android.os.BaseBundle implements java.lang.Cloneable android.os.Parcelable {
+ method @FlaggedApi("android.os.enable_has_binders") public int hasBinders();
+ field @FlaggedApi("android.os.enable_has_binders") public static final int STATUS_BINDERS_NOT_PRESENT = 0; // 0x0
+ field @FlaggedApi("android.os.enable_has_binders") public static final int STATUS_BINDERS_PRESENT = 1; // 0x1
+ field @FlaggedApi("android.os.enable_has_binders") public static final int STATUS_BINDERS_UNKNOWN = 2; // 0x2
+ }
+
public class Handler {
method @FlaggedApi("android.os.mainline_vcn_platform_api") public final boolean hasMessagesOrCallbacks();
method @FlaggedApi("android.os.mainline_vcn_platform_api") public final void removeCallbacksAndEqualMessages(@Nullable Object);
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index ed95fdd..a171f3d 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -57,6 +57,7 @@
field @Deprecated public static final String BIND_CONNECTION_SERVICE = "android.permission.BIND_CONNECTION_SERVICE";
field public static final String BIND_CONTENT_CAPTURE_SERVICE = "android.permission.BIND_CONTENT_CAPTURE_SERVICE";
field public static final String BIND_CONTENT_SUGGESTIONS_SERVICE = "android.permission.BIND_CONTENT_SUGGESTIONS_SERVICE";
+ field @FlaggedApi("android.content.pm.sdk_dependency_installer") public static final String BIND_DEPENDENCY_INSTALLER = "android.permission.BIND_DEPENDENCY_INSTALLER";
field public static final String BIND_DIRECTORY_SEARCH = "android.permission.BIND_DIRECTORY_SEARCH";
field public static final String BIND_DISPLAY_HASHING_SERVICE = "android.permission.BIND_DISPLAY_HASHING_SERVICE";
field @FlaggedApi("com.android.internal.telephony.flags.use_oem_domain_selection_service") public static final String BIND_DOMAIN_SELECTION_SERVICE = "android.permission.BIND_DOMAIN_SELECTION_SERVICE";
@@ -65,6 +66,7 @@
field @FlaggedApi("android.crashrecovery.flags.enable_crashrecovery") public static final String BIND_EXPLICIT_HEALTH_CHECK_SERVICE = "android.permission.BIND_EXPLICIT_HEALTH_CHECK_SERVICE";
field public static final String BIND_EXTERNAL_STORAGE_SERVICE = "android.permission.BIND_EXTERNAL_STORAGE_SERVICE";
field public static final String BIND_FIELD_CLASSIFICATION_SERVICE = "android.permission.BIND_FIELD_CLASSIFICATION_SERVICE";
+ field @FlaggedApi("android.security.afl_api") public static final String BIND_FORENSIC_EVENT_TRANSPORT_SERVICE = "android.permission.BIND_FORENSIC_EVENT_TRANSPORT_SERVICE";
field public static final String BIND_GBA_SERVICE = "android.permission.BIND_GBA_SERVICE";
field public static final String BIND_HOTWORD_DETECTION_SERVICE = "android.permission.BIND_HOTWORD_DETECTION_SERVICE";
field public static final String BIND_IMS_SERVICE = "android.permission.BIND_IMS_SERVICE";
@@ -101,6 +103,7 @@
field public static final String BRIGHTNESS_SLIDER_USAGE = "android.permission.BRIGHTNESS_SLIDER_USAGE";
field public static final String BROADCAST_CLOSE_SYSTEM_DIALOGS = "android.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS";
field @Deprecated public static final String BROADCAST_NETWORK_PRIVILEGED = "android.permission.BROADCAST_NETWORK_PRIVILEGED";
+ field @FlaggedApi("android.media.audio.concurrent_audio_record_bypass_permission") public static final String BYPASS_CONCURRENT_RECORD_AUDIO_RESTRICTION = "android.permission.BYPASS_CONCURRENT_RECORD_AUDIO_RESTRICTION";
field public static final String BYPASS_ROLE_QUALIFICATION = "android.permission.BYPASS_ROLE_QUALIFICATION";
field public static final String CALL_AUDIO_INTERCEPTION = "android.permission.CALL_AUDIO_INTERCEPTION";
field public static final String CAMERA_DISABLE_TRANSMIT_LED = "android.permission.CAMERA_DISABLE_TRANSMIT_LED";
@@ -132,6 +135,7 @@
field public static final String CONTROL_KEYGUARD_SECURE_NOTIFICATIONS = "android.permission.CONTROL_KEYGUARD_SECURE_NOTIFICATIONS";
field public static final String CONTROL_OEM_PAID_NETWORK_PREFERENCE = "android.permission.CONTROL_OEM_PAID_NETWORK_PREFERENCE";
field public static final String CONTROL_VPN = "android.permission.CONTROL_VPN";
+ field @FlaggedApi("android.app.admin.flags.split_create_managed_profile_enabled") public static final String COPY_ACCOUNTS = "android.permission.COPY_ACCOUNTS";
field public static final String CREATE_USERS = "android.permission.CREATE_USERS";
field public static final String CREATE_VIRTUAL_DEVICE = "android.permission.CREATE_VIRTUAL_DEVICE";
field public static final String CRYPT_KEEPER = "android.permission.CRYPT_KEEPER";
@@ -164,6 +168,7 @@
field public static final String HDMI_CEC = "android.permission.HDMI_CEC";
field @Deprecated public static final String HIDE_NON_SYSTEM_OVERLAY_WINDOWS = "android.permission.HIDE_NON_SYSTEM_OVERLAY_WINDOWS";
field public static final String INJECT_EVENTS = "android.permission.INJECT_EVENTS";
+ field @FlaggedApi("android.content.pm.sdk_dependency_installer") public static final String INSTALL_DEPENDENCY_SHARED_LIBRARIES = "android.permission.INSTALL_DEPENDENCY_SHARED_LIBRARIES";
field public static final String INSTALL_DPC_PACKAGES = "android.permission.INSTALL_DPC_PACKAGES";
field public static final String INSTALL_DYNAMIC_SYSTEM = "android.permission.INSTALL_DYNAMIC_SYSTEM";
field public static final String INSTALL_EXISTING_PACKAGES = "com.android.permission.INSTALL_EXISTING_PACKAGES";
@@ -209,6 +214,7 @@
field @FlaggedApi("android.permission.flags.enhanced_confirmation_mode_apis_enabled") public static final String MANAGE_ENHANCED_CONFIRMATION_STATES = "android.permission.MANAGE_ENHANCED_CONFIRMATION_STATES";
field public static final String MANAGE_ETHERNET_NETWORKS = "android.permission.MANAGE_ETHERNET_NETWORKS";
field public static final String MANAGE_FACTORY_RESET_PROTECTION = "android.permission.MANAGE_FACTORY_RESET_PROTECTION";
+ field @FlaggedApi("android.security.afl_api") public static final String MANAGE_FORENSIC_STATE = "android.permission.MANAGE_FORENSIC_STATE";
field public static final String MANAGE_GAME_ACTIVITY = "android.permission.MANAGE_GAME_ACTIVITY";
field public static final String MANAGE_GAME_MODE = "android.permission.MANAGE_GAME_MODE";
field @FlaggedApi("android.media.tv.flags.media_quality_fw") public static final String MANAGE_GLOBAL_PICTURE_QUALITY_SERVICE = "android.permission.MANAGE_GLOBAL_PICTURE_QUALITY_SERVICE";
@@ -225,6 +231,7 @@
field public static final String MANAGE_ROTATION_RESOLVER = "android.permission.MANAGE_ROTATION_RESOLVER";
field public static final String MANAGE_SAFETY_CENTER = "android.permission.MANAGE_SAFETY_CENTER";
field public static final String MANAGE_SEARCH_UI = "android.permission.MANAGE_SEARCH_UI";
+ field @FlaggedApi("android.security.secure_lockdown") public static final String MANAGE_SECURE_LOCK_DEVICE = "android.permission.MANAGE_SECURE_LOCK_DEVICE";
field public static final String MANAGE_SENSOR_PRIVACY = "android.permission.MANAGE_SENSOR_PRIVACY";
field public static final String MANAGE_SMARTSPACE = "android.permission.MANAGE_SMARTSPACE";
field public static final String MANAGE_SOUND_TRIGGER = "android.permission.MANAGE_SOUND_TRIGGER";
@@ -304,6 +311,7 @@
field public static final String READ_CONTENT_RATING_SYSTEMS = "android.permission.READ_CONTENT_RATING_SYSTEMS";
field public static final String READ_DEVICE_CONFIG = "android.permission.READ_DEVICE_CONFIG";
field public static final String READ_DREAM_STATE = "android.permission.READ_DREAM_STATE";
+ field @FlaggedApi("android.security.afl_api") public static final String READ_FORENSIC_STATE = "android.permission.READ_FORENSIC_STATE";
field public static final String READ_GLOBAL_APP_SEARCH_DATA = "android.permission.READ_GLOBAL_APP_SEARCH_DATA";
field @FlaggedApi("android.content.pm.get_resolved_apk_path") public static final String READ_INSTALLED_SESSION_PATHS = "android.permission.READ_INSTALLED_SESSION_PATHS";
field public static final String READ_INSTALL_SESSIONS = "android.permission.READ_INSTALL_SESSIONS";
@@ -340,6 +348,7 @@
field public static final String REGISTER_SIM_SUBSCRIPTION = "android.permission.REGISTER_SIM_SUBSCRIPTION";
field public static final String REGISTER_STATS_PULL_ATOM = "android.permission.REGISTER_STATS_PULL_ATOM";
field public static final String REMOTE_DISPLAY_PROVIDER = "android.permission.REMOTE_DISPLAY_PROVIDER";
+ field @FlaggedApi("android.app.admin.flags.split_create_managed_profile_enabled") public static final String REMOVE_ACCOUNTS = "android.permission.REMOVE_ACCOUNTS";
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";
@@ -528,6 +537,7 @@
field public static final int config_systemCallStreaming = 17039431; // 0x1040047
field public static final int config_systemCompanionDeviceProvider = 17039417; // 0x1040039
field public static final int config_systemContacts = 17039403; // 0x104002b
+ field @FlaggedApi("android.content.pm.sdk_dependency_installer") public static final int config_systemDependencyInstaller;
field public static final int config_systemFinancedDeviceController = 17039430; // 0x1040046
field public static final int config_systemGallery = 17039399; // 0x1040027
field public static final int config_systemNotificationIntelligence = 17039413; // 0x1040035
@@ -564,6 +574,7 @@
package android.accounts {
public class AccountManager {
+ method @FlaggedApi("android.app.admin.flags.split_create_managed_profile_enabled") @NonNull @RequiresPermission(anyOf={android.Manifest.permission.COPY_ACCOUNTS, android.Manifest.permission.INTERACT_ACROSS_USERS_FULL}) public android.accounts.AccountManagerFuture<java.lang.Boolean> copyAccountToUser(@NonNull android.accounts.Account, @NonNull android.os.UserHandle, @NonNull android.os.UserHandle, @Nullable android.accounts.AccountManagerCallback<java.lang.Boolean>, @Nullable android.os.Handler);
method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public android.accounts.AccountManagerFuture<android.os.Bundle> finishSessionAsUser(android.os.Bundle, android.app.Activity, android.os.UserHandle, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler);
}
@@ -1336,8 +1347,10 @@
public class DevicePolicyManager {
method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public int checkProvisioningPrecondition(@NonNull String, @NonNull String);
method @RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_POLICY_AUDIT_LOGGING) public void clearAuditLogEventCallback();
- method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public android.os.UserHandle createAndProvisionManagedProfile(@NonNull android.app.admin.ManagedProfileProvisioningParams) throws android.app.admin.ProvisioningException;
+ method @Deprecated @FlaggedApi("android.app.admin.flags.split_create_managed_profile_enabled") @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public android.os.UserHandle createAndProvisionManagedProfile(@NonNull android.app.admin.ManagedProfileProvisioningParams) throws android.app.admin.ProvisioningException;
+ method @FlaggedApi("android.app.admin.flags.split_create_managed_profile_enabled") @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public android.os.UserHandle createManagedProfile(@NonNull android.app.admin.ManagedProfileProvisioningParams) throws android.app.admin.ProvisioningException;
method @Nullable public android.content.Intent createProvisioningIntentFromNfcIntent(@NonNull android.content.Intent);
+ method @FlaggedApi("android.app.admin.flags.split_create_managed_profile_enabled") @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public void finalizeCreateManagedProfile(@NonNull android.app.admin.ManagedProfileProvisioningParams, @NonNull android.os.UserHandle) throws android.app.admin.ProvisioningException;
method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public void finalizeWorkProfileProvisioning(@NonNull android.os.UserHandle, @Nullable android.accounts.Account);
method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_POLICY_APP_EXEMPTIONS) public java.util.Set<java.lang.Integer> getApplicationExemptions(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public boolean getBluetoothContactSharingDisabled(@NonNull android.os.UserHandle);
@@ -1932,6 +1945,7 @@
method public android.os.IBinder getBinder();
method public long getCurrentRestoreSet();
method public int getNextFullRestoreDataChunk(android.os.ParcelFileDescriptor);
+ method @FlaggedApi("com.android.server.backup.enable_restricted_mode_changes") @NonNull public java.util.List<java.lang.String> getPackagesThatShouldNotUseRestrictedMode(@NonNull java.util.List<java.lang.String>, int);
method public int getRestoreData(android.os.ParcelFileDescriptor);
method public int getTransportFlags();
method public int initializeDevice();
@@ -3860,6 +3874,7 @@
field public static final String APP_INTEGRITY_SERVICE = "app_integrity";
field public static final String APP_PREDICTION_SERVICE = "app_prediction";
field public static final String AUDIO_DEVICE_VOLUME_SERVICE = "audio_device_volume";
+ field @FlaggedApi("android.security.secure_lockdown") public static final String AUTHENTICATION_POLICY_SERVICE = "authentication_policy";
field public static final String BACKUP_SERVICE = "backup";
field public static final String BATTERY_STATS_SERVICE = "batterystats";
field public static final int BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS = 1048576; // 0x100000
@@ -4585,6 +4600,24 @@
}
+package android.content.pm.dependencyinstaller {
+
+ @FlaggedApi("android.content.pm.sdk_dependency_installer") public final class DependencyInstallerCallback implements android.os.Parcelable {
+ method public int describeContents();
+ method public void onAllDependenciesResolved(@NonNull int[]);
+ method public void onFailureToResolveAllDependencies();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.dependencyinstaller.DependencyInstallerCallback> CREATOR;
+ }
+
+ @FlaggedApi("android.content.pm.sdk_dependency_installer") public abstract class DependencyInstallerService extends android.app.Service {
+ ctor public DependencyInstallerService();
+ method @NonNull public final android.os.IBinder onBind(@Nullable android.content.Intent);
+ method public abstract void onDependenciesRequired(@NonNull java.util.List<android.content.pm.SharedLibraryInfo>, @NonNull android.content.pm.dependencyinstaller.DependencyInstallerCallback);
+ }
+
+}
+
package android.content.pm.dex {
public class ArtManager {
@@ -5045,15 +5078,27 @@
package android.hardware.camera2 {
+ public final class CameraCharacteristics extends android.hardware.camera2.CameraMetadata<android.hardware.camera2.CameraCharacteristics.Key<?>> {
+ field @FlaggedApi("com.android.internal.camera.flags.camera_multi_client") @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.SharedSessionConfiguration> SHARED_SESSION_CONFIGURATION;
+ }
+
public abstract class CameraDevice implements java.lang.AutoCloseable {
method @Deprecated public abstract void createCustomCaptureSession(android.hardware.camera2.params.InputConfiguration, @NonNull java.util.List<android.hardware.camera2.params.OutputConfiguration>, int, @NonNull android.hardware.camera2.CameraCaptureSession.StateCallback, @Nullable android.os.Handler) throws android.hardware.camera2.CameraAccessException;
field public static final int SESSION_OPERATION_MODE_CONSTRAINED_HIGH_SPEED = 1; // 0x1
field public static final int SESSION_OPERATION_MODE_NORMAL = 0; // 0x0
+ field @FlaggedApi("com.android.internal.camera.flags.camera_multi_client") public static final int SESSION_OPERATION_MODE_SHARED = 2; // 0x2
field public static final int SESSION_OPERATION_MODE_VENDOR_START = 32768; // 0x8000
}
+ public abstract static class CameraDevice.StateCallback {
+ method @FlaggedApi("com.android.internal.camera.flags.camera_multi_client") public void onClientSharedAccessPriorityChanged(@NonNull android.hardware.camera2.CameraDevice, boolean);
+ method @FlaggedApi("com.android.internal.camera.flags.camera_multi_client") public void onOpenedInSharedMode(@NonNull android.hardware.camera2.CameraDevice, boolean);
+ }
+
public final class CameraManager {
+ method @FlaggedApi("com.android.internal.camera.flags.camera_multi_client") public boolean isCameraDeviceSharingSupported(@NonNull String) throws android.hardware.camera2.CameraAccessException;
method @RequiresPermission(allOf={android.Manifest.permission.SYSTEM_CAMERA, android.Manifest.permission.CAMERA}) public void openCamera(@NonNull String, int, @NonNull java.util.concurrent.Executor, @NonNull android.hardware.camera2.CameraDevice.StateCallback) throws android.hardware.camera2.CameraAccessException;
+ method @FlaggedApi("com.android.internal.camera.flags.camera_multi_client") @RequiresPermission(allOf={android.Manifest.permission.SYSTEM_CAMERA, android.Manifest.permission.CAMERA}) public void openSharedCamera(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.hardware.camera2.CameraDevice.StateCallback) throws android.hardware.camera2.CameraAccessException;
}
public abstract static class CameraManager.AvailabilityCallback {
@@ -5061,6 +5106,12 @@
method @RequiresPermission(android.Manifest.permission.CAMERA_OPEN_CLOSE_LISTENER) public void onCameraOpened(@NonNull String, @NonNull String);
}
+ @FlaggedApi("com.android.internal.camera.flags.camera_multi_client") public abstract class CameraSharedCaptureSession extends android.hardware.camera2.CameraCaptureSession {
+ ctor public CameraSharedCaptureSession();
+ method @FlaggedApi("com.android.internal.camera.flags.camera_multi_client") public abstract int startStreaming(@NonNull java.util.List<android.view.Surface>, @NonNull java.util.concurrent.Executor, @NonNull android.hardware.camera2.CameraCaptureSession.CaptureCallback) throws android.hardware.camera2.CameraAccessException;
+ method @FlaggedApi("com.android.internal.camera.flags.camera_multi_client") public abstract void stopStreaming() throws android.hardware.camera2.CameraAccessException;
+ }
+
}
package android.hardware.camera2.extension {
@@ -5169,6 +5220,142 @@
field public static final int ROTATION_90 = 1; // 0x1
}
+ public final class SessionConfiguration implements android.os.Parcelable {
+ field @FlaggedApi("com.android.internal.camera.flags.camera_multi_client") public static final int SESSION_SHARED = 2; // 0x2
+ }
+
+ @FlaggedApi("com.android.internal.camera.flags.camera_multi_client") public final class SharedSessionConfiguration {
+ method @Nullable public android.graphics.ColorSpace getColorSpace();
+ method @NonNull public java.util.List<android.hardware.camera2.params.SharedSessionConfiguration.SharedOutputConfiguration> getOutputStreamsInformation();
+ }
+
+ public static final class SharedSessionConfiguration.SharedOutputConfiguration {
+ method public int getDataspace();
+ method public int getFormat();
+ method public int getMirrorMode();
+ method @Nullable public String getPhysicalCameraId();
+ method @NonNull public android.util.Size getSize();
+ method public long getStreamUseCase();
+ method public int getSurfaceType();
+ method public int getTimestampBase();
+ method public long getUsage();
+ method public boolean isReadoutTimestampEnabled();
+ }
+
+}
+
+package android.hardware.contexthub {
+
+ @FlaggedApi("android.chre.flags.offload_api") public class HubDiscoveryInfo {
+ method @NonNull public android.hardware.contexthub.HubEndpointInfo getHubEndpointInfo();
+ method @Nullable public android.hardware.contexthub.HubServiceInfo getHubServiceInfo();
+ }
+
+ @FlaggedApi("android.chre.flags.offload_api") public class HubEndpoint {
+ method @Nullable public android.hardware.contexthub.IHubEndpointLifecycleCallback getLifecycleCallback();
+ method @Nullable public android.hardware.contexthub.IHubEndpointMessageCallback getMessageCallback();
+ method @NonNull public java.util.Collection<android.hardware.contexthub.HubServiceInfo> getServiceInfoCollection();
+ method @Nullable public String getTag();
+ method public int getVersion();
+ }
+
+ public static final class HubEndpoint.Builder {
+ ctor public HubEndpoint.Builder(@NonNull android.content.Context);
+ method @NonNull public android.hardware.contexthub.HubEndpoint build();
+ method @NonNull public android.hardware.contexthub.HubEndpoint.Builder setLifecycleCallback(@NonNull android.hardware.contexthub.IHubEndpointLifecycleCallback);
+ method @NonNull public android.hardware.contexthub.HubEndpoint.Builder setLifecycleCallback(@NonNull java.util.concurrent.Executor, @NonNull android.hardware.contexthub.IHubEndpointLifecycleCallback);
+ method @NonNull public android.hardware.contexthub.HubEndpoint.Builder setMessageCallback(@NonNull android.hardware.contexthub.IHubEndpointMessageCallback);
+ method @NonNull public android.hardware.contexthub.HubEndpoint.Builder setMessageCallback(@NonNull java.util.concurrent.Executor, @NonNull android.hardware.contexthub.IHubEndpointMessageCallback);
+ method @NonNull public android.hardware.contexthub.HubEndpoint.Builder setServiceInfoCollection(@NonNull java.util.Collection<android.hardware.contexthub.HubServiceInfo>);
+ method @NonNull public android.hardware.contexthub.HubEndpoint.Builder setTag(@NonNull String);
+ }
+
+ @FlaggedApi("android.chre.flags.offload_api") public final class HubEndpointInfo implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public android.hardware.contexthub.HubEndpointInfo.HubEndpointIdentifier getIdentifier();
+ method @NonNull public String getName();
+ method @NonNull public java.util.Collection<java.lang.String> getRequiredPermissions();
+ method @NonNull public java.util.Collection<android.hardware.contexthub.HubServiceInfo> getServiceInfoCollection();
+ method @Nullable public String getTag();
+ method public int getType();
+ method public int getVersion();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.hardware.contexthub.HubEndpointInfo> CREATOR;
+ field public static final int TYPE_APP = 2; // 0x2
+ field public static final int TYPE_FRAMEWORK = 1; // 0x1
+ field public static final int TYPE_HUB_ENDPOINT = 5; // 0x5
+ field public static final int TYPE_NANOAPP = 4; // 0x4
+ field public static final int TYPE_NATIVE = 3; // 0x3
+ }
+
+ public static class HubEndpointInfo.HubEndpointIdentifier {
+ method public long getEndpoint();
+ method public long getHub();
+ }
+
+ @FlaggedApi("android.chre.flags.offload_api") public class HubEndpointSession implements java.lang.AutoCloseable {
+ method public void close();
+ method @Nullable public android.hardware.contexthub.HubServiceInfo getServiceInfo();
+ method @NonNull public android.hardware.location.ContextHubTransaction<java.lang.Void> sendMessage(@NonNull android.hardware.contexthub.HubMessage);
+ }
+
+ @FlaggedApi("android.chre.flags.offload_api") public class HubEndpointSessionResult {
+ method @NonNull public static android.hardware.contexthub.HubEndpointSessionResult accept();
+ method @Nullable public String getReason();
+ method public boolean isAccepted();
+ method @NonNull public static android.hardware.contexthub.HubEndpointSessionResult reject(@NonNull String);
+ }
+
+ @FlaggedApi("android.chre.flags.offload_api") public final class HubMessage implements android.os.Parcelable {
+ method @NonNull public static android.hardware.contexthub.HubMessage createMessage(int, @NonNull byte[]);
+ method @NonNull public static android.hardware.contexthub.HubMessage createMessage(int, @NonNull byte[], @NonNull android.hardware.contexthub.HubMessage.DeliveryParams);
+ method public int describeContents();
+ method @NonNull public byte[] getMessageBody();
+ method public int getMessageType();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.hardware.contexthub.HubMessage> CREATOR;
+ }
+
+ public static class HubMessage.DeliveryParams {
+ method public boolean isResponseRequired();
+ method @NonNull public static android.hardware.contexthub.HubMessage.DeliveryParams makeBasic();
+ method @NonNull public android.hardware.contexthub.HubMessage.DeliveryParams setResponseRequired(boolean);
+ }
+
+ @FlaggedApi("android.chre.flags.offload_api") public final class HubServiceInfo implements android.os.Parcelable {
+ ctor public HubServiceInfo(@NonNull String, int, int, int, @NonNull android.os.ParcelableHolder);
+ method public int describeContents();
+ method @NonNull public android.os.ParcelableHolder getExtendedInfo();
+ method public int getFormat();
+ method public int getMajorVersion();
+ method public int getMinorVersion();
+ method @NonNull public String getServiceDescriptor();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.hardware.contexthub.HubServiceInfo> CREATOR;
+ field public static final int FORMAT_AIDL = 1; // 0x1
+ field public static final int FORMAT_CUSTOM = 0; // 0x0
+ field public static final int FORMAT_PW_RPC_PROTOBUF = 2; // 0x2
+ }
+
+ public static final class HubServiceInfo.Builder {
+ ctor public HubServiceInfo.Builder(@NonNull String, int, int, int);
+ method @NonNull public android.hardware.contexthub.HubServiceInfo build();
+ method @NonNull public android.hardware.contexthub.HubServiceInfo.Builder setExtendedInfo(@Nullable android.os.Parcelable);
+ }
+
+ @FlaggedApi("android.chre.flags.offload_api") public interface IHubEndpointLifecycleCallback {
+ method public void onSessionClosed(@NonNull android.hardware.contexthub.HubEndpointSession, int);
+ method @NonNull public android.hardware.contexthub.HubEndpointSessionResult onSessionOpenRequest(@NonNull android.hardware.contexthub.HubEndpointInfo, @Nullable android.hardware.contexthub.HubServiceInfo);
+ method public void onSessionOpened(@NonNull android.hardware.contexthub.HubEndpointSession);
+ field public static final int REASON_CLOSE_ENDPOINT_SESSION_REQUESTED = 4; // 0x4
+ field public static final int REASON_OPEN_ENDPOINT_SESSION_REQUEST_REJECTED = 3; // 0x3
+ field public static final int REASON_UNSPECIFIED = 0; // 0x0
+ }
+
+ @FlaggedApi("android.chre.flags.offload_api") public interface IHubEndpointMessageCallback {
+ method public void onMessageReceived(@NonNull android.hardware.contexthub.HubEndpointSession, @NonNull android.hardware.contexthub.HubMessage);
+ }
+
}
package android.hardware.devicestate {
@@ -5301,6 +5488,7 @@
method @Nullable @RequiresPermission(android.Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS) public android.hardware.display.BrightnessConfiguration getDefaultBrightnessConfiguration();
method public android.util.Pair<float[],float[]> getMinimumBrightnessCurve();
method public android.graphics.Point getStableDisplaySize();
+ method @FlaggedApi("com.android.server.display.feature.flags.is_always_on_available_api") public boolean isAlwaysOnDisplayCurrentlyAvailable();
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);
@@ -6162,6 +6350,8 @@
method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public android.hardware.location.ContextHubClient createClient(@NonNull android.hardware.location.ContextHubInfo, @NonNull android.app.PendingIntent, long);
method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public android.hardware.location.ContextHubTransaction<java.lang.Void> disableNanoApp(@NonNull android.hardware.location.ContextHubInfo, long);
method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public android.hardware.location.ContextHubTransaction<java.lang.Void> enableNanoApp(@NonNull android.hardware.location.ContextHubInfo, long);
+ method @FlaggedApi("android.chre.flags.offload_api") @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public java.util.List<android.hardware.contexthub.HubDiscoveryInfo> findEndpoints(long);
+ method @FlaggedApi("android.chre.flags.offload_api") @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public java.util.List<android.hardware.contexthub.HubDiscoveryInfo> findEndpoints(@NonNull String);
method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public int[] findNanoAppOnHub(int, @NonNull android.hardware.location.NanoAppFilter);
method @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public int[] getContextHubHandles();
method @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public android.hardware.location.ContextHubInfo getContextHubInfo(int);
@@ -6169,13 +6359,17 @@
method @Deprecated @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public android.hardware.location.NanoAppInstanceInfo getNanoAppInstanceInfo(int);
method @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public int loadNanoApp(int, @NonNull android.hardware.location.NanoApp);
method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public android.hardware.location.ContextHubTransaction<java.lang.Void> loadNanoApp(@NonNull android.hardware.location.ContextHubInfo, @NonNull android.hardware.location.NanoAppBinary);
+ method @FlaggedApi("android.chre.flags.offload_api") @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public void openSession(@NonNull android.hardware.contexthub.HubEndpoint, @NonNull android.hardware.contexthub.HubEndpointInfo);
+ method @FlaggedApi("android.chre.flags.offload_api") @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public void openSession(@NonNull android.hardware.contexthub.HubEndpoint, @NonNull android.hardware.contexthub.HubEndpointInfo, @NonNull android.hardware.contexthub.HubServiceInfo);
method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public android.hardware.location.ContextHubTransaction<java.util.List<android.hardware.location.NanoAppState>> queryNanoApps(@NonNull android.hardware.location.ContextHubInfo);
method @Deprecated public int registerCallback(@NonNull android.hardware.location.ContextHubManager.Callback);
method @Deprecated public int registerCallback(android.hardware.location.ContextHubManager.Callback, android.os.Handler);
+ method @FlaggedApi("android.chre.flags.offload_api") @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public void registerEndpoint(@NonNull android.hardware.contexthub.HubEndpoint);
method @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public int sendMessage(int, int, @NonNull android.hardware.location.ContextHubMessage);
method @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public int unloadNanoApp(int);
method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public android.hardware.location.ContextHubTransaction<java.lang.Void> unloadNanoApp(@NonNull android.hardware.location.ContextHubInfo, long);
method @Deprecated public int unregisterCallback(@NonNull android.hardware.location.ContextHubManager.Callback);
+ method @FlaggedApi("android.chre.flags.offload_api") @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public void unregisterEndpoint(@NonNull android.hardware.contexthub.HubEndpoint);
field public static final int AUTHORIZATION_DENIED = 0; // 0x0
field public static final int AUTHORIZATION_DENIED_GRACE_PERIOD = 1; // 0x1
field public static final int AUTHORIZATION_GRANTED = 2; // 0x2
@@ -6231,6 +6425,8 @@
field public static final int RESULT_SUCCESS = 0; // 0x0
field public static final int TYPE_DISABLE_NANOAPP = 3; // 0x3
field public static final int TYPE_ENABLE_NANOAPP = 2; // 0x2
+ field @FlaggedApi("android.chre.flags.offload_api") public static final int TYPE_HUB_MESSAGE_DEFAULT = 6; // 0x6
+ field @FlaggedApi("android.chre.flags.offload_api") public static final int TYPE_HUB_MESSAGE_REQUIRES_RESPONSE = 7; // 0x7
field public static final int TYPE_LOAD_NANOAPP = 0; // 0x0
field public static final int TYPE_QUERY_NANOAPPS = 4; // 0x4
field public static final int TYPE_RELIABLE_MESSAGE = 5; // 0x5
@@ -7464,6 +7660,7 @@
}
public final class AudioPlaybackConfiguration implements android.os.Parcelable {
+ method @FlaggedApi("android.media.audio.routed_device_ids") @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public java.util.List<android.media.AudioDeviceInfo> getAudioDeviceInfos();
method public int getChannelMask();
method public int getClientPid();
method public int getClientUid();
@@ -7618,7 +7815,7 @@
public final class MediaCas implements java.lang.AutoCloseable {
method @FlaggedApi("android.media.tv.flags.set_resource_holder_retain") @RequiresPermission("android.permission.TUNER_RESOURCE_ACCESS") public void setResourceHolderRetain(boolean);
- method @FlaggedApi("com.android.media.flags.update_client_profile_priority") @RequiresPermission("android.permission.TUNER_RESOURCE_ACCESS") public boolean updateResourcePriority(int, int);
+ method @FlaggedApi("android.media.tv.flags.mediacas_update_client_profile_priority") @RequiresPermission("android.permission.TUNER_RESOURCE_ACCESS") public boolean updateResourcePriority(int, int);
}
public final class MediaCodec {
@@ -7957,6 +8154,25 @@
}
+package android.media.quality {
+
+ @FlaggedApi("android.media.tv.flags.media_quality_fw") public final class MediaQualityManager {
+ method @NonNull public java.util.List<java.lang.String> getPictureProfileAllowList();
+ method @NonNull public java.util.List<java.lang.String> getPictureProfilePackageNames();
+ method @NonNull public java.util.List<android.media.quality.PictureProfile> getPictureProfilesByPackage(@NonNull String);
+ method public void setAutoPictureQualityEnabled(boolean);
+ method public void setPictureProfileAllowList(@NonNull java.util.List<java.lang.String>);
+ method public void setSuperResolutionEnabled(boolean);
+ }
+
+ public static final class PictureProfile.Builder {
+ method @NonNull public android.media.quality.PictureProfile.Builder setInputId(@NonNull String);
+ method @NonNull public android.media.quality.PictureProfile.Builder setPackageName(@NonNull String);
+ method @NonNull public android.media.quality.PictureProfile.Builder setProfileType(int);
+ }
+
+}
+
package android.media.session {
public final class MediaSessionManager {
@@ -8249,18 +8465,96 @@
@FlaggedApi("android.media.tv.flags.tif_extension_standardization") public final class TvInputServiceExtensionManager {
method @RequiresPermission(android.Manifest.permission.TV_INPUT_HARDWARE) public int registerExtensionIBinder(@NonNull String, @NonNull android.os.IBinder);
- field public static final String IBROADCAST_TIME = "android.media.tv.extension.time.BroadcastTime";
+ field public static final String IANALOG_ATTRIBUTE_INTERFACE = "android.media.tv.extension.analog.IAnalogAttributeInterface";
+ field public static final String IANALOG_AUDIO_INFO = "android.media.tv.extension.signal.IAnalogAudioInfo";
+ field public static final String IAUDIO_SIGNAL_INFO = "android.media.tv.extension.signal.IAudioSignalInfo";
+ field public static final String IAUDIO_SIGNAL_INFO_LISTENER = "android.media.tv.extension.signal.IAudioSignalInfoListener";
+ field public static final String IBROADCAST_TIME = "android.media.tv.extension.time.IBroadcastTime";
+ field public static final String ICAM_APP_INFO_LISTENER = "android.media.tv.extension.cam.ICamAppInfoListener";
field public static final String ICAM_APP_INFO_SERVICE = "android.media.tv.extension.cam.ICamAppInfoService";
+ field public static final String ICAM_DRM_INFO_LISTENER = "android.media.tv.extension.cam.ICamDrmInfoListener";
+ field public static final String ICAM_HOST_CONTROL_ASK_RELEASE_REPLY_CALLBACK = "android.media.tv.extension.cam.ICamHostControlAskReleaseReplyCallback";
+ field public static final String ICAM_HOST_CONTROL_INFO_LISTENER = "android.media.tv.extension.cam.ICamHostControlInfoListener";
+ field public static final String ICAM_HOST_CONTROL_SERVICE = "android.media.tv.extension.cam.ICamHostControlService";
+ field public static final String ICAM_HOST_CONTROL_TUNE_QUIETLY_FLAG = "android.media.tv.extension.cam.ICamHostControlTuneQuietlyFlag";
+ field public static final String ICAM_HOST_CONTROL_TUNE_QUIETLY_FLAG_LISTENER = "android.media.tv.extension.cam.ICamHostControlTuneQuietlyFlagListener";
+ field public static final String ICAM_INFO_LISTENER = "android.media.tv.extension.cam.ICamInfoListener";
+ field public static final String ICAM_MONITORING_SERVICE = "android.media.tv.extension.cam.ICamMonitoringService";
+ field public static final String ICAM_PIN_CAPABILITY_LISTENER = "android.media.tv.extension.cam.ICamPinCapabilityListener";
+ field public static final String ICAM_PIN_SERVICE = "android.media.tv.extension.cam.ICamPinService";
+ field public static final String ICAM_PIN_STATUS_LISTENER = "android.media.tv.extension.cam.ICamPinStatusListener";
+ field public static final String ICAM_PROFILE_INTERFACE = "android.media.tv.extension.cam.ICamProfileInterface";
+ field public static final String ICHANNEL_LIST_TRANSFER = "android.media.tv.extension.servicedb.IChannelListTransfer";
+ field public static final String ICHANNEL_TUNED_INTERFACE = "android.media.tv.extension.tune.IChannelTunedInterface";
+ field public static final String ICHANNEL_TUNED_LISTENER = "android.media.tv.extension.tune.IChannelTunedListener";
+ field public static final String ICI_OPERATOR_INTERFACE = "android.media.tv.extension.cam.ICiOperatorInterface";
+ field public static final String ICI_OPERATOR_LISTENER = "android.media.tv.extension.cam.ICiOperatorListener";
field public static final String ICLIENT_TOKEN = "android.media.tv.extension.clienttoken.IClientToken";
+ field public static final String ICONTENT_CONTROL_SERVICE = "android.media.tv.extension.cam.IContentControlService";
field public static final String IDATA_SERVICE_SIGNAL_INFO = "android.media.tv.extension.teletext.IDataServiceSignalInfo";
+ field public static final String IDATA_SERVICE_SIGNAL_INFO_LISTENER = "android.media.tv.extension.teletext.IDataServiceSignalInfoListener";
+ field public static final String IDELETE_RECORDED_CONTENTS_CALLBACK = "android.media.tv.extension.pvr.IDeleteRecordedContentsCallback";
+ field public static final String IDOWNLOADABLE_RATING_TABLE_MONITOR = "android.media.tv.extension.rating.IDownloadableRatingTableMonitor";
+ field public static final String IENTER_MENU_ERROR_CALLBACK = "android.media.tv.extension.cam.IEnterMenuErrorCallback";
+ field public static final String IEVENT_DOWNLOAD = "android.media.tv.extension.event.IEventDownload";
+ field public static final String IEVENT_DOWNLOAD_LISTENER = "android.media.tv.extension.event.IEventDownloadListener";
+ field public static final String IEVENT_DOWNLOAD_SESSION = "android.media.tv.extension.event.IEventDownloadSession";
field public static final String IEVENT_MONITOR = "android.media.tv.extension.event.IEventMonitor";
+ field public static final String IEVENT_MONITOR_LISTENER = "android.media.tv.extension.event.IEventMonitorListener";
+ field public static final String IFAVORITE_NETWORK = "android.media.tv.extension.scan.IFavoriteNetwork";
+ field public static final String IFAVORITE_NETWORK_LISTENER = "android.media.tv.extension.scan.IFavoriteNetworkListener";
+ field public static final String IGET_INFO_RECORDED_CONTENTS_CALLBACK = "android.media.tv.extension.pvr.IGetInfoRecordedContentsCallback";
+ field public static final String IHDMI_SIGNAL_INFO_LISTENER = "android.media.tv.extension.signal.IHdmiSignalInfoListener";
field public static final String IHDMI_SIGNAL_INTERFACE = "android.media.tv.extension.signal.IHdmiSignalInterface";
+ field public static final String IHDPLUS_INFO = "android.media.tv.extension.scan.IHDPlusInfo";
+ field public static final String ILCNV2_CHANNEL_LIST = "android.media.tv.extension.scan.ILcnV2ChannelList";
+ field public static final String ILCNV2_CHANNEL_LIST_LISTENER = "android.media.tv.extension.scan.ILcnV2ChannelListListener";
+ field public static final String ILCN_CONFLICT = "android.media.tv.extension.scan.ILcnConflict";
+ field public static final String ILCN_CONFLICT_LISTENER = "android.media.tv.extension.scan.ILcnConflictListener";
+ field public static final String IMMI_INTERFACE = "android.media.tv.extension.cam.IMmiInterface";
+ field public static final String IMMI_SESSION = "android.media.tv.extension.cam.IMmiSession";
+ field public static final String IMMI_STATUS_CALLBACK = "android.media.tv.extension.cam.IMmiStatusCallback";
+ field public static final String IMUX_TUNE = "android.media.tv.extension.tune.IMuxTune";
+ field public static final String IMUX_TUNE_SESSION = "android.media.tv.extension.tune.IMuxTuneSession";
field public static final String IOAD_UPDATE_INTERFACE = "android.media.tv.extension.oad.IOadUpdateInterface";
+ field public static final String IOPERATOR_DETECTION = "android.media.tv.extension.scan.IOperatorDetection";
+ field public static final String IOPERATOR_DETECTION_LISTENER = "android.media.tv.extension.scan.IOperatorDetectionListener";
+ field public static final String IPMT_RATING_INTERFACE = "android.media.tv.extension.rating.IPmtRatingInterface";
+ field public static final String IPMT_RATING_LISTENER = "android.media.tv.extension.rating.IPmtRatingListener";
+ field public static final String IPROGRAM_INFO = "android.media.tv.extension.rating.IProgramInfo";
+ field public static final String IPROGRAM_INFO_LISTENER = "android.media.tv.extension.rating.IProgramInfoListener";
field public static final String IRATING_INTERFACE = "android.media.tv.extension.rating.IRatingInterface";
field public static final String IRECORDED_CONTENTS = "android.media.tv.extension.pvr.IRecordedContents";
+ field public static final String IREGION_CHANNEL_LIST = "android.media.tv.extension.scan.IRegionChannelList";
+ field public static final String IREGION_CHANNEL_LIST_LISTENER = "android.media.tv.extension.scan.IRegionChannelListListener";
+ field public static final String ISCAN_BACKGROUND_SERVICE_UPDATE = "android.media.tv.extension.scanbsu.IScanBackgroundServiceUpdate";
+ field public static final String ISCAN_BACKGROUND_SERVICE_UPDATE_LISTENER = "android.media.tv.extension.scanbsu.IScanBackgroundServiceUpdateListener";
field public static final String ISCAN_INTERFACE = "android.media.tv.extension.scan.IScanInterface";
+ field public static final String ISCAN_LISTENER = "android.media.tv.extension.scan.IScanListener";
+ field public static final String ISCAN_SAT_SEARCH = "android.media.tv.extension.scan.IScanSatSearch";
+ field public static final String ISCAN_SESSION = "android.media.tv.extension.scan.IScanSession";
field public static final String ISCREEN_MODE_SETTINGS = "android.media.tv.extension.screenmode.IScreenModeSettings";
+ field public static final String ISERVICE_LIST = "android.media.tv.extension.servicedb.IServiceList";
+ field public static final String ISERVICE_LIST_EDIT = "android.media.tv.extension.servicedb.IServiceListEdit";
field public static final String ISERVICE_LIST_EDIT_LISTENER = "android.media.tv.extension.servicedb.IServiceListEditListener";
+ field public static final String ISERVICE_LIST_EXPORT_LISTENER = "android.media.tv.extension.servicedb.IServiceListExportListener";
+ field public static final String ISERVICE_LIST_EXPORT_SESSION = "android.media.tv.extension.servicedb.IServiceListExportSession";
+ field public static final String ISERVICE_LIST_IMPORT_LISTENER = "android.media.tv.extension.servicedb.IServiceListImportListener";
+ field public static final String ISERVICE_LIST_IMPORT_SESSION = "android.media.tv.extension.servicedb.IServiceListImportSession";
+ field public static final String ISERVICE_LIST_SET_CHANNEL_LIST_LISTENER = "android.media.tv.extension.servicedb.IServiceListSetChannelListListener";
+ field public static final String ISERVICE_LIST_SET_CHANNEL_LIST_SESSION = "android.media.tv.extension.servicedb.IServiceListSetChannelListSession";
+ field public static final String ISERVICE_LIST_TRANSFER_INTERFACE = "android.media.tv.extension.servicedb.IServiceListTransferInterface";
+ field public static final String ITARGET_REGION = "android.media.tv.extension.scan.ITargetRegion";
+ field public static final String ITARGET_REGION_LISTENER = "android.media.tv.extension.scan.ITargetRegionListener";
+ field public static final String ITELETEXT_PAGE_SUB_CODE = "android.media.tv.extension.teletext.ITeletextPageSubCode";
+ field public static final String ITKGS_INFO = "android.media.tv.extension.scan.ITkgsInfo";
+ field public static final String ITKGS_INFO_LISTENER = "android.media.tv.extension.scan.ITkgsInfoListener";
+ field public static final String ITUNER_FRONTEND_SIGNAL_INFO_INTERFACE = "android.media.tv.extension.signal.ITunerFrontendSignalInfoInterface";
+ field public static final String ITUNER_FRONTEND_SIGNAL_INFO_LISTENER = "android.media.tv.extension.signal.ITunerFrontendSignalInfoListener";
+ field public static final String IVBI_RATING_INTERFACE = "android.media.tv.extension.rating.IVbiRatingInterface";
+ field public static final String IVBI_RATING_LISTENER = "android.media.tv.extension.rating.IVbiRatingListener";
+ field public static final String IVIDEO_SIGNAL_INFO = "android.media.tv.extension.signal.IVideoSignalInfo";
+ field public static final String IVIDEO_SIGNAL_INFO_LISTENER = "android.media.tv.extension.signal.IVideoSignalInfoListener";
field public static final int REGISTER_FAIL_IMPLEMENTATION_NOT_STANDARDIZED = 2; // 0x2
field public static final int REGISTER_FAIL_NAME_NOT_STANDARDIZED = 1; // 0x1
field public static final int REGISTER_FAIL_REMOTE_EXCEPTION = 3; // 0x3
@@ -9605,7 +9899,7 @@
method public int getSignalStrength();
method public int getSnr();
method public int getSpectralInversion();
- method @FlaggedApi("android.media.tv.flags.tuner_w_apis") @NonNull public android.media.tv.tuner.frontend.StandardExt getStandardExt();
+ method @FlaggedApi("android.media.tv.flags.tuner_w_apis") @NonNull public android.media.tv.tuner.frontend.StandardExtension getStandardExtension();
method @NonNull public int[] getStreamIds();
method public int getSymbolRate();
method @IntRange(from=0, to=65535) public int getSystemId();
@@ -9660,7 +9954,7 @@
field public static final int FRONTEND_STATUS_TYPE_SIGNAL_STRENGTH = 6; // 0x6
field public static final int FRONTEND_STATUS_TYPE_SNR = 1; // 0x1
field public static final int FRONTEND_STATUS_TYPE_SPECTRAL = 10; // 0xa
- field @FlaggedApi("android.media.tv.flags.tuner_w_apis") public static final int FRONTEND_STATUS_TYPE_STANDARD_EXT = 47; // 0x2f
+ field @FlaggedApi("android.media.tv.flags.tuner_w_apis") public static final int FRONTEND_STATUS_TYPE_STANDARD_EXTENSION = 47; // 0x2f
field public static final int FRONTEND_STATUS_TYPE_STREAM_IDS = 39; // 0x27
field public static final int FRONTEND_STATUS_TYPE_SYMBOL_RATE = 7; // 0x7
field public static final int FRONTEND_STATUS_TYPE_T2_SYSTEM_ID = 29; // 0x1d
@@ -9952,9 +10246,9 @@
method public default void onUnlocked();
}
- @FlaggedApi("android.media.tv.flags.tuner_w_apis") public final class StandardExt {
- method public int getDvbsStandardExt();
- method public int getDvbtStandardExt();
+ @FlaggedApi("android.media.tv.flags.tuner_w_apis") public final class StandardExtension {
+ method public int getDvbsStandardExtension();
+ method public int getDvbtStandardExtension();
}
}
@@ -12570,6 +12864,59 @@
}
+package android.security.authenticationpolicy {
+
+ @FlaggedApi("android.security.secure_lockdown") public final class AuthenticationPolicyManager {
+ method @FlaggedApi("android.security.secure_lockdown") @RequiresPermission(android.Manifest.permission.MANAGE_SECURE_LOCK_DEVICE) public int disableSecureLockDevice(@NonNull android.security.authenticationpolicy.DisableSecureLockDeviceParams);
+ method @FlaggedApi("android.security.secure_lockdown") @RequiresPermission(android.Manifest.permission.MANAGE_SECURE_LOCK_DEVICE) public int enableSecureLockDevice(@NonNull android.security.authenticationpolicy.EnableSecureLockDeviceParams);
+ field @FlaggedApi("android.security.secure_lockdown") public static final int ERROR_ALREADY_ENABLED = 6; // 0x6
+ field @FlaggedApi("android.security.secure_lockdown") public static final int ERROR_INSUFFICIENT_BIOMETRICS = 5; // 0x5
+ field @FlaggedApi("android.security.secure_lockdown") public static final int ERROR_INVALID_PARAMS = 3; // 0x3
+ field @FlaggedApi("android.security.secure_lockdown") public static final int ERROR_NO_BIOMETRICS_ENROLLED = 4; // 0x4
+ field @FlaggedApi("android.security.secure_lockdown") public static final int ERROR_UNKNOWN = 0; // 0x0
+ field @FlaggedApi("android.security.secure_lockdown") public static final int ERROR_UNSUPPORTED = 2; // 0x2
+ field @FlaggedApi("android.security.secure_lockdown") public static final int SUCCESS = 1; // 0x1
+ }
+
+ @FlaggedApi("android.security.secure_lockdown") public final class DisableSecureLockDeviceParams implements android.os.Parcelable {
+ ctor public DisableSecureLockDeviceParams(@NonNull String);
+ method public int describeContents();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.security.authenticationpolicy.DisableSecureLockDeviceParams> CREATOR;
+ }
+
+ @FlaggedApi("android.security.secure_lockdown") public final class EnableSecureLockDeviceParams implements android.os.Parcelable {
+ ctor public EnableSecureLockDeviceParams(@NonNull String);
+ method public int describeContents();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.security.authenticationpolicy.EnableSecureLockDeviceParams> CREATOR;
+ }
+
+}
+
+package android.security.forensic {
+
+ @FlaggedApi("android.security.afl_api") public class ForensicManager {
+ method @RequiresPermission(android.Manifest.permission.READ_FORENSIC_STATE) public void addStateCallback(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
+ method @RequiresPermission(android.Manifest.permission.MANAGE_FORENSIC_STATE) public void disable(@NonNull java.util.concurrent.Executor, @NonNull android.security.forensic.ForensicManager.CommandCallback);
+ method @RequiresPermission(android.Manifest.permission.MANAGE_FORENSIC_STATE) public void enable(@NonNull java.util.concurrent.Executor, @NonNull android.security.forensic.ForensicManager.CommandCallback);
+ method @RequiresPermission(android.Manifest.permission.READ_FORENSIC_STATE) public void removeStateCallback(@NonNull java.util.function.Consumer<java.lang.Integer>);
+ field public static final int ERROR_DATA_SOURCE_UNAVAILABLE = 4; // 0x4
+ field public static final int ERROR_PERMISSION_DENIED = 1; // 0x1
+ field public static final int ERROR_TRANSPORT_UNAVAILABLE = 3; // 0x3
+ field public static final int ERROR_UNKNOWN = 0; // 0x0
+ field public static final int STATE_DISABLED = 1; // 0x1
+ field public static final int STATE_ENABLED = 2; // 0x2
+ field public static final int STATE_UNKNOWN = 0; // 0x0
+ }
+
+ public static interface ForensicManager.CommandCallback {
+ method public void onFailure(int);
+ method public void onSuccess();
+ }
+
+}
+
package android.security.keystore {
public class AndroidKeyStoreProvider extends java.security.Provider {
@@ -14969,6 +15316,32 @@
method @NonNull public android.telephony.CellIdentityWcdma sanitizeLocationInfo();
}
+ @FlaggedApi("com.android.internal.telephony.flags.cellular_identifier_disclosure_indications") public final class CellularIdentifierDisclosure implements android.os.Parcelable {
+ method public int describeContents();
+ method public int getCellularIdentifier();
+ method public int getNasProtocolMessage();
+ method @NonNull public String getPlmn();
+ method public boolean isEmergency();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field public static final int CELLULAR_IDENTIFIER_IMEI = 2; // 0x2
+ field public static final int CELLULAR_IDENTIFIER_IMSI = 1; // 0x1
+ field public static final int CELLULAR_IDENTIFIER_SUCI = 3; // 0x3
+ field public static final int CELLULAR_IDENTIFIER_UNKNOWN = 0; // 0x0
+ field @NonNull public static final android.os.Parcelable.Creator<android.telephony.CellularIdentifierDisclosure> CREATOR;
+ field public static final int NAS_PROTOCOL_MESSAGE_ATTACH_REQUEST = 1; // 0x1
+ field public static final int NAS_PROTOCOL_MESSAGE_AUTHENTICATION_AND_CIPHERING_RESPONSE = 6; // 0x6
+ field public static final int NAS_PROTOCOL_MESSAGE_CM_REESTABLISHMENT_REQUEST = 9; // 0x9
+ field public static final int NAS_PROTOCOL_MESSAGE_CM_SERVICE_REQUEST = 10; // 0xa
+ field public static final int NAS_PROTOCOL_MESSAGE_DEREGISTRATION_REQUEST = 8; // 0x8
+ field public static final int NAS_PROTOCOL_MESSAGE_DETACH_REQUEST = 3; // 0x3
+ field public static final int NAS_PROTOCOL_MESSAGE_IDENTITY_RESPONSE = 2; // 0x2
+ field public static final int NAS_PROTOCOL_MESSAGE_IMSI_DETACH_INDICATION = 11; // 0xb
+ field public static final int NAS_PROTOCOL_MESSAGE_LOCATION_UPDATE_REQUEST = 5; // 0x5
+ field public static final int NAS_PROTOCOL_MESSAGE_REGISTRATION_REQUEST = 7; // 0x7
+ field public static final int NAS_PROTOCOL_MESSAGE_TRACKING_AREA_UPDATE_REQUEST = 4; // 0x4
+ field public static final int NAS_PROTOCOL_MESSAGE_UNKNOWN = 0; // 0x0
+ }
+
public final class DataFailCause {
field @Deprecated public static final int VSNCP_APN_UNATHORIZED = 2238; // 0x8be
}
@@ -15420,6 +15793,75 @@
field public static final int USER_NOT_MEMBER_OF_CUG = 87; // 0x57
}
+ @FlaggedApi("com.android.internal.telephony.flags.security_algorithms_update_indications") public final class SecurityAlgorithmUpdate implements android.os.Parcelable {
+ method public int describeContents();
+ method public int getConnectionEvent();
+ method public int getEncryption();
+ method public int getIntegrity();
+ method public boolean isUnprotectedEmergency();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field public static final int CONNECTION_EVENT_AS_SIGNALLING_5G = 11; // 0xb
+ field public static final int CONNECTION_EVENT_AS_SIGNALLING_LTE = 5; // 0x5
+ field public static final int CONNECTION_EVENT_CS_SIGNALLING_3G = 2; // 0x2
+ field public static final int CONNECTION_EVENT_CS_SIGNALLING_GSM = 0; // 0x0
+ field public static final int CONNECTION_EVENT_NAS_SIGNALLING_5G = 10; // 0xa
+ field public static final int CONNECTION_EVENT_NAS_SIGNALLING_LTE = 4; // 0x4
+ field public static final int CONNECTION_EVENT_PS_SIGNALLING_3G = 3; // 0x3
+ field public static final int CONNECTION_EVENT_PS_SIGNALLING_GPRS = 1; // 0x1
+ field public static final int CONNECTION_EVENT_VOLTE_RTP = 8; // 0x8
+ field public static final int CONNECTION_EVENT_VOLTE_RTP_SOS = 9; // 0x9
+ field public static final int CONNECTION_EVENT_VOLTE_SIP = 6; // 0x6
+ field public static final int CONNECTION_EVENT_VOLTE_SIP_SOS = 7; // 0x7
+ field public static final int CONNECTION_EVENT_VONR_RTP = 14; // 0xe
+ field public static final int CONNECTION_EVENT_VONR_RTP_SOS = 15; // 0xf
+ field public static final int CONNECTION_EVENT_VONR_SIP = 12; // 0xc
+ field public static final int CONNECTION_EVENT_VONR_SIP_SOS = 13; // 0xd
+ field @NonNull public static final android.os.Parcelable.Creator<android.telephony.SecurityAlgorithmUpdate> CREATOR;
+ field public static final int SECURITY_ALGORITHM_A50 = 0; // 0x0
+ field public static final int SECURITY_ALGORITHM_A51 = 1; // 0x1
+ field public static final int SECURITY_ALGORITHM_A52 = 2; // 0x2
+ field public static final int SECURITY_ALGORITHM_A53 = 3; // 0x3
+ field public static final int SECURITY_ALGORITHM_A54 = 4; // 0x4
+ field public static final int SECURITY_ALGORITHM_AES_CBC = 71; // 0x47
+ field public static final int SECURITY_ALGORITHM_AES_EDE3_CBC = 73; // 0x49
+ field public static final int SECURITY_ALGORITHM_AES_GCM = 69; // 0x45
+ field public static final int SECURITY_ALGORITHM_AES_GMAC = 70; // 0x46
+ field public static final int SECURITY_ALGORITHM_AUTH_HMAC_SHA2_256_128 = 101; // 0x65
+ field public static final int SECURITY_ALGORITHM_DES_EDE3_CBC = 72; // 0x48
+ field public static final int SECURITY_ALGORITHM_EEA0 = 41; // 0x29
+ field public static final int SECURITY_ALGORITHM_EEA1 = 42; // 0x2a
+ field public static final int SECURITY_ALGORITHM_EEA2 = 43; // 0x2b
+ field public static final int SECURITY_ALGORITHM_EEA3 = 44; // 0x2c
+ field public static final int SECURITY_ALGORITHM_ENCR_AES_CBC = 100; // 0x64
+ field public static final int SECURITY_ALGORITHM_ENCR_AES_GCM_16 = 99; // 0x63
+ field public static final int SECURITY_ALGORITHM_GEA0 = 14; // 0xe
+ field public static final int SECURITY_ALGORITHM_GEA1 = 15; // 0xf
+ field public static final int SECURITY_ALGORITHM_GEA2 = 16; // 0x10
+ field public static final int SECURITY_ALGORITHM_GEA3 = 17; // 0x11
+ field public static final int SECURITY_ALGORITHM_GEA4 = 18; // 0x12
+ field public static final int SECURITY_ALGORITHM_GEA5 = 19; // 0x13
+ field public static final int SECURITY_ALGORITHM_HMAC_MD5_96 = 75; // 0x4b
+ field public static final int SECURITY_ALGORITHM_HMAC_SHA1_96 = 74; // 0x4a
+ field public static final int SECURITY_ALGORITHM_IMS_NULL = 67; // 0x43
+ field public static final int SECURITY_ALGORITHM_NEA0 = 55; // 0x37
+ field public static final int SECURITY_ALGORITHM_NEA1 = 56; // 0x38
+ field public static final int SECURITY_ALGORITHM_NEA2 = 57; // 0x39
+ field public static final int SECURITY_ALGORITHM_NEA3 = 58; // 0x3a
+ field public static final int SECURITY_ALGORITHM_ORYX = 124; // 0x7c
+ field public static final int SECURITY_ALGORITHM_OTHER = 114; // 0x72
+ field public static final int SECURITY_ALGORITHM_RTP = 85; // 0x55
+ field public static final int SECURITY_ALGORITHM_SIP_NO_IPSEC_CONFIG = 66; // 0x42
+ field public static final int SECURITY_ALGORITHM_SIP_NULL = 68; // 0x44
+ field public static final int SECURITY_ALGORITHM_SRTP_AES_COUNTER = 87; // 0x57
+ field public static final int SECURITY_ALGORITHM_SRTP_AES_F8 = 88; // 0x58
+ field public static final int SECURITY_ALGORITHM_SRTP_HMAC_SHA1 = 89; // 0x59
+ field public static final int SECURITY_ALGORITHM_SRTP_NULL = 86; // 0x56
+ field public static final int SECURITY_ALGORITHM_UEA0 = 29; // 0x1d
+ field public static final int SECURITY_ALGORITHM_UEA1 = 30; // 0x1e
+ field public static final int SECURITY_ALGORITHM_UEA2 = 31; // 0x1f
+ field public static final int SECURITY_ALGORITHM_UNKNOWN = 113; // 0x71
+ }
+
public class ServiceState implements android.os.Parcelable {
method @Nullable public android.telephony.NetworkRegistrationInfo getNetworkRegistrationInfo(int, int);
method @NonNull public java.util.List<android.telephony.NetworkRegistrationInfo> getNetworkRegistrationInfoListForDomain(int);
@@ -15644,6 +16086,7 @@
field @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public static final int EVENT_CALL_FORWARDING_INDICATOR_CHANGED = 4; // 0x4
field public static final int EVENT_CALL_STATE_CHANGED = 6; // 0x6
field public static final int EVENT_CARRIER_NETWORK_CHANGED = 17; // 0x11
+ field @FlaggedApi("com.android.internal.telephony.flags.cellular_identifier_disclosure_indications") @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int EVENT_CELLULAR_IDENTIFIER_DISCLOSED_CHANGED = 47; // 0x2f
field @RequiresPermission(allOf={android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public static final int EVENT_CELL_INFO_CHANGED = 11; // 0xb
field @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public static final int EVENT_CELL_LOCATION_CHANGED = 5; // 0x5
field public static final int EVENT_DATA_ACTIVATION_STATE_CHANGED = 19; // 0x13
@@ -15668,6 +16111,7 @@
field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_PRECISE_DATA_CONNECTION_STATE_CHANGED = 13; // 0xd
field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int EVENT_RADIO_POWER_STATE_CHANGED = 24; // 0x18
field @RequiresPermission(allOf={android.Manifest.permission.READ_PRECISE_PHONE_STATE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public static final int EVENT_REGISTRATION_FAILURE = 31; // 0x1f
+ field @FlaggedApi("com.android.internal.telephony.flags.cellular_identifier_disclosure_indications") @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int EVENT_SECURITY_ALGORITHMS_CHANGED = 46; // 0x2e
field public static final int EVENT_SERVICE_STATE_CHANGED = 1; // 0x1
field public static final int EVENT_SIGNAL_STRENGTHS_CHANGED = 9; // 0x9
field public static final int EVENT_SIGNAL_STRENGTH_CHANGED = 2; // 0x2
@@ -15686,6 +16130,10 @@
method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public default void onCallStatesChanged(@NonNull java.util.List<android.telephony.CallState>);
}
+ @FlaggedApi("com.android.internal.telephony.flags.cellular_identifier_disclosure_indications") public static interface TelephonyCallback.CellularIdentifierDisclosedListener {
+ method public void onCellularIdentifierDisclosedChanged(@NonNull android.telephony.CellularIdentifierDisclosure);
+ }
+
public static interface TelephonyCallback.DataEnabledListener {
method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onDataEnabledChanged(boolean, int);
}
@@ -15724,6 +16172,10 @@
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void onRadioPowerStateChanged(int);
}
+ @FlaggedApi("com.android.internal.telephony.flags.security_algorithms_update_indications") public static interface TelephonyCallback.SecurityAlgorithmsListener {
+ method public void onSecurityAlgorithmsChanged(@NonNull android.telephony.SecurityAlgorithmUpdate);
+ }
+
@FlaggedApi("com.android.internal.telephony.flags.simultaneous_calling_indications") public static interface TelephonyCallback.SimultaneousCellularCallingSupportListener {
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void onSimultaneousCellularCallingSubscriptionsChanged(@NonNull java.util.Set<java.lang.Integer>);
}
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 1192713..ad1d937 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -1805,6 +1805,7 @@
method @RequiresPermission(android.Manifest.permission.REMAP_MODIFIER_KEYS) public void remapModifierKey(int, int);
method @FlaggedApi("com.android.input.flags.device_associations") @RequiresPermission("android.permission.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY") public void removeUniqueIdAssociationByDescriptor(@NonNull String);
method @RequiresPermission("android.permission.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY") public void removeUniqueIdAssociationByPort(@NonNull String);
+ method public void resetLockedModifierState();
field public static final long BLOCK_UNTRUSTED_TOUCHES = 158002302L; // 0x96aec7eL
}
@@ -2318,7 +2319,7 @@
public class SharedConnectivityManager {
method @Nullable public static android.net.wifi.sharedconnectivity.app.SharedConnectivityManager create(@NonNull android.content.Context, @NonNull String, @NonNull String);
- method @FlaggedApi("com.android.wifi.flags.shared_connectivity_broadcast_receiver_test_api") @NonNull public android.content.BroadcastReceiver getBroadcastReceiver();
+ method @NonNull public android.content.BroadcastReceiver getBroadcastReceiver();
method @Nullable public android.content.ServiceConnection getServiceConnection();
method public void setService(@Nullable android.os.IInterface);
}
@@ -3240,6 +3241,7 @@
method @Nullable public android.content.Intent createWalletIntent();
method @Nullable public android.content.Intent createWalletSettingsIntent();
method public void disconnect();
+ method @FlaggedApi("android.service.quickaccesswallet.launch_wallet_option_on_power_double_tap") public void getGestureTargetActivityPendingIntent(@NonNull java.util.concurrent.Executor, @NonNull android.service.quickaccesswallet.QuickAccessWalletClient.GesturePendingIntentCallback);
method public void getWalletCards(@NonNull android.service.quickaccesswallet.GetWalletCardsRequest, @NonNull android.service.quickaccesswallet.QuickAccessWalletClient.OnWalletCardsRetrievedCallback);
method public void getWalletCards(@NonNull java.util.concurrent.Executor, @NonNull android.service.quickaccesswallet.GetWalletCardsRequest, @NonNull android.service.quickaccesswallet.QuickAccessWalletClient.OnWalletCardsRetrievedCallback);
method public void getWalletPendingIntent(@NonNull java.util.concurrent.Executor, @NonNull android.service.quickaccesswallet.QuickAccessWalletClient.WalletPendingIntentCallback);
@@ -3251,6 +3253,10 @@
method public void selectWalletCard(@NonNull android.service.quickaccesswallet.SelectWalletCardRequest);
}
+ @FlaggedApi("android.service.quickaccesswallet.launch_wallet_option_on_power_double_tap") public static interface QuickAccessWalletClient.GesturePendingIntentCallback {
+ method @FlaggedApi("android.service.quickaccesswallet.launch_wallet_option_on_power_double_tap") public void onGesturePendingIntentRetrieved(@Nullable android.app.PendingIntent);
+ }
+
public static interface QuickAccessWalletClient.OnWalletCardsRetrievedCallback {
method public void onWalletCardRetrievalError(@NonNull android.service.quickaccesswallet.GetWalletCardsError);
method public void onWalletCardsRetrieved(@NonNull android.service.quickaccesswallet.GetWalletCardsResponse);
@@ -3393,6 +3399,10 @@
ctor public BarringInfo.BarringServiceInfo(int, boolean, int, int);
}
+ @FlaggedApi("com.android.internal.telephony.flags.cellular_identifier_disclosure_indications") public final class CellularIdentifierDisclosure implements android.os.Parcelable {
+ ctor public CellularIdentifierDisclosure(int, int, @NonNull String, boolean);
+ }
+
public class MbmsDownloadSession implements java.lang.AutoCloseable {
field public static final String MBMS_DOWNLOAD_SERVICE_OVERRIDE_METADATA = "mbms-download-service-override";
}
@@ -3420,6 +3430,10 @@
ctor @Deprecated public PreciseDataConnectionState(int, int, int, @NonNull String, @Nullable android.net.LinkProperties, int);
}
+ @FlaggedApi("com.android.internal.telephony.flags.security_algorithms_update_indications") public final class SecurityAlgorithmUpdate implements android.os.Parcelable {
+ ctor public SecurityAlgorithmUpdate(int, int, int, boolean);
+ }
+
public class ServiceState implements android.os.Parcelable {
method public void addNetworkRegistrationInfo(android.telephony.NetworkRegistrationInfo);
method public int getDataNetworkType();
diff --git a/core/api/test-lint-baseline.txt b/core/api/test-lint-baseline.txt
index 08bb082..c2fac70 100644
--- a/core/api/test-lint-baseline.txt
+++ b/core/api/test-lint-baseline.txt
@@ -1,4 +1,10 @@
// Baseline format: 1.0
+ActionValue: android.view.contentcapture.ViewNode.ViewStructureImpl#EXTRA_VIRTUAL_STRUCTURE_TYPE:
+ Inconsistent extra value; expected `android.view.contentcapture.extra.VIRTUAL_STRUCTURE_TYPE`, was `android.view.ViewStructure.extra.VIRTUAL_STRUCTURE_TYPE`
+ActionValue: android.view.contentcapture.ViewNode.ViewStructureImpl#EXTRA_VIRTUAL_STRUCTURE_VERSION_NUMBER:
+ Inconsistent extra value; expected `android.view.contentcapture.extra.VIRTUAL_STRUCTURE_VERSION_NUMBER`, was `android.view.ViewStructure.extra.VIRTUAL_STRUCTURE_VERSION_NUMBER`
+
+
BannedThrow: android.os.vibrator.persistence.VibrationXmlSerializer#serialize(android.os.VibrationEffect, java.io.Writer):
Methods must not throw unchecked exceptions
diff --git a/core/java/Android.bp b/core/java/Android.bp
index cf5ebbaa3..bc38294 100644
--- a/core/java/Android.bp
+++ b/core/java/Android.bp
@@ -232,6 +232,8 @@
"android.hardware.power-aidl",
],
srcs: [
+ "android/os/CpuHeadroomParamsInternal.aidl",
+ "android/os/GpuHeadroomParamsInternal.aidl",
"android/os/IHintManager.aidl",
"android/os/IHintSession.aidl",
],
diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java
index 87acbbf..72450999 100644
--- a/core/java/android/accounts/AccountManager.java
+++ b/core/java/android/accounts/AccountManager.java
@@ -16,9 +16,13 @@
package android.accounts;
+import static android.Manifest.permission.COPY_ACCOUNTS;
+import static android.Manifest.permission.REMOVE_ACCOUNTS;
import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
+import static android.app.admin.flags.Flags.FLAG_SPLIT_CREATE_MANAGED_PROFILE_ENABLED;
import android.annotation.BroadcastBehavior;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -26,6 +30,7 @@
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.Size;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.UserHandleAware;
@@ -1312,7 +1317,8 @@
* {@link AccountManagerFuture} must not be used on the main thread.
*
* <p>This method requires the caller to have a signature match with the
- * authenticator that manages the specified account.
+ * authenticator that manages the specified account, be a profile owner or have the
+ * {@link android.Manifest.permission#REMOVE_ACCOUNTS} permission.
*
* <p><b>NOTE:</b> If targeting your app to work on API level 22 and before,
* MANAGE_ACCOUNTS permission is needed for those platforms. See docs for
@@ -1344,6 +1350,8 @@
* </ul>
*/
@UserHandleAware
+ @RequiresPermission(value = REMOVE_ACCOUNTS, conditional = true)
+ @FlaggedApi(FLAG_SPLIT_CREATE_MANAGED_PROFILE_ENABLED)
public AccountManagerFuture<Bundle> removeAccount(final Account account,
final Activity activity, AccountManagerCallback<Bundle> callback, Handler handler) {
return removeAccountAsUser(account, activity, callback, handler, mContext.getUser());
@@ -2019,9 +2027,15 @@
* succeeded.
* @hide
*/
+ @SuppressLint("SamShouldBeLast")
+ @NonNull
+ @SystemApi
+ @RequiresPermission(anyOf = {COPY_ACCOUNTS, INTERACT_ACROSS_USERS_FULL})
+ @FlaggedApi(FLAG_SPLIT_CREATE_MANAGED_PROFILE_ENABLED)
public AccountManagerFuture<Boolean> copyAccountToUser(
- final Account account, final UserHandle fromUser, final UserHandle toUser,
- AccountManagerCallback<Boolean> callback, Handler handler) {
+ @NonNull final Account account, @NonNull final UserHandle fromUser,
+ @NonNull final UserHandle toUser, @Nullable AccountManagerCallback<Boolean> callback,
+ @Nullable Handler handler) {
if (account == null) throw new IllegalArgumentException("account is null");
if (toUser == null || fromUser == null) {
throw new IllegalArgumentException("fromUser and toUser cannot be null");
diff --git a/core/java/android/adaptiveauth/OWNERS b/core/java/android/adaptiveauth/OWNERS
index bc8efa9..4310d1a 100644
--- a/core/java/android/adaptiveauth/OWNERS
+++ b/core/java/android/adaptiveauth/OWNERS
@@ -1 +1 @@
-include /services/core/java/com/android/server/security/adaptiveauthentication/OWNERS
\ No newline at end of file
+include /services/core/java/com/android/server/security/authenticationpolicy/OWNERS
\ No newline at end of file
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 419eb7d..38aea64 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -1270,27 +1270,22 @@
}
/**
- * To make users aware of system features such as the app header menu and its various
- * functionalities, educational dialogs are shown to demonstrate how to find and utilize these
- * features. Using this method, an activity can specify if it wants these educational dialogs to
- * be shown. When set to {@code true}, these dialogs are not completely blocked; however, the
- * system will be notified that they should not be shown unless necessary. If this API is not
- * called, the system's educational dialogs are not limited by default.
+ * Requests to show the “Open in browser” education. “Open in browser” is a feature
+ * within the app header that allows users to switch from an app to the web. The feature
+ * is made available when an application is opened by a user clicking a link or when a
+ * link is provided by an application. Links can be provided by utilizing
+ * {@link AssistContent#EXTRA_AUTHENTICATING_USER_WEB_URI} or
+ * {@link AssistContent#setWebUri}.
*
- * <p>This method can be utilized when activities have states where showing an
- * educational dialog would be disruptive to the user. For example, if a game application is
- * expecting prompt user input, this method can be used to limit educational dialogs such as the
- * dialogs that showcase the app header's features which, in this instance, would disrupt the
- * user's experience if shown.</p>
- *
- * <p>Note that educational dialogs may be shown soon after this activity is launched, so
- * this method must be called early if the intent is to limit the dialogs from the start.</p>
+ * <p>This method should be utilized when an activity wants to nudge the user to switch
+ * to the web application in cases where the web may provide the user with a better
+ * experience. Note that this method does not guarantee that the education will be shown.</p>
*/
@FlaggedApi(com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_TO_WEB_EDUCATION)
- public final void setLimitSystemEducationDialogs(boolean limitSystemEducationDialogs) {
+ public final void requestOpenInBrowserEducation() {
try {
ActivityTaskManager
- .getService().setLimitSystemEducationDialogs(mToken, limitSystemEducationDialogs);
+ .getService().requestOpenInBrowserEducation(mToken);
} catch (RemoteException e) {
// Empty
}
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index ab75069..33ba058 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -2679,62 +2679,6 @@
*/
public static class RecentTaskInfo extends TaskInfo implements Parcelable {
/**
- * @hide
- */
- public static class PersistedTaskSnapshotData {
- /**
- * The bounds of the task when the last snapshot was taken, may be null if the task is
- * not yet attached to the hierarchy.
- * @see {@link android.window.TaskSnapshot#mTaskSize}.
- * @hide
- */
- public @Nullable Point taskSize;
-
- /**
- * The content insets of the task when the task snapshot was taken.
- * @see {@link android.window.TaskSnapshot#mContentInsets}.
- * @hide
- */
- public @Nullable Rect contentInsets;
-
- /**
- * The size of the last snapshot taken, may be null if there is no associated snapshot.
- * @see {@link android.window.TaskSnapshot#mSnapshot}.
- * @hide
- */
- public @Nullable Point bufferSize;
-
- /**
- * Sets the data from the other data.
- * @hide
- */
- public void set(PersistedTaskSnapshotData other) {
- taskSize = other.taskSize;
- contentInsets = other.contentInsets;
- bufferSize = other.bufferSize;
- }
-
- /**
- * Sets the data from the provided {@param snapshot}.
- * @hide
- */
- public void set(TaskSnapshot snapshot) {
- if (snapshot == null) {
- taskSize = null;
- contentInsets = null;
- bufferSize = null;
- return;
- }
- final HardwareBuffer buffer = snapshot.getHardwareBuffer();
- taskSize = new Point(snapshot.getTaskSize());
- contentInsets = new Rect(snapshot.getContentInsets());
- bufferSize = buffer != null
- ? new Point(buffer.getWidth(), buffer.getHeight())
- : null;
- }
- }
-
- /**
* If this task is currently running, this is the identifier for it.
* If it is not running, this will be -1.
*
@@ -2770,24 +2714,6 @@
@Deprecated
public int affiliatedTaskId;
- /**
- * Information of organized child tasks.
- *
- * @deprecated No longer used
- * @hide
- */
- @Deprecated
- public ArrayList<RecentTaskInfo> childrenTaskInfos = new ArrayList<>();
-
- /**
- * Information about the last snapshot taken for this task.
- *
- * @deprecated No longer used
- * @hide
- */
- @Deprecated
- public PersistedTaskSnapshotData lastSnapshotData = new PersistedTaskSnapshotData();
-
public RecentTaskInfo() {
}
@@ -2803,10 +2729,6 @@
public void readFromParcel(Parcel source) {
id = source.readInt();
persistentId = source.readInt();
- childrenTaskInfos = source.readArrayList(RecentTaskInfo.class.getClassLoader(), android.app.ActivityManager.RecentTaskInfo.class);
- lastSnapshotData.taskSize = source.readTypedObject(Point.CREATOR);
- lastSnapshotData.contentInsets = source.readTypedObject(Rect.CREATOR);
- lastSnapshotData.bufferSize = source.readTypedObject(Point.CREATOR);
super.readTaskFromParcel(source);
}
@@ -2814,10 +2736,6 @@
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(id);
dest.writeInt(persistentId);
- dest.writeList(childrenTaskInfos);
- dest.writeTypedObject(lastSnapshotData.taskSize, flags);
- dest.writeTypedObject(lastSnapshotData.contentInsets, flags);
- dest.writeTypedObject(lastSnapshotData.bufferSize, flags);
super.writeTaskToParcel(dest, flags);
}
@@ -2884,11 +2802,6 @@
pw.println(" }");
}
pw.print(" ");
- pw.print(" lastSnapshotData {");
- pw.print(" taskSize=" + lastSnapshotData.taskSize);
- pw.print(" contentInsets=" + lastSnapshotData.contentInsets);
- pw.print(" bufferSize=" + lastSnapshotData.bufferSize);
- pw.println(" }");
}
}
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 8b37dbd..3476578 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -1624,9 +1624,19 @@
/** @hide Access to read oxygen saturation. */
public static final int OP_READ_OXYGEN_SATURATION = AppOpEnums.APP_OP_READ_OXYGEN_SATURATION;
+ /** @hide Access to write system preferences. */
+ public static final int OP_WRITE_SYSTEM_PREFERENCES =
+ AppOpEnums.APP_OP_WRITE_SYSTEM_PREFERENCES;
+
+ /** @hide Access to audio playback and control APIs. */
+ public static final int OP_CONTROL_AUDIO = AppOpEnums.APP_OP_CONTROL_AUDIO;
+
+ /** @hide Similar to {@link OP_CONTROL_AUDIO}, but doesn't require capabilities. */
+ public static final int OP_CONTROL_AUDIO_PARTIAL = AppOpEnums.APP_OP_CONTROL_AUDIO_PARTIAL;
+
/** @hide */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- public static final int _NUM_OP = 153;
+ public static final int _NUM_OP = 156;
/**
* All app ops represented as strings.
@@ -1783,6 +1793,9 @@
OPSTR_READ_SKIN_TEMPERATURE,
OPSTR_RANGING,
OPSTR_READ_OXYGEN_SATURATION,
+ OPSTR_WRITE_SYSTEM_PREFERENCES,
+ OPSTR_CONTROL_AUDIO,
+ OPSTR_CONTROL_AUDIO_PARTIAL,
})
public @interface AppOpString {}
@@ -2540,6 +2553,15 @@
@FlaggedApi(Flags.FLAG_RANGING_PERMISSION_ENABLED)
public static final String OPSTR_RANGING = "android:ranging";
+ /** @hide Access to system preferences write services */
+ public static final String OPSTR_WRITE_SYSTEM_PREFERENCES = "android:write_system_preferences";
+
+ /** @hide Access to audio playback and control APIs */
+ public static final String OPSTR_CONTROL_AUDIO = "android:control_audio";
+
+ /** @hide Access to a audio playback and control APIs without capability requirements */
+ public static final String OPSTR_CONTROL_AUDIO_PARTIAL = "android:control_audio_partial";
+
/** {@link #sAppOpsToNote} not initialized yet for this op */
private static final byte SHOULD_COLLECT_NOTE_OP_NOT_INITIALIZED = 0;
/** Should not collect noting of this app-op in {@link #sAppOpsToNote} */
@@ -2656,6 +2678,7 @@
OP_RECEIVE_SANDBOX_TRIGGER_AUDIO,
OP_MEDIA_ROUTING_CONTROL,
OP_READ_SYSTEM_GRAMMATICAL_GENDER,
+ OP_WRITE_SYSTEM_PREFERENCES,
};
@SuppressWarnings("FlaggedApi")
@@ -3144,6 +3167,14 @@
Flags.replaceBodySensorPermissionEnabled()
? HealthPermissions.READ_OXYGEN_SATURATION : null)
.setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
+ new AppOpInfo.Builder(OP_WRITE_SYSTEM_PREFERENCES, OPSTR_WRITE_SYSTEM_PREFERENCES,
+ "WRITE_SYSTEM_PREFERENCES").setPermission(
+ com.android.settingslib.flags.Flags.writeSystemPreferencePermissionEnabled()
+ ? Manifest.permission.WRITE_SYSTEM_PREFERENCES : null).build(),
+ new AppOpInfo.Builder(OP_CONTROL_AUDIO, OPSTR_CONTROL_AUDIO,
+ "CONTROL_AUDIO").setDefaultMode(AppOpsManager.MODE_FOREGROUND).build(),
+ new AppOpInfo.Builder(OP_CONTROL_AUDIO_PARTIAL, OPSTR_CONTROL_AUDIO_PARTIAL,
+ "CONTROL_AUDIO_PARTIAL").setDefaultMode(AppOpsManager.MODE_FOREGROUND).build(),
};
// The number of longs needed to form a full bitmask of app ops
@@ -8903,12 +8934,21 @@
}
/**
- * Do a quick check for whether an application might be able to perform an operation.
- * This is <em>not</em> a security check; you must use {@link #noteOp(String, int, String,
- * String, String)} or {@link #startOp(String, int, String, String, String)} for your actual
- * security checks. This function can just be used for a quick check to see if an operation has
- * been disabled for the application, as an early reject of some work. This does not modify the
- * time stamp or other data about the operation.
+ * Check whether an application might be able to perform an operation.
+ * <p>
+ * For platform versions before {@link android.os.Build.VERSION_CODES#BAKLAVA}, this is
+ * <em>not</em> a security check; you must use {@link #noteOp(String, int, String, String,
+ * String)} or {@link #startOp(String, int, String, String, String)} for your actual security
+ * checks. This function can just be used for a quick check to see if an operation has been
+ * disabled for the application, as an early reject of some work.
+ * <p>
+ * For platform versions equal to or after {@link android.os.Build.VERSION_CODES#BAKLAVA}, this
+ * is no longer an unsafe check, and it does the same security check as {@link #noteOp(String,
+ * int, String, String, String)} and {@link #startOp(String, int, String, String, String)}.
+ * However, it's preferred to use {@link #checkOp(String, int, String)}, since the word "unsafe"
+ * in the name of this API is no longer accurate.
+ * <p>
+ * This API does not modify the time stamp or other data about the operation.
*
* @param op The operation to check. One of the OPSTR_* constants.
* @param uid The user id of the application attempting to perform the operation.
@@ -8917,31 +8957,108 @@
* {@link #MODE_IGNORED} if it is not allowed and should be silently ignored (without
* causing the app to crash).
* @throws SecurityException If the app has been configured to crash on this op.
+ *
+ * @deprecated Use {@link #checkOp(String, int, String)}
*/
+ @Deprecated
+ @FlaggedApi(android.permission.flags.Flags.FLAG_CHECK_OP_OVERLOAD_API_ENABLED)
public int unsafeCheckOp(@NonNull String op, int uid, @NonNull String packageName) {
return checkOp(strOpToOp(op), uid, packageName);
}
/**
- * @deprecated Renamed to {@link #unsafeCheckOp(String, int, String)}.
+ * Check whether an application can perform an operation.
+ * <p>
+ * For platform versions before {@link android.os.Build.VERSION_CODES#BAKLAVA}, this is
+ * <em>not</em> a security check; you must use {@link #noteOp(String, int, String, String,
+ * String)} or {@link #startOp(String, int, String, String, String)} for your actual security
+ * checks. This function can just be used for a quick check to see if an operation has been
+ * disabled for the application, as an early reject of some work.
+ * <p>
+ * For platform versions equal to or after {@link android.os.Build.VERSION_CODES#BAKLAVA}, it
+ * does the same security check as {@link #noteOp(String, int, String, String, String)} and
+ * {@link #startOp(String, int, String, String, String)}, and should be preferred to use.
+ * <p>
+ * This API does not modify the time stamp or other data about the operation.
+ *
+ * @param op The operation to check. One of the OPSTR_* constants.
+ * @param uid The uid of the application attempting to perform the operation.
+ * @param packageName The name of the application attempting to perform the operation.
+ * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or
+ * {@link #MODE_IGNORED} if it is not allowed and should be silently ignored (without
+ * causing the app to crash).
+ * @throws SecurityException If the app has been configured to crash on this op.
*/
- @Deprecated
+ @FlaggedApi(android.permission.flags.Flags.FLAG_CHECK_OP_OVERLOAD_API_ENABLED)
public int checkOp(@NonNull String op, int uid, @NonNull String packageName) {
return checkOp(strOpToOp(op), uid, packageName);
}
/**
- * Like {@link #checkOp} but instead of throwing a {@link SecurityException} it
- * returns {@link #MODE_ERRORED}.
+ * Like {@link #unsafeCheckOp(String, int, String)} but instead of throwing a
+ * {@link SecurityException} it returns {@link #MODE_ERRORED}.
+ *
+ * @deprecated Use {@link #checkOpNoThrow(String, int, String)}
*/
+ @Deprecated
+ @FlaggedApi(android.permission.flags.Flags.FLAG_CHECK_OP_OVERLOAD_API_ENABLED)
public int unsafeCheckOpNoThrow(@NonNull String op, int uid, @NonNull String packageName) {
return checkOpNoThrow(strOpToOp(op), uid, packageName);
}
/**
- * @deprecated Renamed to {@link #unsafeCheckOpNoThrow(String, int, String)}.
+ * Check whether an application can perform an operation. It does the same security check as
+ * {@link #noteOp(String, int, String, String, String)} and {@link #startOp(String, int, String,
+ * String, String)}, but does not modify the time stamp or other data about the operation.
+ *
+ * @param op The operation to check. One of the OPSTR_* constants.
+ * @param uid The uid of the application attempting to perform the operation.
+ * @param packageName The name of the application attempting to perform the operation.
+ * @param attributionTag The {@link Context#createAttributionContext attribution tag} of the
+ * calling context or {@code null} for default attribution
+ * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or {@link #MODE_IGNORED}
+ * if it is not allowed and should be silently ignored (without causing the app to crash).
+ * @throws SecurityException If the app has been configured to crash on this op.
*/
- @Deprecated
+ @FlaggedApi(android.permission.flags.Flags.FLAG_CHECK_OP_OVERLOAD_API_ENABLED)
+ public int checkOp(@NonNull String op, int uid, @NonNull String packageName,
+ @Nullable String attributionTag) {
+ int mode = checkOpNoThrow(strOpToOp(op), uid, packageName, attributionTag,
+ Context.DEVICE_ID_DEFAULT);
+ if (mode == MODE_ERRORED) {
+ throw new SecurityException(buildSecurityExceptionMsg(strOpToOp(op), uid, packageName));
+ }
+ return mode;
+ }
+
+ /**
+ * Like {@link #checkOp(String, int, String, String)} but instead of throwing a
+ * {@link SecurityException} it returns {@link #MODE_ERRORED}.
+ */
+ @FlaggedApi(android.permission.flags.Flags.FLAG_CHECK_OP_OVERLOAD_API_ENABLED)
+ public int checkOpNoThrow(@NonNull String op, int uid, @NonNull String packageName,
+ @Nullable String attributionTag) {
+ return checkOpNoThrow(strOpToOp(op), uid, packageName, attributionTag,
+ Context.DEVICE_ID_DEFAULT);
+ }
+
+ /**
+ * Like {@link #checkOp(String, int, String, String)} but returns the <em>raw</em> mode
+ * associated with the op. Does not throw a security exception, does not translate
+ * {@link #MODE_FOREGROUND}.
+ */
+ @FlaggedApi(android.permission.flags.Flags.FLAG_CHECK_OP_OVERLOAD_API_ENABLED)
+ public int checkOpRawNoThrow(@NonNull String op, int uid, @NonNull String packageName,
+ @Nullable String attributionTag) {
+ return checkOpRawNoThrow(strOpToOp(op), uid, packageName, attributionTag,
+ Context.DEVICE_ID_DEFAULT);
+ }
+
+ /**
+ * Like {@link #checkOp(String, int, String)} but instead of throwing a
+ * {@link SecurityException} it returns {@link #MODE_ERRORED}.
+ */
+ @FlaggedApi(android.permission.flags.Flags.FLAG_CHECK_OP_OVERLOAD_API_ENABLED)
public int checkOpNoThrow(@NonNull String op, int uid, @NonNull String packageName) {
return checkOpNoThrow(strOpToOp(op), uid, packageName);
}
@@ -8949,16 +9066,23 @@
/**
* Like {@link #checkOp} but returns the <em>raw</em> mode associated with the op.
* Does not throw a security exception, does not translate {@link #MODE_FOREGROUND}.
+ *
+ * @deprecated Use {@link #checkOpRawNoThrow(String, int, String, String)} instead
*/
+ @Deprecated
+ @FlaggedApi(android.permission.flags.Flags.FLAG_CHECK_OP_OVERLOAD_API_ENABLED)
public int unsafeCheckOpRaw(@NonNull String op, int uid, @NonNull String packageName) {
return unsafeCheckOpRawNoThrow(op, uid, packageName);
}
/**
- * Like {@link #unsafeCheckOpNoThrow(String, int, String)} but returns the <em>raw</em>
- * mode associated with the op. Does not throw a security exception, does not translate
- * {@link #MODE_FOREGROUND}.
+ * Like {@link #checkOp} but returns the <em>raw</em> mode associated with the op.
+ * Does not throw a security exception, does not translate {@link #MODE_FOREGROUND}.
+ *
+ * @deprecated Use {@link #checkOpRawNoThrow(String, int, String, String)} instead
*/
+ @Deprecated
+ @FlaggedApi(android.permission.flags.Flags.FLAG_CHECK_OP_OVERLOAD_API_ENABLED)
public int unsafeCheckOpRawNoThrow(@NonNull String op, int uid, @NonNull String packageName) {
return unsafeCheckOpRawNoThrow(strOpToOp(op), uid, packageName);
}
@@ -8969,8 +9093,9 @@
* @hide
*/
public int unsafeCheckOpRawNoThrow(int op, @NonNull AttributionSource attributionSource) {
- return unsafeCheckOpRawNoThrow(op, attributionSource.getUid(),
- attributionSource.getPackageName(), attributionSource.getDeviceId());
+ return checkOpRawNoThrow(op, attributionSource.getUid(),
+ attributionSource.getPackageName(), attributionSource.getAttributionTag(),
+ attributionSource.getDeviceId());
}
/**
@@ -8991,20 +9116,20 @@
* @hide
*/
public int unsafeCheckOpRawNoThrow(int op, int uid, @NonNull String packageName) {
- return unsafeCheckOpRawNoThrow(op, uid, packageName, Context.DEVICE_ID_DEFAULT);
+ return checkOpRawNoThrow(op, uid, packageName, null, Context.DEVICE_ID_DEFAULT);
}
- private int unsafeCheckOpRawNoThrow(int op, int uid, @NonNull String packageName,
- int virtualDeviceId) {
+ private int checkOpRawNoThrow(int op, int uid, @NonNull String packageName,
+ @Nullable String attributionTag, int virtualDeviceId) {
try {
int mode;
if (isAppOpModeCachingEnabled(op)) {
mode = sAppOpModeCache.query(
- new AppOpModeQuery(op, uid, packageName, virtualDeviceId, null,
+ new AppOpModeQuery(op, uid, packageName, virtualDeviceId, attributionTag,
"unsafeCheckOpRawNoThrow"));
} else {
mode = mService.checkOperationRawForDevice(
- op, uid, packageName, null, virtualDeviceId);
+ op, uid, packageName, attributionTag, virtualDeviceId);
}
return mode;
} catch (RemoteException e) {
@@ -9403,10 +9528,12 @@
}
/**
- * Do a quick check for whether an application might be able to perform an operation.
- * This is <em>not</em> a security check; you must use {@link #noteOp(String, int, String,
- * String, String)} or {@link #startOp(int, int, String, boolean, String, String)} for your
- * actual security checks, which also ensure that the given uid and package name are consistent.
+ * Check whether an application can perform an operation.
+ * <p>
+ * For platform versions before {@link android.os.Build.VERSION_CODES#BAKLAVA}, this is
+ * <em>not</em> a security check; you must use {@link #noteOp(String, int, String, String,
+ * String)} or {@link #startOp(int, int, String, boolean, String, String)} for your actual
+ * security checks, which also ensure that the given uid and package name are consistent.
* This function can just be used for a quick check to see if an operation has been disabled for
* the application, as an early reject of some work. This does not modify the time stamp or
* other data about the operation.
@@ -9422,6 +9549,13 @@
* as {@link #MODE_ALLOWED}.</li>
* </ul>
*
+ * <p>
+ * For platform versions equal to or after {@link android.os.Build.VERSION_CODES#BAKLAVA}, it
+ * does the same security check as {@link #noteOp(String, int, String, String, String)} and
+ * {@link #startOp(String, int, String, String, String)}.
+ * <p>
+ * This API does not modify the time stamp or other data about the operation.
+ *
* @param op The operation to check. One of the OP_* constants.
* @param uid The user id of the application attempting to perform the operation.
* @param packageName The name of the application attempting to perform the operation.
@@ -9433,29 +9567,11 @@
*/
@UnsupportedAppUsage
public int checkOp(int op, int uid, String packageName) {
- try {
- int mode;
- if (isAppOpModeCachingEnabled(op)) {
- mode = sAppOpModeCache.query(
- new AppOpModeQuery(op, uid, packageName, Context.DEVICE_ID_DEFAULT, null,
- "checkOp"));
- if (mode == MODE_FOREGROUND) {
- // We only cache raw mode. If the mode is FOREGROUND, we need another binder
- // call to fetch translated value based on the process state.
- mode = mService.checkOperationForDevice(op, uid, packageName,
- Context.DEVICE_ID_DEFAULT);
- }
- } else {
- mode = mService.checkOperationForDevice(op, uid, packageName,
- Context.DEVICE_ID_DEFAULT);
- }
- if (mode == MODE_ERRORED) {
- throw new SecurityException(buildSecurityExceptionMsg(op, uid, packageName));
- }
- return mode;
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ int mode = checkOpNoThrow(op, uid, packageName, null, Context.DEVICE_ID_DEFAULT);
+ if (mode == MODE_ERRORED) {
+ throw new SecurityException(buildSecurityExceptionMsg(op, uid, packageName));
}
+ return mode;
}
/**
@@ -9468,7 +9584,7 @@
*/
public int checkOpNoThrow(int op, AttributionSource attributionSource) {
return checkOpNoThrow(op, attributionSource.getUid(), attributionSource.getPackageName(),
- attributionSource.getDeviceId());
+ attributionSource.getAttributionTag(), attributionSource.getDeviceId());
}
/**
@@ -9481,23 +9597,26 @@
*/
@UnsupportedAppUsage
public int checkOpNoThrow(int op, int uid, String packageName) {
- return checkOpNoThrow(op, uid, packageName, Context.DEVICE_ID_DEFAULT);
+ return checkOpNoThrow(op, uid, packageName, null, Context.DEVICE_ID_DEFAULT);
}
- private int checkOpNoThrow(int op, int uid, String packageName, int virtualDeviceId) {
+ private int checkOpNoThrow(int op, int uid, String packageName, @Nullable String attributionTag,
+ int virtualDeviceId) {
try {
int mode;
if (isAppOpModeCachingEnabled(op)) {
mode = sAppOpModeCache.query(
- new AppOpModeQuery(op, uid, packageName, virtualDeviceId, null,
+ new AppOpModeQuery(op, uid, packageName, virtualDeviceId, attributionTag,
"checkOpNoThrow"));
if (mode == MODE_FOREGROUND) {
// We only cache raw mode. If the mode is FOREGROUND, we need another binder
// call to fetch translated value based on the process state.
- mode = mService.checkOperationForDevice(op, uid, packageName, virtualDeviceId);
+ mode = mService.checkOperationForDevice(op, uid, packageName, attributionTag,
+ virtualDeviceId);
}
} else {
- mode = mService.checkOperationForDevice(op, uid, packageName, virtualDeviceId);
+ mode = mService.checkOperationForDevice(op, uid, packageName, attributionTag,
+ virtualDeviceId);
}
return mode;
} catch (RemoteException e) {
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index e2479169..7e0a9b6 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -16,6 +16,7 @@
package android.app;
+import static android.app.PropertyInvalidatedCache.MODULE_SYSTEM;
import static android.app.PropertyInvalidatedCache.createSystemCacheKey;
import static android.app.admin.DevicePolicyResources.Drawables.Style.SOLID_COLORED;
import static android.app.admin.DevicePolicyResources.Drawables.Style.SOLID_NOT_COLORED;
@@ -783,43 +784,24 @@
}
/**
+ * The API and cache name for hasSystemFeature.
+ */
+ private static final String HAS_SYSTEM_FEATURE_API = "has_system_feature";
+
+ /**
* Identifies a single hasSystemFeature query.
*/
- @Immutable
- private static final class HasSystemFeatureQuery {
- public final String name;
- public final int version;
- public HasSystemFeatureQuery(String n, int v) {
- name = n;
- version = v;
- }
- @Override
- public String toString() {
- return String.format("HasSystemFeatureQuery(name=\"%s\", version=%d)",
- name, version);
- }
- @Override
- public boolean equals(@Nullable Object o) {
- if (o instanceof HasSystemFeatureQuery) {
- HasSystemFeatureQuery r = (HasSystemFeatureQuery) o;
- return Objects.equals(name, r.name) && version == r.version;
- } else {
- return false;
- }
- }
- @Override
- public int hashCode() {
- return Objects.hashCode(name) * 13 + version;
- }
- }
+ private record HasSystemFeatureQuery(String name, int version) {}
// Make this cache relatively large. There are many system features and
// none are ever invalidated. MPTS tests suggests that the cache should
// hold at least 150 entries.
private final static PropertyInvalidatedCache<HasSystemFeatureQuery, Boolean>
- mHasSystemFeatureCache =
- new PropertyInvalidatedCache<HasSystemFeatureQuery, Boolean>(
- 256, createSystemCacheKey("has_system_feature")) {
+ mHasSystemFeatureCache = new PropertyInvalidatedCache<>(
+ new PropertyInvalidatedCache.Args(MODULE_SYSTEM)
+ .api(HAS_SYSTEM_FEATURE_API).maxEntries(256).isolateUids(false),
+ HAS_SYSTEM_FEATURE_API, null) {
+
@Override
public Boolean recompute(HasSystemFeatureQuery query) {
try {
diff --git a/core/java/android/app/ForegroundServiceTypePolicy.java b/core/java/android/app/ForegroundServiceTypePolicy.java
index 16444dc..6efc4ef 100644
--- a/core/java/android/app/ForegroundServiceTypePolicy.java
+++ b/core/java/android/app/ForegroundServiceTypePolicy.java
@@ -62,6 +62,7 @@
import android.hardware.usb.UsbAccessory;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbManager;
+import android.health.connect.HealthPermissions;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
@@ -484,21 +485,35 @@
*/
public static final @NonNull ForegroundServiceTypePolicyInfo FGS_TYPE_POLICY_HEALTH =
new ForegroundServiceTypePolicyInfo(
- FOREGROUND_SERVICE_TYPE_HEALTH,
- ForegroundServiceTypePolicyInfo.INVALID_CHANGE_ID,
- ForegroundServiceTypePolicyInfo.INVALID_CHANGE_ID,
- new ForegroundServiceTypePermissions(new ForegroundServiceTypePermission[] {
- new RegularPermission(Manifest.permission.FOREGROUND_SERVICE_HEALTH)
- }, true),
- new ForegroundServiceTypePermissions(new ForegroundServiceTypePermission[] {
- new RegularPermission(Manifest.permission.ACTIVITY_RECOGNITION),
- new RegularPermission(Manifest.permission.BODY_SENSORS),
- new RegularPermission(Manifest.permission.HIGH_SAMPLING_RATE_SENSORS),
- }, false),
- FGS_TYPE_PERM_ENFORCEMENT_FLAG_HEALTH /* permissionEnforcementFlag */,
- true /* permissionEnforcementFlagDefaultValue */,
- false /* foregroundOnlyPermission */
- );
+ FOREGROUND_SERVICE_TYPE_HEALTH,
+ ForegroundServiceTypePolicyInfo.INVALID_CHANGE_ID,
+ ForegroundServiceTypePolicyInfo.INVALID_CHANGE_ID,
+ new ForegroundServiceTypePermissions(
+ new ForegroundServiceTypePermission[] {
+ new RegularPermission(Manifest.permission.FOREGROUND_SERVICE_HEALTH)
+ },
+ true),
+ new ForegroundServiceTypePermissions(getAllowedHealthPermissions(), false),
+ FGS_TYPE_PERM_ENFORCEMENT_FLAG_HEALTH /* permissionEnforcementFlag */,
+ true /* permissionEnforcementFlagDefaultValue */,
+ false /* foregroundOnlyPermission */);
+
+ /** Returns the permissions needed for the policy of the health foreground service type. */
+ private static ForegroundServiceTypePermission[] getAllowedHealthPermissions() {
+ final ArrayList<ForegroundServiceTypePermission> permissions = new ArrayList<>();
+ permissions.add(new RegularPermission(Manifest.permission.ACTIVITY_RECOGNITION));
+ permissions.add(new RegularPermission(Manifest.permission.HIGH_SAMPLING_RATE_SENSORS));
+
+ if (android.permission.flags.Flags.replaceBodySensorPermissionEnabled()) {
+ permissions.add(new RegularPermission(HealthPermissions.READ_HEART_RATE));
+ permissions.add(new RegularPermission(HealthPermissions.READ_SKIN_TEMPERATURE));
+ permissions.add(new RegularPermission(HealthPermissions.READ_OXYGEN_SATURATION));
+ } else {
+ permissions.add(new RegularPermission(Manifest.permission.BODY_SENSORS));
+ }
+
+ return permissions.toArray(new ForegroundServiceTypePermission[permissions.size()]);
+ }
/**
* The policy for the {@link ServiceInfo#FOREGROUND_SERVICE_TYPE_REMOTE_MESSAGING}.
diff --git a/core/java/android/app/IActivityClientController.aidl b/core/java/android/app/IActivityClientController.aidl
index 9615015..7a329cd 100644
--- a/core/java/android/app/IActivityClientController.aidl
+++ b/core/java/android/app/IActivityClientController.aidl
@@ -112,8 +112,8 @@
oneway void requestMultiwindowFullscreen(in IBinder token, in int request,
in IRemoteCallback callback);
- oneway void startLockTaskModeByToken(in IBinder token);
- oneway void stopLockTaskModeByToken(in IBinder token);
+ void startLockTaskModeByToken(in IBinder token);
+ void stopLockTaskModeByToken(in IBinder token);
oneway void showLockTaskEscapeMessage(in IBinder token);
void setTaskDescription(in IBinder token, in ActivityManager.TaskDescription values);
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 34a3ad1..0668958 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -358,7 +358,7 @@
@UnsupportedAppUsage
void resumeAppSwitches();
boolean bindBackupAgent(in String packageName, int backupRestoreMode, int targetUserId,
- int backupDestination);
+ int backupDestination, boolean useRestrictedMode);
void backupAgentCreated(in String packageName, in IBinder agent, int userId);
void unbindBackupAgent(in ApplicationInfo appInfo);
int handleIncomingUser(int callingPid, int callingUid, int userId, boolean allowAll,
@@ -864,7 +864,8 @@
/**
* Suppress or reenable the rate limit on foreground service notification deferral.
- * This is for use within CTS and is protected by android.permission.WRITE_DEVICE_CONFIG.
+ * This is for use within CTS and is protected by android.permission.WRITE_DEVICE_CONFIG
+ * and WRITE_ALLOWLISTED_DEVICE_CONFIG.
*
* @param enable false to suppress rate-limit policy; true to reenable it.
*/
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index ec7b72e..c6f62a2 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -242,8 +242,8 @@
boolean supportsLocalVoiceInteraction();
- // Sets whether system educational dialogs should be limited
- void setLimitSystemEducationDialogs(IBinder appToken, boolean limitSystemEducationDialogs);
+ // Requests the "Open in browser" education to be shown
+ void requestOpenInBrowserEducation(IBinder appToken);
// Get device configuration
ConfigurationInfo getDeviceConfigurationInfo();
diff --git a/core/java/android/app/KeyguardManager.java b/core/java/android/app/KeyguardManager.java
index 62820ad..67f7bee 100644
--- a/core/java/android/app/KeyguardManager.java
+++ b/core/java/android/app/KeyguardManager.java
@@ -18,6 +18,7 @@
import android.Manifest;
import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -52,7 +53,9 @@
import android.view.IWindowManager;
import android.view.WindowManagerGlobal;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.policy.IDeviceLockedStateListener;
import com.android.internal.policy.IKeyguardDismissCallback;
import com.android.internal.policy.IKeyguardLockedStateListener;
import com.android.internal.util.Preconditions;
@@ -253,6 +256,26 @@
private final ArrayMap<KeyguardLockedStateListener, Executor>
mKeyguardLockedStateListeners = new ArrayMap<>();
+ private final IDeviceLockedStateListener mIDeviceLockedStateListener =
+ new IDeviceLockedStateListener.Stub() {
+ @Override
+ public void onDeviceLockedStateChanged(boolean isDeviceLocked) {
+ if (!Flags.deviceUnlockListener()) {
+ return;
+ }
+ synchronized (mDeviceLockedStateListeners) {
+ mDeviceLockedStateListeners.forEach((listener, executor) -> {
+ executor.execute(
+ () -> listener.onDeviceLockedStateChanged(isDeviceLocked));
+ });
+ }
+ }
+ };
+
+ @GuardedBy("mDeviceLockedStateListeners")
+ private final ArrayMap<DeviceLockedStateListener, Executor>
+ mDeviceLockedStateListeners = new ArrayMap<>();
+
/**
* Get an intent to prompt the user to confirm credentials (pin, pattern, password or biometrics
* if enrolled) for the current user of the device. The caller is expected to launch this
@@ -1370,4 +1393,77 @@
}
}
}
+
+
+ /**
+ * Listener for device locked state changes.
+ */
+ @FunctionalInterface
+ @FlaggedApi(Flags.FLAG_DEVICE_UNLOCK_LISTENER)
+ public interface DeviceLockedStateListener {
+ /**
+ * Callback function that executes when the device locked state changes.
+ */
+ void onDeviceLockedStateChanged(boolean isDeviceLocked);
+ }
+
+
+ /**
+ * Registers a listener to execute when the device locked state changes.
+ *
+ * @param executor The {@link Executor} where the {@code listener} will be invoked
+ * @param listener The listener to add to receive device locked state changes.
+ *
+ * @see #isDeviceLocked()
+ * @see #removeDeviceLockedStateListener(DeviceLockedStateListener)
+ */
+ @RequiresPermission(Manifest.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE)
+ @FlaggedApi(Flags.FLAG_DEVICE_UNLOCK_LISTENER)
+ public void addDeviceLockedStateListener(@NonNull @CallbackExecutor Executor executor,
+ @NonNull DeviceLockedStateListener listener) {
+ if (!Flags.deviceUnlockListener()) {
+ return;
+ }
+
+ synchronized (mDeviceLockedStateListeners) {
+ mDeviceLockedStateListeners.put(listener, executor);
+ if (mDeviceLockedStateListeners.size() > 1) {
+ return;
+ }
+ try {
+ mTrustManager.registerDeviceLockedStateListener(mIDeviceLockedStateListener,
+ mContext.getDeviceId());
+ } catch (RemoteException re) {
+ Log.d(TAG, "TrustManager service died", re);
+ }
+ }
+ }
+
+ /**
+ * Unregisters a listener that executes when the device locked state changes.
+ *
+ * @param listener The listener to remove.
+ *
+ * @see #isDeviceLocked()
+ * @see #addDeviceLockedStateListener(Executor, DeviceLockedStateListener)
+ */
+ @RequiresPermission(Manifest.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE)
+ @FlaggedApi(Flags.FLAG_DEVICE_UNLOCK_LISTENER)
+ public void removeDeviceLockedStateListener(@NonNull DeviceLockedStateListener listener) {
+ if (!Flags.deviceUnlockListener()) {
+ return;
+ }
+
+ synchronized (mDeviceLockedStateListeners) {
+ mDeviceLockedStateListeners.remove(listener);
+ if (!mDeviceLockedStateListeners.isEmpty()) {
+ return;
+ }
+ try {
+ mTrustManager.unregisterDeviceLockedStateListener(mIDeviceLockedStateListener);
+ } catch (RemoteException re) {
+ Log.d(TAG, "TrustManager service died", re);
+ }
+ }
+ }
}
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 3d9c55c..b78f111 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -811,6 +811,27 @@
}
private static boolean isStandardLayout(int layoutId) {
+ if (Flags.notificationsRedesignTemplates()) {
+ return switch (layoutId) {
+ case R.layout.notification_2025_template_collapsed_base,
+ R.layout.notification_2025_template_heads_up_base,
+ R.layout.notification_2025_template_header,
+ R.layout.notification_template_material_big_base,
+ R.layout.notification_template_material_big_picture,
+ R.layout.notification_template_material_big_text,
+ R.layout.notification_template_material_inbox,
+ R.layout.notification_template_material_messaging,
+ R.layout.notification_template_material_big_messaging,
+ R.layout.notification_template_material_conversation,
+ R.layout.notification_template_material_media,
+ R.layout.notification_template_material_big_media,
+ R.layout.notification_template_material_call,
+ R.layout.notification_template_material_big_call,
+ R.layout.notification_template_header -> true;
+ case R.layout.notification_template_material_progress -> Flags.apiRichOngoing();
+ default -> false;
+ };
+ }
if (Flags.apiRichOngoing()) {
if (layoutId == R.layout.notification_template_material_progress) {
return true;
@@ -3236,7 +3257,7 @@
*/
@FlaggedApi(Flags.FLAG_UI_RICH_ONGOING)
public boolean hasPromotableCharacteristics() {
- return isColorized()
+ return isColorizedRequested()
&& hasTitle()
&& !containsCustomViews()
&& hasPromotableStyle();
@@ -4062,6 +4083,12 @@
flags &= ~FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY;
}
}
+ if (Flags.apiRichOngoing()) {
+ if ((flags & FLAG_PROMOTED_ONGOING) != 0) {
+ flagStrings.add("PROMOTED_ONGOING");
+ flags &= ~FLAG_PROMOTED_ONGOING;
+ }
+ }
if (android.service.notification.Flags.notificationSilentFlag()) {
if ((flags & FLAG_SILENT) != 0) {
@@ -6718,7 +6745,7 @@
// Headers on their own are never colorized
p.disallowColorization();
RemoteViews header = new BuilderRemoteViews(mContext.getApplicationInfo(),
- R.layout.notification_template_header);
+ getHeaderLayoutResource());
resetNotificationHeader(header);
bindNotificationHeader(header, p);
return header;
@@ -7478,13 +7505,29 @@
return clone;
}
+ private int getHeaderLayoutResource() {
+ if (Flags.notificationsRedesignTemplates()) {
+ return R.layout.notification_2025_template_header;
+ } else {
+ return R.layout.notification_template_header;
+ }
+ }
+
@UnsupportedAppUsage
private int getBaseLayoutResource() {
- return R.layout.notification_template_material_base;
+ if (Flags.notificationsRedesignTemplates()) {
+ return R.layout.notification_2025_template_collapsed_base;
+ } else {
+ return R.layout.notification_template_material_base;
+ }
}
private int getHeadsUpBaseLayoutResource() {
- return R.layout.notification_template_material_heads_up_base;
+ if (Flags.notificationsRedesignTemplates()) {
+ return R.layout.notification_2025_template_heads_up_base;
+ } else {
+ return R.layout.notification_template_material_heads_up_base;
+ }
}
private int getCompactHeadsUpBaseLayoutResource() {
@@ -7755,8 +7798,16 @@
* @hide
*/
public boolean isColorized() {
- return extras.getBoolean(EXTRA_COLORIZED)
- && (hasColorizedPermission() || isFgsOrUij());
+ return isColorizedRequested()
+ && (hasColorizedPermission() || isFgsOrUij() || isPromotedOngoing());
+ }
+
+ /**
+ * @return true if this notification has requested to be colorized, regardless of whether it
+ * meets the requirements to be displayed that way.
+ */
+ private boolean isColorizedRequested() {
+ return extras.getBoolean(EXTRA_COLORIZED);
}
/**
@@ -7770,6 +7821,19 @@
}
/**
+ * Returns whether this notification is a promoted ongoing notification.
+ *
+ * This requires the Notification.FLAG_PROMOTED_ONGOING flag to be set
+ * (which may be true once the api_rich_ongoing feature flag is enabled),
+ * and requires that the ui_rich_ongoing feature flag is enabled.
+ *
+ * @hide
+ */
+ public boolean isPromotedOngoing() {
+ return Flags.uiRichOngoing() && (flags & Notification.FLAG_PROMOTED_ONGOING) != 0;
+ }
+
+ /**
* @return true if this is a media style notification with a media session
*
* @hide
@@ -9499,7 +9563,6 @@
contentView.setViewVisibility(R.id.icon, View.GONE);
contentView.setViewVisibility(R.id.conversation_face_pile, View.GONE);
contentView.setViewVisibility(R.id.conversation_icon, View.VISIBLE);
- contentView.setBoolean(R.id.conversation_icon, "setApplyCircularCrop", true);
contentView.setImageViewIcon(R.id.conversation_icon, conversationIcon);
} else if (mIsGroupConversation) {
contentView.setViewVisibility(R.id.icon, View.GONE);
@@ -11527,11 +11590,9 @@
contentView.setBundle(R.id.progress,
"setProgressModel", model.toBundle());
- if (mTrackerIcon != null) {
- contentView.setIcon(R.id.progress,
- "setProgressTrackerIcon",
- mTrackerIcon);
- }
+ contentView.setIcon(R.id.progress,
+ "setProgressTrackerIcon",
+ mTrackerIcon);
return contentView;
}
@@ -11637,8 +11698,10 @@
return points;
}
- @NonNull
- private NotificationProgressModel createProgressModel(int defaultProgressColor,
+ /**
+ * @hide
+ */
+ public @NonNull NotificationProgressModel createProgressModel(int defaultProgressColor,
int backgroundColor) {
final NotificationProgressModel model;
if (mIndeterminate) {
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index c49b022..c310f95 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -16,6 +16,8 @@
package android.app;
+import static android.Manifest.permission.POST_NOTIFICATIONS;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.service.notification.Flags.notificationClassification;
import android.annotation.CallbackExecutor;
@@ -1597,11 +1599,15 @@
* Returns whether notifications from the calling package are enabled.
*/
public boolean areNotificationsEnabled() {
- INotificationManager service = getService();
- try {
- return service.areNotificationsEnabled(mContext.getPackageName());
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ if (Flags.nmBinderPerfPermissionCheck()) {
+ return mContext.checkSelfPermission(POST_NOTIFICATIONS) == PERMISSION_GRANTED;
+ } else {
+ INotificationManager service = getService();
+ try {
+ return service.areNotificationsEnabled(mContext.getPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
}
diff --git a/core/java/android/app/PropertyInvalidatedCache.java b/core/java/android/app/PropertyInvalidatedCache.java
index 1dc7742..e218418 100644
--- a/core/java/android/app/PropertyInvalidatedCache.java
+++ b/core/java/android/app/PropertyInvalidatedCache.java
@@ -314,6 +314,14 @@
@GuardedBy("mLock")
private long mMisses = 0;
+ // This counter tracks the number of times {@link #recompute} returned a null value. Null
+ // results are cached, or not, depending on instantiation arguments. Caching nulls when they
+ // should not be cached is a functional error. Failing to cache nulls that can be cached is a
+ // performance error. A non-zero value here means the cache should be examined to be sure
+ // that nulls are correctly cached, or not.
+ @GuardedBy("mLock")
+ private long mNulls = 0;
+
@GuardedBy("mLock")
private long[] mSkips = new long[MAX_RESERVED_NONCE + 1];
@@ -374,6 +382,11 @@
private final String mCacheName;
/**
+ * True if nulls are valid returns from recompute().
+ */
+ private final boolean mCacheNullResults;
+
+ /**
* The function that computes a Result, given a Query. This function is called on a
* cache miss.
*/
@@ -509,6 +522,19 @@
}
/**
+ * Return true if the entry is in the cache.
+ */
+ boolean containsKey(Query query) {
+ final int uid = callerUid();
+ var map = mCache.get(uid);
+ if (map != null) {
+ return map.containsKey(query);
+ } else {
+ return false;
+ }
+ }
+
+ /**
* Remove an entry from the cache.
*/
void remove(Query query) {
@@ -1101,7 +1127,7 @@
* @hide
*/
public static record Args(@NonNull String mModule, @Nullable String mApi,
- int mMaxEntries, boolean mIsolateUids, boolean mTestMode) {
+ int mMaxEntries, boolean mIsolateUids, boolean mTestMode, boolean mCacheNulls) {
// Validation: the module must be one of the known module strings and the maxEntries must
// be positive.
@@ -1119,24 +1145,29 @@
null, // api
32, // maxEntries
true, // isolateUids
- false // testMode
+ false, // testMode
+ true // allowNulls
);
}
public Args api(@NonNull String api) {
- return new Args(mModule, api, mMaxEntries, mIsolateUids, mTestMode);
+ return new Args(mModule, api, mMaxEntries, mIsolateUids, mTestMode, mCacheNulls);
}
public Args maxEntries(int val) {
- return new Args(mModule, mApi, val, mIsolateUids, mTestMode);
+ return new Args(mModule, mApi, val, mIsolateUids, mTestMode, mCacheNulls);
}
public Args isolateUids(boolean val) {
- return new Args(mModule, mApi, mMaxEntries, val, mTestMode);
+ return new Args(mModule, mApi, mMaxEntries, val, mTestMode, mCacheNulls);
}
public Args testMode(boolean val) {
- return new Args(mModule, mApi, mMaxEntries, mIsolateUids, val);
+ return new Args(mModule, mApi, mMaxEntries, mIsolateUids, val, mCacheNulls);
+ }
+
+ public Args cacheNulls(boolean val) {
+ return new Args(mModule, mApi, mMaxEntries, mIsolateUids, mTestMode, val);
}
}
@@ -1153,6 +1184,7 @@
@Nullable QueryHandler<Query, Result> computer) {
mPropertyName = createPropertyName(args.mModule, args.mApi);
mCacheName = cacheName;
+ mCacheNullResults = args.mCacheNulls && Flags.picCacheNulls();
mNonce = getNonceHandler(mPropertyName);
mMaxEntries = args.mMaxEntries;
mCache = new CacheMap<>(args.mIsolateUids, args.mTestMode);
@@ -1491,12 +1523,24 @@
}
return recompute(query);
}
+
+ final boolean cacheHit;
final Result cachedResult;
synchronized (mLock) {
if (currentNonce == mLastSeenNonce) {
cachedResult = mCache.get(query);
-
- if (cachedResult != null) mHits++;
+ if (cachedResult == null) {
+ if (mCacheNullResults) {
+ cacheHit = mCache.containsKey(query);
+ } else {
+ cacheHit = false;
+ }
+ } else {
+ cacheHit = true;
+ }
+ if (cacheHit) {
+ mHits++;
+ }
} else {
if (DEBUG) {
Log.d(TAG, formatSimple(
@@ -1506,16 +1550,18 @@
}
clear();
mLastSeenNonce = currentNonce;
+ cacheHit = false;
cachedResult = null;
}
}
+
// Cache hit --- but we're not quite done yet. A value in the cache might need to
// be augmented in a "refresh" operation. The refresh operation can combine the
// old and the new nonce values. In order to make sure the new parts of the value
// are consistent with the old, possibly-reused parts, we check the property value
// again after the refresh and do the whole fetch again if the property invalidated
// us while we were refreshing.
- if (cachedResult != null) {
+ if (cacheHit) {
final Result refreshedResult = refresh(cachedResult, query);
if (refreshedResult != cachedResult) {
if (DEBUG) {
@@ -1550,6 +1596,7 @@
}
return maybeCheckConsistency(query, cachedResult);
}
+
// Cache miss: make the value from scratch.
if (DEBUG) {
Log.d(TAG, "cache miss for " + cacheName() + " " + queryToString(query));
@@ -1558,8 +1605,13 @@
synchronized (mLock) {
// If someone else invalidated the cache while we did the recomputation, don't
// update the cache with a potentially stale result.
- if (mLastSeenNonce == currentNonce && result != null) {
- mCache.put(query, result);
+ if (mLastSeenNonce == currentNonce) {
+ if (result != null || mCacheNullResults) {
+ mCache.put(query, result);
+ }
+ if (result == null) {
+ mNulls++;
+ }
}
mMisses++;
}
@@ -1947,10 +1999,12 @@
}
// Return true if this cache has had any activity. If the hits, misses, and skips are all
- // zero then the client never tried to use the cache.
- private boolean isActive() {
+ // zero then the client never tried to use the cache. If invalidations and corks are also
+ // zero then the server never tried to use the cache.
+ private boolean isActive(NonceHandler.Stats stats) {
synchronized (mLock) {
- return mHits + mMisses + getSkipsLocked() > 0;
+ return mHits + mMisses + getSkipsLocked()
+ + stats.invalidated + stats.corkedInvalidates > 0;
}
}
@@ -1968,15 +2022,15 @@
NonceHandler.Stats stats = mNonce.getStats();
synchronized (mLock) {
- if (brief && !isActive()) {
+ if (brief && !isActive(stats)) {
return;
}
pw.println(formatSimple(" Cache Name: %s", cacheName()));
pw.println(formatSimple(" Property: %s", mPropertyName));
pw.println(formatSimple(
- " Hits: %d, Misses: %d, Skips: %d, Clears: %d, Uids: %d",
- mHits, mMisses, getSkipsLocked(), mClears, mCache.size()));
+ " Hits: %d, Misses: %d, Skips: %d, Clears: %d, Nulls: %d",
+ mHits, mMisses, getSkipsLocked(), mClears, mNulls));
// Print all the skip reasons.
pw.format(" Skip-%s: %d", sNonceName[0], mSkips[0]);
@@ -1986,7 +2040,7 @@
pw.println();
pw.println(formatSimple(
- " Nonce: 0x%016x, Invalidates: %d, CorkedInvalidates: %d",
+ " Nonce: 0x%016x, Invalidates: %d, Corked: %d",
mLastSeenNonce, stats.invalidated, stats.corkedInvalidates));
pw.println(formatSimple(
" Current Size: %d, Max Size: %d, HW Mark: %d, Overflows: %d",
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index 087e246..3cffca7 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -206,11 +206,15 @@
assets.addPresetApkKeys(extractApkKeys(collector.collectedKey()));
return new Pair<>(assets, size);
}
- final var newAssetsBuilder = new AssetManager.Builder();
+ final var newAssetsBuilder = new AssetManager.Builder().setNoInit();
for (final var asset : assets.getApkAssets()) {
- if (!asset.isForLoader()) {
- newAssetsBuilder.addApkAssets(asset);
+ // Skip everything that's either default, or will get added by the collector (builder
+ // doesn't check for duplicates at all).
+ if (asset.isSystem() || asset.isForLoader() || asset.isOverlay()
+ || asset.isSharedLib()) {
+ continue;
}
+ newAssetsBuilder.addApkAssets(asset);
}
for (final var key : extractApkKeys(collector.collectedKey())) {
try {
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index e451116..6a23349 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -17,6 +17,7 @@
package android.app;
import static android.app.appfunctions.flags.Flags.enableAppFunctionManager;
+import static android.provider.flags.Flags.stageFlagsForBuild;
import static android.server.Flags.removeGameManagerServiceFromWear;
import android.accounts.AccountManager;
@@ -163,6 +164,7 @@
import android.media.tv.tunerresourcemanager.TunerResourceManager;
import android.nearby.NearbyFrameworkInitializer;
import android.net.ConnectivityFrameworkInitializer;
+import android.net.ConnectivityFrameworkInitializerBaklava;
import android.net.ConnectivityFrameworkInitializerTiramisu;
import android.net.INetworkPolicyManager;
import android.net.IPacProxyManager;
@@ -173,7 +175,6 @@
import android.net.PacProxyManager;
import android.net.TetheringManager;
import android.net.VpnManager;
-import android.net.vcn.VcnFrameworkInitializer;
import android.net.wifi.WifiFrameworkInitializer;
import android.net.wifi.nl80211.WifiNl80211Manager;
import android.net.wifi.sharedconnectivity.app.SharedConnectivityManager;
@@ -190,6 +191,7 @@
import android.os.IBinder;
import android.os.IDumpstate;
import android.os.IHardwarePropertiesManager;
+import android.os.IHintManager;
import android.os.IPowerManager;
import android.os.IPowerStatsService;
import android.os.IRecoverySystem;
@@ -215,6 +217,7 @@
import android.os.UserManager;
import android.os.Vibrator;
import android.os.VibratorManager;
+import android.os.flagging.ConfigInfrastructureFrameworkInitializer;
import android.os.health.SystemHealthManager;
import android.os.image.DynamicSystemManager;
import android.os.image.IDynamicSystemService;
@@ -238,6 +241,10 @@
import android.security.advancedprotection.IAdvancedProtectionService;
import android.security.attestationverification.AttestationVerificationManager;
import android.security.attestationverification.IAttestationVerificationManagerService;
+import android.security.authenticationpolicy.AuthenticationPolicyManager;
+import android.security.authenticationpolicy.IAuthenticationPolicyService;
+import android.security.forensic.ForensicManager;
+import android.security.forensic.IForensicService;
import android.security.keystore.KeyStoreManager;
import android.service.oemlock.IOemLockService;
import android.service.oemlock.OemLockManager;
@@ -1020,6 +1027,25 @@
}
});
+ registerService(Context.AUTHENTICATION_POLICY_SERVICE,
+ AuthenticationPolicyManager.class,
+ new CachedServiceFetcher<AuthenticationPolicyManager>() {
+ @Override
+ public AuthenticationPolicyManager createService(ContextImpl ctx)
+ throws ServiceNotFoundException {
+ if (!android.security.Flags.secureLockdown()) {
+ throw new ServiceNotFoundException(
+ Context.AUTHENTICATION_POLICY_SERVICE);
+ }
+
+ final IBinder binder = ServiceManager.getServiceOrThrow(
+ Context.AUTHENTICATION_POLICY_SERVICE);
+ final IAuthenticationPolicyService service =
+ IAuthenticationPolicyService.Stub.asInterface(binder);
+ return new AuthenticationPolicyManager(ctx.getOuterContext(), service);
+ }
+ });
+
registerService(Context.TV_INTERACTIVE_APP_SERVICE, TvInteractiveAppManager.class,
new CachedServiceFetcher<TvInteractiveAppManager>() {
@Override
@@ -1195,8 +1221,10 @@
public SystemHealthManager createService(ContextImpl ctx) throws ServiceNotFoundException {
IBinder batteryStats = ServiceManager.getServiceOrThrow(BatteryStats.SERVICE_NAME);
IBinder powerStats = ServiceManager.getService(Context.POWER_STATS_SERVICE);
+ IBinder perfHint = ServiceManager.getService(Context.PERFORMANCE_HINT_SERVICE);
return new SystemHealthManager(IBatteryStats.Stub.asInterface(batteryStats),
- IPowerStatsService.Stub.asInterface(powerStats));
+ IPowerStatsService.Stub.asInterface(powerStats),
+ IHintManager.Stub.asInterface(perfHint));
}});
registerService(Context.CONTEXTHUB_SERVICE, ContextHubManager.class,
@@ -1790,6 +1818,18 @@
}
});
+ registerService(Context.FORENSIC_SERVICE, ForensicManager.class,
+ new CachedServiceFetcher<ForensicManager>() {
+ @Override
+ public ForensicManager createService(ContextImpl ctx)
+ throws ServiceNotFoundException {
+ IBinder b = ServiceManager.getServiceOrThrow(
+ Context.FORENSIC_SERVICE);
+ IForensicService service = IForensicService.Stub.asInterface(b);
+ return new ForensicManager(service);
+ }
+ });
+
sInitializing = true;
try {
// Note: the following functions need to be @SystemApis, once they become mainline
@@ -1818,7 +1858,11 @@
OnDevicePersonalizationFrameworkInitializer.registerServiceWrappers();
DeviceLockFrameworkInitializer.registerServiceWrappers();
VirtualizationFrameworkInitializer.registerServiceWrappers();
- VcnFrameworkInitializer.registerServiceWrappers();
+ ConnectivityFrameworkInitializerBaklava.registerServiceWrappers();
+
+ if (stageFlagsForBuild()) {
+ ConfigInfrastructureFrameworkInitializer.registerServiceWrappers();
+ }
if (com.android.server.telecom.flags.Flags.telecomMainlineBlockedNumbersManager()) {
ProviderFrameworkInitializer.registerServiceWrappers();
diff --git a/core/java/android/app/TaskInfo.java b/core/java/android/app/TaskInfo.java
index aac963a..01cc9d8 100644
--- a/core/java/android/app/TaskInfo.java
+++ b/core/java/android/app/TaskInfo.java
@@ -340,10 +340,10 @@
public int requestedVisibleTypes;
/**
- * Whether the top activity has requested to limit educational dialogs shown by the system.
+ * The timestamp of the top activity's last request to show the "Open in Browser" education.
* @hide
*/
- public boolean isTopActivityLimitSystemEducationDialogs;
+ public long topActivityRequestOpenInBrowserEducationTimestamp;
/**
* Encapsulate specific App Compat information.
@@ -493,8 +493,8 @@
&& Objects.equals(capturedLink, that.capturedLink)
&& capturedLinkTimestamp == that.capturedLinkTimestamp
&& requestedVisibleTypes == that.requestedVisibleTypes
- && isTopActivityLimitSystemEducationDialogs
- == that.isTopActivityLimitSystemEducationDialogs
+ && topActivityRequestOpenInBrowserEducationTimestamp
+ == that.topActivityRequestOpenInBrowserEducationTimestamp
&& appCompatTaskInfo.equalsForTaskOrganizer(that.appCompatTaskInfo)
&& Objects.equals(topActivityMainWindowFrame, that.topActivityMainWindowFrame);
}
@@ -571,7 +571,7 @@
capturedLink = source.readTypedObject(Uri.CREATOR);
capturedLinkTimestamp = source.readLong();
requestedVisibleTypes = source.readInt();
- isTopActivityLimitSystemEducationDialogs = source.readBoolean();
+ topActivityRequestOpenInBrowserEducationTimestamp = source.readLong();
appCompatTaskInfo = source.readTypedObject(AppCompatTaskInfo.CREATOR);
topActivityMainWindowFrame = source.readTypedObject(Rect.CREATOR);
}
@@ -627,7 +627,7 @@
dest.writeTypedObject(capturedLink, flags);
dest.writeLong(capturedLinkTimestamp);
dest.writeInt(requestedVisibleTypes);
- dest.writeBoolean(isTopActivityLimitSystemEducationDialogs);
+ dest.writeLong(topActivityRequestOpenInBrowserEducationTimestamp);
dest.writeTypedObject(appCompatTaskInfo, flags);
dest.writeTypedObject(topActivityMainWindowFrame, flags);
}
@@ -672,8 +672,8 @@
+ " capturedLink=" + capturedLink
+ " capturedLinkTimestamp=" + capturedLinkTimestamp
+ " requestedVisibleTypes=" + requestedVisibleTypes
- + " isTopActivityLimitSystemEducationDialogs="
- + isTopActivityLimitSystemEducationDialogs
+ + " topActivityRequestOpenInBrowserEducationTimestamp="
+ + topActivityRequestOpenInBrowserEducationTimestamp
+ " appCompatTaskInfo=" + appCompatTaskInfo
+ " topActivityMainWindowFrame=" + topActivityMainWindowFrame
+ "}";
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index c789c41..e766ae2 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -16,6 +16,7 @@
package android.app.admin;
+import static android.app.admin.flags.Flags.FLAG_SPLIT_CREATE_MANAGED_PROFILE_ENABLED;
import static android.Manifest.permission.INTERACT_ACROSS_USERS;
import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
import static android.Manifest.permission.LOCK_DEVICE;
@@ -12196,6 +12197,33 @@
}
/**
+ * Adds a user restriction globally, specified by the {@code key}.
+ *
+ * <p>Called by a system service only, meaning that the caller's UID must be equal to
+ * {@link Process#SYSTEM_UID}.
+ *
+ * @param systemEntity The service entity that adds the restriction. A user restriction set by
+ * a service entity can only be cleared by the same entity. This can be
+ * just the calling package name, or any string of the caller's choice
+ * can be used.
+ * @param key The key of the restriction.
+ * @throws SecurityException if the caller is not a system service.
+ *
+ * @hide
+ */
+ public void addUserRestrictionGlobally(@NonNull String systemEntity,
+ @NonNull @UserManager.UserRestrictionKey String key) {
+ if (mService != null) {
+ try {
+ mService.setUserRestrictionGloballyFromSystem(systemEntity, key,
+ /* enable= */ true);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ /**
* Called by a profile owner, device owner or a holder of any permission that is associated with
* a user restriction to clear a user restriction specified by the key.
* <p>
@@ -12281,6 +12309,33 @@
}
/**
+ * Clears a user restriction globally, specified by the {@code key}.
+ *
+ * <p>Called by a system service only, meaning that the caller's UID must be equal to
+ * {@link Process#SYSTEM_UID}.
+ *
+ * @param systemEntity The system entity that clears the restriction. A user restriction
+ * set by a system entity can only be cleared by the same entity. This
+ * can be just the calling package name, or any string of the caller's
+ * choice can be used.
+ * @param key The key of the restriction.
+ * @throws SecurityException if the caller is not a system service.
+ *
+ * @hide
+ */
+ public void clearUserRestrictionGlobally(@NonNull String systemEntity,
+ @NonNull @UserManager.UserRestrictionKey String key) {
+ if (mService != null) {
+ try {
+ mService.setUserRestrictionGloballyFromSystem(systemEntity, key,
+ /* enable= */ false);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ /**
* Called by an admin to get user restrictions set by themselves with
* {@link #addUserRestriction(ComponentName, String)}.
* <p>
@@ -17142,11 +17197,14 @@
* @throws SecurityException if the caller does not hold
* {@link android.Manifest.permission#MANAGE_PROFILE_AND_DEVICE_OWNERS}.
* @throws ProvisioningException if an error occurred during provisioning.
+ * @deprecated Use {@link #createManagedProfile} and {@link #finalizeCreateManagedProfile}
* @hide
*/
@Nullable
@SystemApi
+ @Deprecated
@RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS)
+ @FlaggedApi(FLAG_SPLIT_CREATE_MANAGED_PROFILE_ENABLED)
public UserHandle createAndProvisionManagedProfile(
@NonNull ManagedProfileProvisioningParams provisioningParams)
throws ProvisioningException {
@@ -17164,6 +17222,69 @@
}
/**
+ * Creates a managed profile and sets the
+ * {@link ManagedProfileProvisioningParams#getProfileAdminComponentName()} as the profile
+ * owner. The method {@link #finalizeCreateManagedProfile} must be called after to finalize the
+ * creation of the managed profile.
+ *
+ * <p>The method {@link #checkProvisioningPrecondition} must return {@link #STATUS_OK}
+ * before calling this method. If it doesn't, a ProvisioningException will be thrown.
+ *
+ * @param provisioningParams Params required to provision a managed profile,
+ * see {@link ManagedProfileProvisioningParams}.
+ * @return The {@link UserHandle} of the created profile or {@code null} if the service is
+ * not available.
+ * @throws ProvisioningException if an error occurred during provisioning.
+ * @hide
+ */
+ @Nullable
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS)
+ @FlaggedApi(FLAG_SPLIT_CREATE_MANAGED_PROFILE_ENABLED)
+ public UserHandle createManagedProfile(
+ @NonNull ManagedProfileProvisioningParams provisioningParams)
+ throws ProvisioningException {
+ if (mService == null) {
+ return null;
+ }
+ try {
+ return mService.createManagedProfile(provisioningParams, mContext.getPackageName());
+ } catch (ServiceSpecificException e) {
+ throw new ProvisioningException(e, e.errorCode, getErrorMessage(e));
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Finalizes the creation of a managed profile by informing the necessary components that
+ * the managed profile is ready.
+ *
+ * @param provisioningParams Params required to provision a managed profile,
+ * see {@link ManagedProfileProvisioningParams}.
+ * @param managedProfileUser The recently created managed profile.
+ * @throws ProvisioningException if an error occurred during provisioning.
+ * @hide
+ */
+ @SuppressLint("UserHandle")
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS)
+ @FlaggedApi(FLAG_SPLIT_CREATE_MANAGED_PROFILE_ENABLED)
+ public void finalizeCreateManagedProfile(
+ @NonNull ManagedProfileProvisioningParams provisioningParams,
+ @NonNull UserHandle managedProfileUser)
+ throws ProvisioningException {
+ if (mService == null) {
+ return;
+ }
+ try {
+ mService.finalizeCreateManagedProfile(provisioningParams, managedProfileUser);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Removes a manged profile from the device only when called from a managed profile's context
*
* @param user UserHandle of the profile to be removed
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index a406802..d048b53 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -257,6 +257,7 @@
void setUserRestriction(in ComponentName who, in String callerPackage, in String key, boolean enable, boolean parent);
void setUserRestrictionForUser(in String systemEntity, in String key, boolean enable, int targetUser);
void setUserRestrictionGlobally(in String callerPackage, in String key);
+ void setUserRestrictionGloballyFromSystem(in String systemEntity, in String key, boolean enable);
Bundle getUserRestrictions(in ComponentName who, in String callerPackage, boolean parent);
Bundle getUserRestrictionsGlobally(in String callerPackage);
@@ -569,6 +570,8 @@
void setOrganizationIdForUser(in String callerPackage, in String enterpriseId, int userId);
UserHandle createAndProvisionManagedProfile(in ManagedProfileProvisioningParams provisioningParams, in String callerPackage);
+ UserHandle createManagedProfile(in ManagedProfileProvisioningParams provisioningParams, in String callerPackage);
+ void finalizeCreateManagedProfile(in ManagedProfileProvisioningParams provisioningParams, in UserHandle managedProfileUser);
void provisionFullyManagedDevice(in FullyManagedDeviceProvisioningParams provisioningParams, in String callerPackage);
void finalizeWorkProfileProvisioning(in UserHandle managedProfileUser, in Account migratedAccount);
diff --git a/core/java/android/app/admin/flags/flags.aconfig b/core/java/android/app/admin/flags/flags.aconfig
index 404471e..581efa5 100644
--- a/core/java/android/app/admin/flags/flags.aconfig
+++ b/core/java/android/app/admin/flags/flags.aconfig
@@ -343,6 +343,16 @@
}
flag {
+ name: "active_admin_cleanup"
+ namespace: "enterprise"
+ description: "Remove ActiveAdmin from EnforcingAdmin and related cleanups"
+ bug: "335663055"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "user_provisioning_same_state"
namespace: "enterprise"
description: "Handle exceptions while setting same provisioning state."
@@ -372,7 +382,7 @@
is_exported: true
namespace: "enterprise"
description: "Allows DPMS to enable or disable SupervisionService based on whether the device is being managed by the supervision role holder."
- bug: "376213673"
+ bug: "358134581"
}
flag {
diff --git a/core/java/android/app/backup/BackupTransport.java b/core/java/android/app/backup/BackupTransport.java
index dcac59c..5004c02 100644
--- a/core/java/android/app/backup/BackupTransport.java
+++ b/core/java/android/app/backup/BackupTransport.java
@@ -16,6 +16,8 @@
package android.app.backup;
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.content.Intent;
@@ -27,6 +29,7 @@
import com.android.internal.backup.IBackupTransport;
import com.android.internal.backup.ITransportStatusCallback;
import com.android.internal.infra.AndroidFuture;
+import com.android.server.backup.Flags;
import java.util.Arrays;
import java.util.List;
@@ -671,6 +674,22 @@
}
/**
+ * Ask the transport whether packages that are about to be backed up or restored should not be
+ * put into a restricted mode by the framework and started normally instead.
+ *
+ * @param operationType 0 for backup, 1 for restore.
+ * @return a subset of the {@code packageNames} passed in, indicating
+ * which packages should NOT be put into restricted mode for the given operation type.
+ */
+ @NonNull
+ @FlaggedApi(Flags.FLAG_ENABLE_RESTRICTED_MODE_CHANGES)
+ public List<String> getPackagesThatShouldNotUseRestrictedMode(
+ @NonNull List<String> packageNames,
+ @BackupAnnotations.OperationType int operationType) {
+ return List.of();
+ }
+
+ /**
* Bridge between the actual IBackupTransport implementation and the stable API. If the
* binder interface needs to change, we use this layer to translate so that we can
* (if appropriate) decouple those framework-side changes from the BackupTransport
@@ -977,5 +996,19 @@
resultFuture.cancel(/* mayInterruptIfRunning */ true);
}
}
+
+ @Override
+ @FlaggedApi(Flags.FLAG_ENABLE_RESTRICTED_MODE_CHANGES)
+ public void getPackagesThatShouldNotUseRestrictedMode(List<String> packageNames,
+ int operationType, AndroidFuture<List<String>> resultFuture) {
+ try {
+ List<String> result =
+ BackupTransport.this.getPackagesThatShouldNotUseRestrictedMode(packageNames,
+ operationType);
+ resultFuture.complete(result);
+ } catch (RuntimeException e) {
+ resultFuture.cancel(/* mayInterruptIfRunning */ true);
+ }
+ }
}
}
diff --git a/core/java/android/app/jank/FrameOverrunHistogram.java b/core/java/android/app/jank/FrameOverrunHistogram.java
index e28ac12..3ad6531 100644
--- a/core/java/android/app/jank/FrameOverrunHistogram.java
+++ b/core/java/android/app/jank/FrameOverrunHistogram.java
@@ -39,7 +39,7 @@
* Create a new instance of FrameOverrunHistogram.
*/
public FrameOverrunHistogram() {
- mBucketCounts = new int[sBucketEndpoints.length - 1];
+ mBucketCounts = new int[sBucketEndpoints.length];
}
/**
diff --git a/core/java/android/app/jank/JankDataProcessor.java b/core/java/android/app/jank/JankDataProcessor.java
index 7525d04..7ceaeb3 100644
--- a/core/java/android/app/jank/JankDataProcessor.java
+++ b/core/java/android/app/jank/JankDataProcessor.java
@@ -59,7 +59,6 @@
* @param appUid the uid of the app.
*/
public void processJankData(List<JankData> jankData, String activityName, int appUid) {
- mCurrentBatchCount++;
// add all the previous and active states to the pending states list.
mStateTracker.retrieveAllStates(mPendingStates);
@@ -79,9 +78,8 @@
}
}
// At this point we have attributed all frames to a state.
- if (mCurrentBatchCount >= LOG_BATCH_FREQUENCY) {
- logMetricCounts();
- }
+ incrementBatchCountAndMaybeLogStats();
+
// return the StatData object back to the pool to be reused.
jankDataProcessingComplete();
}
@@ -91,7 +89,73 @@
* stats
*/
public void mergeJankStats(AppJankStats jankStats, String activityName) {
- // TODO b/377572463 Add Merging Logic
+ // Each state has a key which is a combination of widget category, widget id and widget
+ // state, this key is also used to identify pending stats, a pending stat is essentially a
+ // state with frames associated with it.
+ String stateKey = mStateTracker.getStateKey(jankStats.getWidgetCategory(),
+ jankStats.getWidgetId(), jankStats.getWidgetState());
+
+ if (mPendingJankStats.containsKey(stateKey)) {
+ mergeExistingStat(stateKey, jankStats);
+ } else {
+ mergeNewStat(stateKey, activityName, jankStats);
+ }
+
+ incrementBatchCountAndMaybeLogStats();
+ }
+
+ private void mergeExistingStat(String stateKey, AppJankStats jankStat) {
+ PendingJankStat pendingStat = mPendingJankStats.get(stateKey);
+
+ pendingStat.mJankyFrames += jankStat.getJankyFrameCount();
+ pendingStat.mTotalFrames += jankStat.getTotalFrameCount();
+
+ mergeOverrunHistograms(pendingStat.mFrameOverrunBuckets,
+ jankStat.getFrameOverrunHistogram().getBucketCounters());
+ }
+
+ private void mergeNewStat(String stateKey, String activityName, AppJankStats jankStats) {
+ // Check if we have space for a new stat
+ if (mPendingJankStats.size() > MAX_IN_MEMORY_STATS) {
+ return;
+ }
+
+ PendingJankStat pendingStat = mPendingJankStatsPool.acquire();
+ if (pendingStat == null) {
+ pendingStat = new PendingJankStat();
+
+ }
+ pendingStat.clearStats();
+
+ pendingStat.mActivityName = activityName;
+ pendingStat.mUid = jankStats.getUid();
+ pendingStat.mWidgetId = jankStats.getWidgetId();
+ pendingStat.mWidgetCategory = jankStats.getWidgetCategory();
+ pendingStat.mWidgetState = jankStats.getWidgetState();
+ pendingStat.mTotalFrames = jankStats.getTotalFrameCount();
+ pendingStat.mJankyFrames = jankStats.getJankyFrameCount();
+
+ mergeOverrunHistograms(pendingStat.mFrameOverrunBuckets,
+ jankStats.getFrameOverrunHistogram().getBucketCounters());
+
+ mPendingJankStats.put(stateKey, pendingStat);
+ }
+
+ private void mergeOverrunHistograms(int[] mergeTarget, int[] mergeSource) {
+ // The length of each histogram should be identical, if they are not then its possible the
+ // buckets are not in sync, these records should not be recorded.
+ if (mergeTarget.length != mergeSource.length) return;
+
+ for (int i = 0; i < mergeTarget.length; i++) {
+ mergeTarget[i] += mergeSource[i];
+ }
+ }
+
+ private void incrementBatchCountAndMaybeLogStats() {
+ mCurrentBatchCount++;
+ if (mCurrentBatchCount >= LOG_BATCH_FREQUENCY) {
+ logMetricCounts();
+ }
}
/**
diff --git a/core/java/android/app/jank/JankTracker.java b/core/java/android/app/jank/JankTracker.java
index 202281f..4695216 100644
--- a/core/java/android/app/jank/JankTracker.java
+++ b/core/java/android/app/jank/JankTracker.java
@@ -89,7 +89,12 @@
* stats
*/
public void mergeAppJankStats(AppJankStats appJankStats) {
- mJankDataProcessor.mergeJankStats(appJankStats, mActivityName);
+ getHandler().post(new Runnable() {
+ @Override
+ public void run() {
+ mJankDataProcessor.mergeJankStats(appJankStats, mActivityName);
+ }
+ });
}
public void setActivityName(@NonNull String activityName) {
diff --git a/core/java/android/app/jank/StateTracker.java b/core/java/android/app/jank/StateTracker.java
index c86d5a5..21bb5e8 100644
--- a/core/java/android/app/jank/StateTracker.java
+++ b/core/java/android/app/jank/StateTracker.java
@@ -180,7 +180,11 @@
}
}
- private String getStateKey(String widgetCategory, String widgetId, String widgetState) {
+ /**
+ * Returns a concatenated string of the inputs. This key can be used to retrieve both pending
+ * stats and the state that was used to create the pending stat.
+ */
+ public String getStateKey(String widgetCategory, String widgetId, String widgetState) {
return widgetCategory + widgetId + widgetState;
}
diff --git a/core/java/android/app/keyguard.aconfig b/core/java/android/app/keyguard.aconfig
new file mode 100644
index 0000000..9cd1c15
--- /dev/null
+++ b/core/java/android/app/keyguard.aconfig
@@ -0,0 +1,10 @@
+package: "android.app"
+container: "system"
+
+flag {
+ namespace: "wallet_integration"
+ name: "device_unlock_listener"
+ is_exported: true
+ description: "Enable listener API for device unlock."
+ bug: "296195355"
+}
\ No newline at end of file
diff --git a/core/java/android/app/notification.aconfig b/core/java/android/app/notification.aconfig
index 6934e98..8b6840c 100644
--- a/core/java/android/app/notification.aconfig
+++ b/core/java/android/app/notification.aconfig
@@ -13,6 +13,13 @@
}
flag {
+ name: "notifications_redesign_templates"
+ namespace: "systemui"
+ description: "Notifications Redesign: Update notification templates"
+ bug: "378660052"
+}
+
+flag {
name: "modes_api"
is_exported: true
namespace: "systemui"
@@ -268,3 +275,17 @@
description: "Adds UI for NAS classification of notifications"
bug: "367996732"
}
+
+flag {
+ name: "nm_binder_perf_permission_check"
+ namespace: "systemui"
+ description: "Use PermissionManager for areNotificationsEnabled() instead of NMS"
+ bug: "362981561"
+}
+
+flag {
+ name: "no_sbnholder"
+ namespace: "systemui"
+ description: "removes sbnholder from NLS"
+ bug: "362981561"
+}
diff --git a/core/java/android/app/performance.aconfig b/core/java/android/app/performance.aconfig
index 61b53f9..359c84e 100644
--- a/core/java/android/app/performance.aconfig
+++ b/core/java/android/app/performance.aconfig
@@ -35,3 +35,10 @@
bug: "373752556"
}
+flag {
+ namespace: "system_performance"
+ name: "pic_cache_nulls"
+ is_fixed_read_only: true
+ description: "Cache null returns from binder calls"
+ bug: "372923336"
+}
diff --git a/services/supervision/java/com/android/server/supervision/SupervisionManagerInternal.java b/core/java/android/app/supervision/SupervisionManagerInternal.java
similarity index 84%
rename from services/supervision/java/com/android/server/supervision/SupervisionManagerInternal.java
rename to core/java/android/app/supervision/SupervisionManagerInternal.java
index 5df9dd5..d571e14 100644
--- a/services/supervision/java/com/android/server/supervision/SupervisionManagerInternal.java
+++ b/core/java/android/app/supervision/SupervisionManagerInternal.java
@@ -14,11 +14,11 @@
* limitations under the License.
*/
-package com.android.server.supervision;
+package android.app.supervision;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
-import android.os.Bundle;
+import android.os.PersistableBundle;
/**
* Local system service interface for {@link SupervisionService}.
@@ -35,6 +35,11 @@
public abstract boolean isSupervisionEnabledForUser(@UserIdInt int userId);
/**
+ * Returns whether the supervision lock screen needs to be shown.
+ */
+ public abstract boolean isSupervisionLockscreenEnabledForUser(@UserIdInt int userId);
+
+ /**
* Set whether supervision is enabled for the specified user.
*
* @param userId The user to set the supervision state for
@@ -50,5 +55,5 @@
* @param options Optional configuration parameters for the supervision lock screen
*/
public abstract void setSupervisionLockscreenEnabledForUser(
- @UserIdInt int userId, boolean enabled, @Nullable Bundle options);
+ @UserIdInt int userId, boolean enabled, @Nullable PersistableBundle options);
}
diff --git a/core/java/android/app/trust/ITrustManager.aidl b/core/java/android/app/trust/ITrustManager.aidl
index 730bb73..ffa5488 100644
--- a/core/java/android/app/trust/ITrustManager.aidl
+++ b/core/java/android/app/trust/ITrustManager.aidl
@@ -18,6 +18,7 @@
import android.app.trust.ITrustListener;
import android.hardware.biometrics.BiometricSourceType;
+import com.android.internal.policy.IDeviceLockedStateListener;
/**
* System private API to comunicate with trust service.
@@ -43,4 +44,8 @@
boolean isActiveUnlockRunning(int userId);
@EnforcePermission("ACCESS_FINE_LOCATION")
boolean isInSignificantPlace();
+ @EnforcePermission("SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE")
+ void registerDeviceLockedStateListener(in IDeviceLockedStateListener listener, int deviceId);
+ @EnforcePermission("SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE")
+ void unregisterDeviceLockedStateListener(in IDeviceLockedStateListener listener);
}
diff --git a/core/java/android/app/trust/TrustManager.java b/core/java/android/app/trust/TrustManager.java
index 1ef83cd..8c8970e 100644
--- a/core/java/android/app/trust/TrustManager.java
+++ b/core/java/android/app/trust/TrustManager.java
@@ -31,6 +31,8 @@
import android.os.RemoteException;
import android.util.ArrayMap;
+import com.android.internal.policy.IDeviceLockedStateListener;
+
import java.util.ArrayList;
import java.util.List;
@@ -259,6 +261,35 @@
}
/**
+ * Registers a listener for device lock state events.
+ *
+ * Requires the {@link android.Manifest.permission#SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE}
+ * permission.
+ */
+ public void registerDeviceLockedStateListener(final IDeviceLockedStateListener listener,
+ int deviceId) {
+ try {
+ mService.registerDeviceLockedStateListener(listener, deviceId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Unregisters a listener for device lock state events.
+ *
+ * Requires the {@link android.Manifest.permission#SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE}
+ * permission.
+ */
+ public void unregisterDeviceLockedStateListener(final IDeviceLockedStateListener listener) {
+ try {
+ mService.unregisterDeviceLockedStateListener(listener);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* @return whether {@param userId} has enabled and configured trust agents. Ignores short-term
* unavailability of trust due to {@link LockPatternUtils.StrongAuthTracker}.
*/
diff --git a/core/java/android/appwidget/flags.aconfig b/core/java/android/appwidget/flags.aconfig
index ce51576..fb33348 100644
--- a/core/java/android/appwidget/flags.aconfig
+++ b/core/java/android/appwidget/flags.aconfig
@@ -92,3 +92,13 @@
is_exported: true
is_fixed_read_only: true
}
+
+flag {
+ name: "check_remote_views_uri_permission"
+ namespace: "app_widgets"
+ description: "Check that the widget provider has permissions to access any URIs within its RemoteViews"
+ bug: "369137473"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/core/java/android/companion/virtual/flags/flags.aconfig b/core/java/android/companion/virtual/flags/flags.aconfig
index c47fe23..de01280 100644
--- a/core/java/android/companion/virtual/flags/flags.aconfig
+++ b/core/java/android/companion/virtual/flags/flags.aconfig
@@ -148,6 +148,14 @@
flag {
namespace: "virtual_devices"
+ name: "notifications_for_device_streaming"
+ description: "Add notifications permissions to device streaming role"
+ bug: "375240276"
+ is_exported: true
+}
+
+flag {
+ namespace: "virtual_devices"
name: "default_device_camera_access_policy"
description: "API for default device camera access policy"
bug: "371173368"
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 6086f24..19cd2e6 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -18,6 +18,7 @@
import static android.app.appfunctions.flags.Flags.FLAG_ENABLE_APP_FUNCTION_MANAGER;
import static android.content.flags.Flags.FLAG_ENABLE_BIND_PACKAGE_ISOLATED_PROCESS;
+import static android.security.Flags.FLAG_SECURE_LOCKDOWN;
import android.annotation.AttrRes;
import android.annotation.CallbackExecutor;
@@ -4256,6 +4257,7 @@
FINGERPRINT_SERVICE,
//@hide: FACE_SERVICE,
BIOMETRIC_SERVICE,
+ AUTHENTICATION_POLICY_SERVICE,
MEDIA_ROUTER_SERVICE,
TELEPHONY_SERVICE,
TELEPHONY_SUBSCRIPTION_SERVICE,
@@ -4437,6 +4439,9 @@
* web domain approval state.
* <dt> {@link #DISPLAY_HASH_SERVICE} ("display_hash")
* <dd> A {@link android.view.displayhash.DisplayHashManager} for management of display hashes.
+ * <dt> {@link #AUTHENTICATION_POLICY_SERVICE} ("authentication_policy")
+ * <dd> A {@link android.security.authenticationpolicy.AuthenticationPolicyManager}
+ * for managing authentication related policies on the device.
* </dl>
*
* <p>Note: System services obtained via this API may be closely associated with
@@ -4521,8 +4526,9 @@
* @see android.content.pm.verify.domain.DomainVerificationManager
* @see #DISPLAY_HASH_SERVICE
* @see android.view.displayhash.DisplayHashManager
+ * @see #AUTHENTICATION_POLICY_SERVICE
+ * @see android.security.authenticationpolicy.AuthenticationPolicyManager
*/
- // TODO(b/347269120): Re-add @Nullable
public abstract Object getSystemService(@ServiceName @NonNull String name);
/**
@@ -4543,7 +4549,8 @@
* {@link android.os.BatteryManager}, {@link android.app.job.JobScheduler},
* {@link android.app.usage.NetworkStatsManager},
* {@link android.content.pm.verify.domain.DomainVerificationManager},
- * {@link android.view.displayhash.DisplayHashManager}.
+ * {@link android.view.displayhash.DisplayHashManager}
+ * {@link android.security.authenticationpolicy.AuthenticationPolicyManager}.
* </p>
*
* <p>
@@ -4568,7 +4575,6 @@
*/
@SuppressWarnings("unchecked")
@RavenwoodKeep
- // TODO(b/347269120): Re-add @Nullable
public final <T> T getSystemService(@NonNull Class<T> serviceClass) {
// Because subclasses may override getSystemService(String) we cannot
// perform a lookup by class alone. We must first map the class to its
@@ -5183,6 +5189,18 @@
public static final String AUTH_SERVICE = "auth";
/**
+ * Use with {@link #getSystemService(String)} to retrieve an {@link
+ * android.security.authenticationpolicy.AuthenticationPolicyManager}.
+ * @see #getSystemService
+ * @see android.security.authenticationpolicy.AuthenticationPolicyManager
+ *
+ * @hide
+ */
+ @SystemApi
+ @FlaggedApi(FLAG_SECURE_LOCKDOWN)
+ public static final String AUTHENTICATION_POLICY_SERVICE = "authentication_policy";
+
+ /**
* Use with {@link #getSystemService(String)} to retrieve a
* {@link android.hardware.fingerprint.FingerprintManager} for handling management
* of fingerprints.
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index 23d17cb..413eb98 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -960,7 +960,6 @@
}
@Override
- // TODO(b/347269120): Re-add @Nullable
public Object getSystemService(String name) {
return mBase.getSystemService(name);
}
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index ce52825..b10f5e4 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -1320,23 +1320,23 @@
264301586L; // buganizer id
/**
- * Excludes the packages the override is applied to from the camera compatibility treatment
- * in free-form windowing mode for fixed-orientation apps.
+ * Includes the packages the override is applied to in the camera compatibility treatment in
+ * free-form windowing mode for fixed-orientation apps.
*
* <p>In free-form windowing mode, the compatibility treatment emulates running on a portrait
* device by letterboxing the app window and changing the camera characteristics to what apps
* commonly expect in a portrait device: 90 and 270 degree sensor rotation for back and front
* cameras, respectively, and setting display rotation to 0.
*
- * <p>Use this flag to disable the compatibility treatment for apps that do not respond well to
- * the treatment.
+ * <p>Use this flag to enable the compatibility treatment for apps in which camera doesn't work
+ * well in freeform windowing.
*
* @hide
*/
@ChangeId
@Overridable
@Disabled
- public static final long OVERRIDE_CAMERA_COMPAT_DISABLE_FREEFORM_WINDOWING_TREATMENT =
+ public static final long OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT =
314961188L;
/**
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 3152ff4..5b305b4 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -193,6 +193,42 @@
"android.net.PROPERTY_SELF_CERTIFIED_NETWORK_CAPABILITIES";
/**
+ * <application> level {@link android.content.pm.PackageManager.Property} tag
+ * specifying whether the app should be put into the "restricted" backup mode when it's started
+ * for backup and restore operations.
+ *
+ * <p> See <a
+ * href="https://developer.android.com/identity/data/autobackup#ImplementingBackupAgent"> for
+ * information about restricted mode</a>.
+ *
+ * <p> Starting with Android 16 apps may not be started in restricted mode based on this
+ * property.
+ *
+ * <p><b>Syntax:</b>
+ * <pre>
+ * <application>
+ * <property
+ * android:name="android.app.backup.PROPERTY_USE_RESTRICTED_BACKUP_MODE"
+ * android:value="true|false"/>
+ * </application>
+ * </pre>
+ *
+ * <p>If this property is set, the operating system will respect it for now (see Note below).
+ * If it's not set, the behavior depends on the SDK level that the app is targeting. For apps
+ * targeting SDK level {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM} or lower, the
+ * property defaults to {@code true}. For apps targeting SDK level
+ * {@link android.os.Build.VERSION_CODES#BAKLAVA} or higher, the operating system will make a
+ * decision dynamically.
+ *
+ * <p>Note: It's not recommended to set this property to {@code true} unless absolutely
+ * necessary. In a future Android version, this property may be deprecated in favor of removing
+ * restricted mode completely.
+ */
+ @FlaggedApi(com.android.server.backup.Flags.FLAG_ENABLE_RESTRICTED_MODE_CHANGES)
+ public static final String PROPERTY_USE_RESTRICTED_BACKUP_MODE =
+ "android.app.backup.PROPERTY_USE_RESTRICTED_BACKUP_MODE";
+
+ /**
* Application level property that an app can specify to opt-out from having private data
* directories both on the internal and external storages.
*
@@ -292,6 +328,10 @@
* <p>
* The value of a property will only have a single type, as defined by
* the property itself.
+ *
+ * <p class="note"><strong>Note:</strong>
+ * In android version {@link Build.VERSION_CODES#VANILLA_ICE_CREAM} and earlier,
+ * the {@code equals} and {@code hashCode} methods for this class may not function as expected.
*/
public static final class Property implements Parcelable {
private static final int TYPE_BOOLEAN = 1;
@@ -523,6 +563,40 @@
return new Property[size];
}
};
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof Property)) {
+ return false;
+ }
+ final Property property = (Property) obj;
+ return mType == property.mType &&
+ Objects.equals(mName, property.mName) &&
+ Objects.equals(mClassName, property.mClassName) &&
+ Objects.equals(mPackageName, property.mPackageName) &&
+ (mType == TYPE_BOOLEAN ? mBooleanValue == property.mBooleanValue :
+ mType == TYPE_FLOAT ? Float.compare(mFloatValue, property.mFloatValue) == 0 :
+ mType == TYPE_INTEGER ? mIntegerValue == property.mIntegerValue :
+ mType == TYPE_RESOURCE ? mIntegerValue == property.mIntegerValue :
+ mStringValue.equals(property.mStringValue));
+ }
+
+ @Override
+ public int hashCode() {
+ int result = Objects.hash(mName, mType, mClassName, mPackageName);
+ if (mType == TYPE_BOOLEAN) {
+ result = 31 * result + (mBooleanValue ? 1 : 0);
+ } else if (mType == TYPE_FLOAT) {
+ result = 31 * result + Float.floatToIntBits(mFloatValue);
+ } else if (mType == TYPE_INTEGER) {
+ result = 31 * result + mIntegerValue;
+ } else if (mType == TYPE_RESOURCE) {
+ result = 31 * result + mIntegerValue;
+ } else if (mType == TYPE_STRING) {
+ result = 31 * result + mStringValue.hashCode();
+ }
+ return result;
+ }
}
/**
@@ -3126,6 +3200,16 @@
/**
* Feature for {@link #getSystemAvailableFeatures} and
+ * {@link #hasSystemFeature}: The device is capable of ranging with
+ * other devices using channel sounding via Bluetooth Low Energy radio.
+ */
+ @FlaggedApi(com.android.ranging.flags.Flags.FLAG_RANGING_CS_ENABLED)
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_BLUETOOTH_LE_CHANNEL_SOUNDING =
+ "android.hardware.bluetooth_le.channel_sounding";
+
+ /**
+ * Feature for {@link #getSystemAvailableFeatures} and
* {@link #hasSystemFeature}: The device has a camera facing away
* from the screen.
*/
diff --git a/core/java/android/content/pm/ServiceInfo.java b/core/java/android/content/pm/ServiceInfo.java
index 4285b0a..8243d88 100644
--- a/core/java/android/content/pm/ServiceInfo.java
+++ b/core/java/android/content/pm/ServiceInfo.java
@@ -20,6 +20,7 @@
import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.RequiresPermission;
+import android.health.connect.HealthPermissions;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Printer;
@@ -361,8 +362,10 @@
* {@link android.Manifest.permission#FOREGROUND_SERVICE_HEALTH} and one of the following
* permissions:
* {@link android.Manifest.permission#ACTIVITY_RECOGNITION},
- * {@link android.Manifest.permission#BODY_SENSORS},
* {@link android.Manifest.permission#HIGH_SAMPLING_RATE_SENSORS}.
+ * {@link android.health.connect.HealthPermissions#READ_HEART_RATE},
+ * {@link android.health.connect.HealthPermissions#READ_SKIN_TEMPERATURE},
+ * {@link android.health.connect.HealthPermissions#READ_OXYGEN_SATURATION},
*/
@RequiresPermission(
allOf = {
@@ -370,10 +373,13 @@
},
anyOf = {
Manifest.permission.ACTIVITY_RECOGNITION,
- Manifest.permission.BODY_SENSORS,
Manifest.permission.HIGH_SAMPLING_RATE_SENSORS,
+ HealthPermissions.READ_HEART_RATE,
+ HealthPermissions.READ_SKIN_TEMPERATURE,
+ HealthPermissions.READ_OXYGEN_SATURATION,
}
)
+ @FlaggedApi(android.permission.flags.Flags.FLAG_REPLACE_BODY_SENSOR_PERMISSION_ENABLED)
public static final int FOREGROUND_SERVICE_TYPE_HEALTH = 1 << 8;
/**
diff --git a/core/java/android/content/pm/SharedLibraryInfo.java b/core/java/android/content/pm/SharedLibraryInfo.java
index f7191e6..5dfec98 100644
--- a/core/java/android/content/pm/SharedLibraryInfo.java
+++ b/core/java/android/content/pm/SharedLibraryInfo.java
@@ -105,6 +105,8 @@
private final List<VersionedPackage> mOptionalDependentPackages;
private List<SharedLibraryInfo> mDependencies;
+ private final List<String> mCertDigests;
+
/**
* Creates a new instance.
*
@@ -134,6 +136,7 @@
mDependencies = dependencies;
mIsNative = isNative;
mOptionalDependentPackages = null;
+ mCertDigests = null;
}
/**
@@ -165,6 +168,7 @@
mDeclaringPackage = declaringPackage;
mDependencies = dependencies;
mIsNative = isNative;
+ mCertDigests = null;
var allDependents = allDependentPackages.first;
var usesLibOptional = allDependentPackages.second;
@@ -206,6 +210,7 @@
mIsNative = parcel.readBoolean();
mOptionalDependentPackages = parcel.readParcelableList(new ArrayList<>(),
VersionedPackage.class.getClassLoader(), VersionedPackage.class);
+ mCertDigests = parcel.createStringArrayList();
}
/**
@@ -214,6 +219,7 @@
* @param versionMajor
*/
public SharedLibraryInfo(String name, long versionMajor, int type) {
+ //TODO: change to this(name, versionMajor, type, /* certDigest= */null); after flag removal
mPath = null;
mPackageName = null;
mName = name;
@@ -224,6 +230,29 @@
mDependencies = null;
mIsNative = false;
mOptionalDependentPackages = null;
+ mCertDigests = null;
+ }
+
+ /**
+ * @hide
+ * @param name The lib name.
+ * @param versionMajor The lib major version.
+ * @param type The type of shared library.
+ * @param certDigests The list of certificate digests for this shared library.
+ */
+ @FlaggedApi(Flags.FLAG_SDK_DEPENDENCY_INSTALLER)
+ public SharedLibraryInfo(String name, long versionMajor, int type, List<String> certDigests) {
+ mPath = null;
+ mPackageName = null;
+ mName = name;
+ mVersion = versionMajor;
+ mType = type;
+ mDeclaringPackage = null;
+ mDependentPackages = null;
+ mDependencies = null;
+ mIsNative = false;
+ mOptionalDependentPackages = null;
+ mCertDigests = certDigests;
}
/**
@@ -433,6 +462,19 @@
return mOptionalDependentPackages;
}
+ /**
+ * Gets the list of certificate digests for the shared library.
+ *
+ * @return The list of certificate digests
+ */
+ @FlaggedApi(Flags.FLAG_SDK_DEPENDENCY_INSTALLER)
+ public @NonNull List<String> getCertDigests() {
+ if (mCertDigests == null) {
+ return Collections.emptyList();
+ }
+ return mCertDigests;
+ }
+
@Override
public int describeContents() {
return 0;
@@ -463,6 +505,7 @@
parcel.writeTypedList(mDependencies);
parcel.writeBoolean(mIsNative);
parcel.writeParcelableList(mOptionalDependentPackages, flags);
+ parcel.writeStringList(mCertDigests);
}
private static String typeToString(int type) {
diff --git a/core/java/android/text/ClientFlags.java b/core/java/android/content/pm/dependencyinstaller/DependencyInstallerCallback.aidl
similarity index 60%
copy from core/java/android/text/ClientFlags.java
copy to core/java/android/content/pm/dependencyinstaller/DependencyInstallerCallback.aidl
index ca88764..06fcabc 100644
--- a/core/java/android/text/ClientFlags.java
+++ b/core/java/android/content/pm/dependencyinstaller/DependencyInstallerCallback.aidl
@@ -1,5 +1,5 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
+/**
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,16 +14,6 @@
* limitations under the License.
*/
-package android.text;
+package android.content.pm.dependencyinstaller;
-/**
- * An aconfig feature flags that can be accessible from application process without
- * ContentProvider IPCs.
- *
- * When you add new flags, you have to add flag string to {@link TextFlags#TEXT_ACONFIGS_FLAGS}.
- *
- * TODO(nona): Remove this class.
- * @hide
- */
-public class ClientFlags {
-}
+parcelable DependencyInstallerCallback;
\ No newline at end of file
diff --git a/core/java/android/content/pm/dependencyinstaller/DependencyInstallerCallback.java b/core/java/android/content/pm/dependencyinstaller/DependencyInstallerCallback.java
new file mode 100644
index 0000000..ba089f7
--- /dev/null
+++ b/core/java/android/content/pm/dependencyinstaller/DependencyInstallerCallback.java
@@ -0,0 +1,100 @@
+/**
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm.dependencyinstaller;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.content.pm.Flags;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.RemoteException;
+
+/**
+ * Callbacks for {@link DependencyInstallerService}. The implementation of
+ * DependencyInstallerService uses this interface to indicate completion of the session creation
+ * request given by the system server.
+ *
+ * @hide
+ */
+@SystemApi
+@FlaggedApi(Flags.FLAG_SDK_DEPENDENCY_INSTALLER)
+public final class DependencyInstallerCallback implements Parcelable {
+ private final IBinder mBinder;
+ private final IDependencyInstallerCallback mCallback;
+
+ /** @hide */
+ public DependencyInstallerCallback(IBinder binder) {
+ mBinder = binder;
+ mCallback = IDependencyInstallerCallback.Stub.asInterface(binder);
+ }
+
+ private DependencyInstallerCallback(Parcel in) {
+ mBinder = in.readStrongBinder();
+ mCallback = IDependencyInstallerCallback.Stub.asInterface(mBinder);
+ }
+
+ /**
+ * Callback to indicate that all the requested dependencies have been resolved and their
+ * sessions created. See {@link DependencyInstallerService#onDependenciesRequired}.
+ *
+ * @param sessionIds the install session IDs for all requested dependencies
+ */
+ public void onAllDependenciesResolved(@NonNull int[] sessionIds) {
+ try {
+ mCallback.onAllDependenciesResolved(sessionIds);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Callback to indicate that at least one of the required dependencies could not be resolved
+ * and any associated sessions have been abandoned.
+ */
+ public void onFailureToResolveAllDependencies() {
+ try {
+ mCallback.onFailureToResolveAllDependencies();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel out, int flags) {
+ out.writeStrongBinder(mBinder);
+ }
+
+ public static final @NonNull Creator<DependencyInstallerCallback> CREATOR =
+ new Creator<>() {
+ @Override
+ public DependencyInstallerCallback createFromParcel(Parcel in) {
+ return new DependencyInstallerCallback(in);
+ }
+
+ @Override
+ public DependencyInstallerCallback[] newArray(int size) {
+ return new DependencyInstallerCallback[size];
+ }
+ };
+}
diff --git a/core/java/android/content/pm/dependencyinstaller/DependencyInstallerService.java b/core/java/android/content/pm/dependencyinstaller/DependencyInstallerService.java
new file mode 100644
index 0000000..1186415
--- /dev/null
+++ b/core/java/android/content/pm/dependencyinstaller/DependencyInstallerService.java
@@ -0,0 +1,83 @@
+/**
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm.dependencyinstaller;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.app.Service;
+import android.content.Intent;
+import android.content.pm.Flags;
+import android.content.pm.SharedLibraryInfo;
+import android.os.IBinder;
+
+import java.util.List;
+
+/**
+ * Service that needs to be implemented by the holder of the DependencyInstaller role. This service
+ * will be invoked by the system during application installations if it depends on
+ * {@link android.content.pm.SharedLibraryInfo#TYPE_STATIC} or
+ * {@link android.content.pm.SharedLibraryInfo#TYPE_SDK_PACKAGE} and those dependencies aren't
+ * already installed.
+ * <p>
+ * Below is an example manifest registration for a {@code DependencyInstallerService}.
+ * <pre>
+ * {@code
+ * <service android:name=".ExampleDependencyInstallerService"
+ * android:permission="android.permission.BIND_DEPENDENCY_INSTALLER" >
+ * ...
+ * <intent-filter>
+ * <action android:name="android.content.pm.action.INSTALL_DEPENDENCY" />
+ * </intent-filter>
+ * </service>
+ * }
+ * </pre>
+ *
+ * @hide
+ */
+@SystemApi
+@FlaggedApi(Flags.FLAG_SDK_DEPENDENCY_INSTALLER)
+public abstract class DependencyInstallerService extends Service {
+
+ private IDependencyInstallerService mBinder;
+
+ @Override
+ public final @NonNull IBinder onBind(@Nullable Intent intent) {
+ if (mBinder == null) {
+ mBinder = new IDependencyInstallerService.Stub() {
+ @Override
+ public void onDependenciesRequired(List<SharedLibraryInfo> neededLibraries,
+ DependencyInstallerCallback callback) {
+ DependencyInstallerService.this.onDependenciesRequired(neededLibraries,
+ callback);
+ }
+ };
+ }
+ return mBinder.asBinder();
+ }
+
+ /**
+ * Notify the holder of the DependencyInstaller role of the missing dependencies required for
+ * the completion of an active install session.
+ *
+ * @param neededLibraries the list of shared library dependencies needed to be obtained and
+ * installed.
+ */
+ public abstract void onDependenciesRequired(@NonNull List<SharedLibraryInfo> neededLibraries,
+ @NonNull DependencyInstallerCallback callback);
+}
diff --git a/core/java/android/content/pm/dependencyinstaller/IDependencyInstallerCallback.aidl b/core/java/android/content/pm/dependencyinstaller/IDependencyInstallerCallback.aidl
new file mode 100644
index 0000000..92d1d9e
--- /dev/null
+++ b/core/java/android/content/pm/dependencyinstaller/IDependencyInstallerCallback.aidl
@@ -0,0 +1,41 @@
+/**
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm.dependencyinstaller;
+
+import java.util.List;
+
+/**
+* Callbacks for Dependency Installer. The app side invokes on this interface to indicate
+* completion of the async dependency install request given by the system server.
+*
+* {@hide}
+*/
+oneway interface IDependencyInstallerCallback {
+ /**
+ * Callback to indicate that all the requested dependencies have been resolved and have been
+ * committed for installation. See {@link DependencyInstallerService#onDependenciesRequired}.
+ *
+ * @param sessionIds the install session IDs for all requested dependencies
+ */
+ void onAllDependenciesResolved(in int[] sessionIds);
+
+ /**
+ * Callback to indicate that at least one of the required dependencies could not be resolved
+ * and any associated sessions have been abandoned.
+ */
+ void onFailureToResolveAllDependencies();
+}
\ No newline at end of file
diff --git a/core/java/android/content/pm/dependencyinstaller/IDependencyInstallerService.aidl b/core/java/android/content/pm/dependencyinstaller/IDependencyInstallerService.aidl
new file mode 100644
index 0000000..94f5bf4
--- /dev/null
+++ b/core/java/android/content/pm/dependencyinstaller/IDependencyInstallerService.aidl
@@ -0,0 +1,37 @@
+/**
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm.dependencyinstaller;
+
+import android.content.pm.dependencyinstaller.DependencyInstallerCallback;
+import android.content.pm.SharedLibraryInfo;
+import java.util.List;
+
+/**
+* Interface used to communicate with the application code that holds the Dependency Installer role.
+* {@hide}
+*/
+oneway interface IDependencyInstallerService {
+ /**
+ * Notify dependency installer of the required dependencies to complete the current install
+ * session.
+ *
+ * @param neededLibraries the list of shared library dependencies needed to be obtained and
+ * installed.
+ */
+ void onDependenciesRequired(in List<SharedLibraryInfo> neededLibraries,
+ in DependencyInstallerCallback callback);
+ }
\ No newline at end of file
diff --git a/core/java/android/content/pm/flags.aconfig b/core/java/android/content/pm/flags.aconfig
index 9ba5a35..e181ae8 100644
--- a/core/java/android/content/pm/flags.aconfig
+++ b/core/java/android/content/pm/flags.aconfig
@@ -366,3 +366,13 @@
description: "Block app installations that specify an incompatible minor SDK version"
bug: "377474232"
}
+
+flag {
+ name: "app_compat_option_16kb"
+ is_exported: true
+ namespace: "devoptions_settings"
+ description: "Feature flag to enable page size app compat mode from manifest, package manager and settings level."
+ bug: "371049373"
+ is_fixed_read_only: true
+}
+
diff --git a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
index e9e8578..05c8f31 100644
--- a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
+++ b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
@@ -537,6 +537,9 @@
hasBindDeviceAdminPermission);
break;
case TAG_USES_SDK_LIBRARY:
+ if (!android.content.pm.Flags.sdkDependencyInstaller()) {
+ break;
+ }
String usesSdkLibName = parser.getAttributeValue(
ANDROID_RES_NAMESPACE, "name");
long usesSdkLibVersionMajor = parser.getAttributeIntValue(
diff --git a/core/java/android/content/res/flags.aconfig b/core/java/android/content/res/flags.aconfig
index f23c193..6fc7d90 100644
--- a/core/java/android/content/res/flags.aconfig
+++ b/core/java/android/content/res/flags.aconfig
@@ -105,3 +105,12 @@
# This flag is used to control aapt2 behavior.
is_fixed_read_only: true
}
+
+flag {
+ name: "resources_minor_version_support"
+ is_exported: true
+ namespace: "resource_manager"
+ description: "Feature flag for supporting minor version in Resources"
+ bug: "373535266"
+ is_fixed_read_only: true
+}
diff --git a/core/java/android/hardware/DisplayLuts.java b/core/java/android/hardware/DisplayLuts.java
index b162ad6..6343ba1 100644
--- a/core/java/android/hardware/DisplayLuts.java
+++ b/core/java/android/hardware/DisplayLuts.java
@@ -16,116 +16,294 @@
package android.hardware;
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
+import android.hardware.flags.Flags;
import android.util.IntArray;
import java.util.ArrayList;
-import java.util.List;
/**
- * @hide
+ * DisplayLuts provides the developers to apply Lookup Tables (Luts) to a
+ * {@link android.view.SurfaceControl}. Luts provides ways to control tonemapping
+ * for specific content.
+ *
+ * The general flow is as follows:
+ * <p>
+ * <img src="{@docRoot}reference/android/images/graphics/DisplayLuts.png" />
+ * <figcaption style="text-align: center;">DisplayLuts flow</figcaption>
+ * </p>
+ *
+ * @see LutProperties
*/
+@FlaggedApi(Flags.FLAG_LUTS_API)
public final class DisplayLuts {
+ private ArrayList<Entry> mEntries;
private IntArray mOffsets;
private int mTotalLength;
- private List<float[]> mLutBuffers;
- private IntArray mLutDimensions;
- private IntArray mLutSizes;
- private IntArray mLutSamplingKeys;
- private static final int LUT_LENGTH_LIMIT = 100000;
-
+ /**
+ * Create a {@link DisplayLuts} instance.
+ */
+ @FlaggedApi(Flags.FLAG_LUTS_API)
public DisplayLuts() {
+ mEntries = new ArrayList<>();
mOffsets = new IntArray();
mTotalLength = 0;
-
- mLutBuffers = new ArrayList<>();
- mLutDimensions = new IntArray();
- mLutSizes = new IntArray();
- mLutSamplingKeys = new IntArray();
}
- /**
- * Add the lut to be applied.
- *
- * @param buffer
- * @param dimension either 1D or 3D
- * @param size
- * @param samplingKey
- */
- public void addLut(@NonNull float[] buffer, @LutProperties.Dimension int dimension,
- int size, @LutProperties.SamplingKey int samplingKey) {
+ @FlaggedApi(Flags.FLAG_LUTS_API)
+ public static class Entry {
+ private float[] mBuffer;
+ private @LutProperties.Dimension int mDimension;
+ private int mSize;
+ private @LutProperties.SamplingKey int mSamplingKey;
- int lutLength = 0;
- if (dimension == LutProperties.ONE_DIMENSION) {
- lutLength = size;
- } else if (dimension == LutProperties.THREE_DIMENSION) {
- lutLength = size * size * size;
- } else {
- clear();
- throw new IllegalArgumentException("The dimension is either 1D or 3D!");
+ private static final int LUT_LENGTH_LIMIT = 100000;
+
+ /**
+ * Create a Lut entry.
+ *
+ * <p>
+ * Noted that 1D Lut(s) are treated as gain curves.
+ * For 3D Lut(s), 3D Lut(s) are used for direct color manipulations.
+ * The values of 3D Lut(s) data should be normalized to the range {@code 0.0}
+ * to {@code 1.0}, inclusive. And 3D Lut(s) data is organized in the order of
+ * R, G, B channels.
+ *
+ * @param buffer The raw lut data
+ * @param dimension Either 1D or 3D
+ * @param samplingKey The sampling kay used for the Lut
+ */
+ @FlaggedApi(Flags.FLAG_LUTS_API)
+ public Entry(@NonNull float[] buffer,
+ @LutProperties.Dimension int dimension,
+ @LutProperties.SamplingKey int samplingKey) {
+ if (buffer == null || buffer.length < 1) {
+ throw new IllegalArgumentException("The buffer cannot be empty!");
+ }
+
+ if (buffer.length >= LUT_LENGTH_LIMIT) {
+ throw new IllegalArgumentException("The lut length is too big to handle!");
+ }
+
+ if (dimension != LutProperties.ONE_DIMENSION
+ && dimension != LutProperties.THREE_DIMENSION) {
+ throw new IllegalArgumentException("The dimension should be either 1D or 3D!");
+ }
+
+ if (dimension == LutProperties.THREE_DIMENSION) {
+ if (buffer.length <= 3) {
+ throw new IllegalArgumentException(
+ "The 3d lut size of each dimension should be over 1!");
+ }
+ int lengthPerChannel = buffer.length;
+ if (lengthPerChannel % 3 != 0) {
+ throw new IllegalArgumentException(
+ "The lut buffer of 3dlut should have 3 channels!");
+ }
+ lengthPerChannel /= 3;
+
+ double size = Math.cbrt(lengthPerChannel);
+ if (size == (int) size) {
+ mSize = (int) size;
+ } else {
+ throw new IllegalArgumentException(
+ "Cannot get the cube root of the 3d lut buffer!");
+ }
+ } else {
+ mSize = buffer.length;
+ }
+
+ mBuffer = buffer;
+ mDimension = dimension;
+ mSamplingKey = samplingKey;
}
- if (lutLength >= LUT_LENGTH_LIMIT) {
- clear();
- throw new IllegalArgumentException("The lut length is too big to handle!");
+ /**
+ * @return the dimension of the lut entry
+ */
+ @FlaggedApi(Flags.FLAG_LUTS_API)
+ public int getDimension() {
+ return mDimension;
}
+ /**
+ * @return the size of the lut for each dimension
+ * @hide
+ */
+ public int getSize() {
+ return mSize;
+ }
+
+ /**
+ * @return the lut raw data of the lut
+ */
+ @FlaggedApi(Flags.FLAG_LUTS_API)
+ public @NonNull float[] getBuffer() {
+ return mBuffer;
+ }
+
+ /**
+ * @return the sampling key used by the lut
+ */
+ @FlaggedApi(Flags.FLAG_LUTS_API)
+ public int getSamplingKey() {
+ return mSamplingKey;
+ }
+
+ @Override
+ public String toString() {
+ return "Entry{"
+ + "dimension=" + DisplayLuts.Entry.dimensionToString(getDimension())
+ + ", size(each dimension)=" + getSize()
+ + ", samplingKey=" + samplingKeyToString(getSamplingKey()) + "}";
+ }
+
+ private static String dimensionToString(int dimension) {
+ switch(dimension) {
+ case LutProperties.ONE_DIMENSION:
+ return "ONE_DIMENSION";
+ case LutProperties.THREE_DIMENSION:
+ return "THREE_DIMENSION";
+ default:
+ return "";
+ }
+ }
+
+ private static String samplingKeyToString(int key) {
+ switch(key) {
+ case LutProperties.SAMPLING_KEY_RGB:
+ return "SAMPLING_KEY_RGB";
+ case LutProperties.SAMPLING_KEY_MAX_RGB:
+ return "SAMPLING_KEY_MAX_RGB";
+ default:
+ return "";
+ }
+ }
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder("DisplayLuts{");
+ sb.append("\n");
+ for (DisplayLuts.Entry entry: mEntries) {
+ sb.append(entry.toString());
+ sb.append("\n");
+ }
+ sb.append("}");
+ return sb.toString();
+ }
+
+ private void addEntry(Entry entry) {
+ mEntries.add(entry);
mOffsets.add(mTotalLength);
- mTotalLength += lutLength;
-
- mLutBuffers.add(buffer);
- mLutDimensions.add(dimension);
- mLutSizes.add(size);
- mLutSamplingKeys.add(samplingKey);
+ mTotalLength += entry.getBuffer().length;
}
private void clear() {
- mTotalLength = 0;
mOffsets.clear();
- mLutBuffers.clear();
- mLutDimensions.clear();
- mLutSamplingKeys.clear();
+ mTotalLength = 0;
+ mEntries.clear();
}
/**
- * @return the array of Lut buffers
+ * Set a Lut to be applied.
+ *
+ * <p>Use either this or {@link #set(Entry, Entry)}. The function will
+ * replace any previously set lut(s).</p>
+ *
+ * @param entry Either an 1D Lut or a 3D Lut
+ */
+ @FlaggedApi(Flags.FLAG_LUTS_API)
+ public void set(@NonNull Entry entry) {
+ if (entry == null) {
+ throw new IllegalArgumentException("The entry is null!");
+ }
+ clear();
+ addEntry(entry);
+ }
+
+ /**
+ * Set Luts in order to be applied.
+ *
+ * <p> An 1D Lut and 3D Lut will be applied in order. Use either this or
+ * {@link #set(Entry)}. The function will replace any previously set lut(s)</p>
+ *
+ * @param first An 1D Lut
+ * @param second A 3D Lut
+ */
+ @FlaggedApi(Flags.FLAG_LUTS_API)
+ public void set(@NonNull Entry first, @NonNull Entry second) {
+ if (first == null || second == null) {
+ throw new IllegalArgumentException("The entry is null!");
+ }
+ if (first.getDimension() != LutProperties.ONE_DIMENSION
+ || second.getDimension() != LutProperties.THREE_DIMENSION) {
+ throw new IllegalArgumentException("The entries should be 1D and 3D in order!");
+ }
+ clear();
+ addEntry(first);
+ addEntry(second);
+ }
+
+ /**
+ * @hide
+ */
+ public boolean valid() {
+ return mEntries.size() > 0;
+ }
+
+ /**
+ * @hide
*/
public float[] getLutBuffers() {
float[] buffer = new float[mTotalLength];
- for (int i = 0; i < mLutBuffers.size(); i++) {
- float[] lutBuffer = mLutBuffers.get(i);
+ for (int i = 0; i < mEntries.size(); i++) {
+ float[] lutBuffer = mEntries.get(i).getBuffer();
System.arraycopy(lutBuffer, 0, buffer, mOffsets.get(i), lutBuffer.length);
}
return buffer;
}
/**
- * @return the starting point of each lut memory region of the lut buffer
+ * @hide
*/
public int[] getOffsets() {
return mOffsets.toArray();
}
/**
- * @return the array of Lut size
+ * @hide
*/
public int[] getLutSizes() {
- return mLutSizes.toArray();
+ int[] sizes = new int[mEntries.size()];
+ for (int i = 0; i < mEntries.size(); i++) {
+ sizes[i] = mEntries.get(i).getSize();
+ }
+ return sizes;
}
/**
- * @return the array of Lut dimension
+ * @hide
*/
public int[] getLutDimensions() {
- return mLutDimensions.toArray();
+ int[] dimensions = new int[mEntries.size()];
+ for (int i = 0; i < mEntries.size(); i++) {
+ dimensions[i] = mEntries.get(i).getDimension();
+ }
+ return dimensions;
}
/**
- * @return the array of sampling key
+ * @hide
*/
public int[] getLutSamplingKeys() {
- return mLutSamplingKeys.toArray();
+ int[] samplingKeys = new int[mEntries.size()];
+ for (int i = 0; i < mEntries.size(); i++) {
+ samplingKeys[i] = mEntries.get(i).getSamplingKey();
+ }
+ return samplingKeys;
}
}
diff --git a/core/java/android/hardware/LutProperties.java b/core/java/android/hardware/LutProperties.java
index c9c6d6d..bf40a41 100644
--- a/core/java/android/hardware/LutProperties.java
+++ b/core/java/android/hardware/LutProperties.java
@@ -16,23 +16,31 @@
package android.hardware;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.hardware.flags.Flags;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
- * Lut properties class.
+ * Provides Lut properties of the device.
*
- * A Lut (Look-Up Table) is a pre-calculated table for color transformation.
- *
- * @hide
+ * <p>
+ * A Lut (Look-Up Table) is a pre-calculated table for color correction.
+ * Applications may be interested in the Lut properties exposed by
+ * this class to determine if the Lut(s) they select using
+ * {@link android.view.SurfaceControl.Transaction#setLuts} are by the HWC.
+ * </p>
*/
+@FlaggedApi(Flags.FLAG_LUTS_API)
public final class LutProperties {
private final @Dimension int mDimension;
private final int mSize;
private final @SamplingKey int[] mSamplingKeys;
+ /** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = {"SAMPLING_KEY_"}, value = {
SAMPLING_KEY_RGB,
@@ -42,11 +50,14 @@
}
/** use r,g,b channel as the gain value of a Lut */
+ @FlaggedApi(Flags.FLAG_LUTS_API)
public static final int SAMPLING_KEY_RGB = 0;
/** use max of r,g,b channel as the gain value of a Lut */
+ @FlaggedApi(Flags.FLAG_LUTS_API)
public static final int SAMPLING_KEY_MAX_RGB = 1;
+ /** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(value = {
ONE_DIMENSION,
@@ -56,18 +67,22 @@
}
/** The Lut is one dimensional */
+ @FlaggedApi(Flags.FLAG_LUTS_API)
public static final int ONE_DIMENSION = 1;
/** The Lut is three dimensional */
+ @FlaggedApi(Flags.FLAG_LUTS_API)
public static final int THREE_DIMENSION = 3;
+ @FlaggedApi(Flags.FLAG_LUTS_API)
public @Dimension int getDimension() {
return mDimension;
}
/**
- * @return the size of the Lut.
+ * @return the size of the Lut for each dimension
*/
+ @FlaggedApi(Flags.FLAG_LUTS_API)
public int getSize() {
return mSize;
}
@@ -75,6 +90,8 @@
/**
* @return the list of sampling keys
*/
+ @FlaggedApi(Flags.FLAG_LUTS_API)
+ @NonNull
public @SamplingKey int[] getSamplingKeys() {
if (mSamplingKeys.length == 0) {
throw new IllegalStateException("no sampling key!");
diff --git a/core/java/android/hardware/OverlayProperties.java b/core/java/android/hardware/OverlayProperties.java
index 24cfc1b..d42bfae 100644
--- a/core/java/android/hardware/OverlayProperties.java
+++ b/core/java/android/hardware/OverlayProperties.java
@@ -18,6 +18,7 @@
import android.annotation.FlaggedApi;
import android.annotation.NonNull;
+import android.annotation.SuppressLint;
import android.hardware.flags.Flags;
import android.os.Parcel;
import android.os.Parcelable;
@@ -72,9 +73,11 @@
}
/**
- * Gets the lut properties of the display.
- * @hide
+ * Returns the lut properties of the device.
*/
+ @FlaggedApi(Flags.FLAG_LUTS_API)
+ @SuppressLint("ArrayReturn")
+ @NonNull
public LutProperties[] getLutProperties() {
if (mNativeObject == 0) {
return null;
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 58e524e..5533a64 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -19,9 +19,9 @@
import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SystemApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.hardware.camera2.impl.CameraMetadataNative;
-import android.hardware.camera2.impl.ExtensionKey;
import android.hardware.camera2.impl.PublicKey;
import android.hardware.camera2.impl.SyntheticKey;
import android.hardware.camera2.params.DeviceStateSensorOrientationMap;
@@ -6172,6 +6172,66 @@
public static final Key<android.hardware.camera2.params.StreamConfigurationDuration[]> JPEGR_AVAILABLE_JPEG_R_STALL_DURATIONS_MAXIMUM_RESOLUTION =
new Key<android.hardware.camera2.params.StreamConfigurationDuration[]>("android.jpegr.availableJpegRStallDurationsMaximumResolution", android.hardware.camera2.params.StreamConfigurationDuration[].class);
+ /**
+ * <p>Color space used for shared session configuration for all the output targets
+ * when camera is opened in shared mode. This should be one of the values specified in
+ * availableColorSpaceProfilesMap.</p>
+ * <p><b>Possible values:</b></p>
+ * <ul>
+ * <li>{@link #SHARED_SESSION_COLOR_SPACE_UNSPECIFIED UNSPECIFIED}</li>
+ * <li>{@link #SHARED_SESSION_COLOR_SPACE_SRGB SRGB}</li>
+ * <li>{@link #SHARED_SESSION_COLOR_SPACE_DISPLAY_P3 DISPLAY_P3}</li>
+ * <li>{@link #SHARED_SESSION_COLOR_SPACE_BT2020_HLG BT2020_HLG}</li>
+ * </ul>
+ *
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ * @see #SHARED_SESSION_COLOR_SPACE_UNSPECIFIED
+ * @see #SHARED_SESSION_COLOR_SPACE_SRGB
+ * @see #SHARED_SESSION_COLOR_SPACE_DISPLAY_P3
+ * @see #SHARED_SESSION_COLOR_SPACE_BT2020_HLG
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_CAMERA_MULTI_CLIENT)
+ public static final Key<Integer> SHARED_SESSION_COLOR_SPACE =
+ new Key<Integer>("android.sharedSession.colorSpace", int.class);
+
+ /**
+ * <p>List of shared output configurations that this camera device supports when
+ * camera is opened in shared mode. Array contains following entries for each supported
+ * shared configuration:
+ * 1) surface type
+ * 2) width
+ * 3) height
+ * 4) format
+ * 5) mirrorMode
+ * 6) useReadoutTimestamp
+ * 7) timestampBase
+ * 8) dataspace
+ * 9) usage
+ * 10) streamUsecase
+ * 11) physical camera id len
+ * 12) physical camera id as UTF-8 null terminated string.</p>
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_CAMERA_MULTI_CLIENT)
+ public static final Key<long[]> SHARED_SESSION_OUTPUT_CONFIGURATIONS =
+ new Key<long[]>("android.sharedSession.outputConfigurations", long[].class);
+
+ /**
+ * <p>The available stream configurations that this camera device supports for
+ * shared capture session when camera is opened in shared mode. Android camera framework
+ * will generate this tag if the camera device can be opened in shared mode.</p>
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ * @hide
+ */
+ @SystemApi
+ @NonNull
+ @SyntheticKey
+ @FlaggedApi(Flags.FLAG_CAMERA_MULTI_CLIENT)
+ public static final Key<android.hardware.camera2.params.SharedSessionConfiguration> SHARED_SESSION_CONFIGURATION =
+ new Key<android.hardware.camera2.params.SharedSessionConfiguration>("android.sharedSession.configuration", android.hardware.camera2.params.SharedSessionConfiguration.class);
+
/**
* Mapping from INFO_SESSION_CONFIGURATION_QUERY_VERSION to session characteristics key.
diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java
index fb381d9..852f047 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -446,6 +446,17 @@
public static final int SESSION_OPERATION_MODE_CONSTRAINED_HIGH_SPEED =
1; // ICameraDeviceUser.CONSTRAINED_HIGH_SPEED_MODE;
+ /**
+ * Shared camera operation mode.
+ *
+ * @see #CameraSharedCaptureSession
+ * @hide
+ */
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_CAMERA_MULTI_CLIENT)
+ public static final int SESSION_OPERATION_MODE_SHARED =
+ 2; // ICameraDeviceUser.SHARED_MODE;
+
/**
* First vendor-specific operating mode
*
@@ -461,6 +472,7 @@
@IntDef(prefix = {"SESSION_OPERATION_MODE"}, value =
{SESSION_OPERATION_MODE_NORMAL,
SESSION_OPERATION_MODE_CONSTRAINED_HIGH_SPEED,
+ SESSION_OPERATION_MODE_SHARED,
SESSION_OPERATION_MODE_VENDOR_START})
public @interface SessionOperatingMode {};
@@ -1240,7 +1252,6 @@
*
* </ul>
*
- *
* @param config A session configuration (see {@link SessionConfiguration}).
*
* @throws IllegalArgumentException In case the session configuration is invalid; or the output
@@ -1559,6 +1570,48 @@
public abstract void onOpened(@NonNull CameraDevice camera); // Must implement
/**
+ * The method called when a camera device has finished opening in shared mode,
+ * where there can be more than one client accessing the same camera.
+ *
+ * <p>At this point, the camera device is ready to use, and
+ * {@link CameraDevice#createCaptureSession} can be called to set up the shared capture
+ * session.</p>
+ *
+ * @param camera the camera device that has become opened
+ * @param isPrimaryClient true if the client opening the camera is currently the primary
+ * client.
+ * @hide
+ */
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_CAMERA_MULTI_CLIENT)
+ public void onOpenedInSharedMode(@NonNull CameraDevice camera, boolean isPrimaryClient) {
+ // Default empty implementation
+ }
+
+ /**
+ * The method called when client access priorities have changed for a camera device opened
+ * in shared mode where there can be more than one client accessing the same camera.
+ *
+ * If the client priority changed from secondary to primary, then it can now
+ * create capture request and change the capture request parameters. If client priority
+ * changed from primary to secondary, that implies that a higher priority client has also
+ * opened the camera in shared mode and the new client is now a primary client
+ *
+ * @param camera the camera device whose access priorities have changed.
+ * @param isPrimaryClient true if the client is now the primary client.
+ * false if another higher priority client also opened the
+ * camera and is now the new primary client and this client is
+ * now a secondary client.
+ * @hide
+ */
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_CAMERA_MULTI_CLIENT)
+ public void onClientSharedAccessPriorityChanged(@NonNull CameraDevice camera,
+ boolean isPrimaryClient) {
+ // Default empty implementation
+ }
+
+ /**
* The method called when a camera device has been closed with
* {@link CameraDevice#close}.
*
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index 75e2058..266efb7 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -976,6 +976,46 @@
}
/**
+ * Checks if a camera device can be opened in a shared mode for a given {@code cameraId}.
+ * If this method returns false for a {@code cameraId}, calling {@link #openSharedCamera}
+ * for that {@code cameraId} will throw an {@link UnsupportedOperationException}.
+ *
+ * @param cameraId The unique identifier of the camera device for which sharing support is
+ * being queried. This identifier must be present in
+ * {@link #getCameraIdList()}.
+ *
+ * @return {@code true} if camera can be opened in shared mode
+ * for the provided {@code cameraId}; {@code false} otherwise.
+ *
+ * @throws IllegalArgumentException If {@code cameraId} is null, or if {@code cameraId} does not
+ * match any device in {@link #getCameraIdList()}.
+ * @throws CameraAccessException if the camera device has been disconnected.
+ *
+ * @see #getCameraIdList()
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_CAMERA_MULTI_CLIENT)
+ @SystemApi
+ public boolean isCameraDeviceSharingSupported(@NonNull String cameraId)
+ throws CameraAccessException {
+ if (cameraId == null) {
+ throw new IllegalArgumentException("Camera ID was null");
+ }
+
+ if (CameraManagerGlobal.sCameraServiceDisabled
+ || !Arrays.asList(CameraManagerGlobal.get().getCameraIdList(mContext.getDeviceId(),
+ getDevicePolicyFromContext(mContext))).contains(cameraId)) {
+ throw new IllegalArgumentException(
+ "Camera ID '" + cameraId + "' not available on device.");
+ }
+
+ CameraCharacteristics chars = getCameraCharacteristics(cameraId);
+ long[] sharedOutputConfiguration =
+ chars.get(CameraCharacteristics.SHARED_SESSION_OUTPUT_CONFIGURATIONS);
+ return (sharedOutputConfiguration != null);
+ }
+
+ /**
* Retrieves the AttributionSourceState to pass to the CameraService.
*
* @param deviceIdOverride An override of the AttributionSource's deviceId, if not equal to
@@ -1036,6 +1076,9 @@
* @param cameraId The unique identifier of the camera device to open
* @param callback The callback for the camera. Must not be null.
* @param executor The executor to invoke the callback with. Must not be null.
+ * @param oomScoreOffset The minimum oom score that cameraservice must see for this client.
+ * @param rotationOverride The type of rotation override.
+ * @param sharedMode Parameter specifying if the camera should be opened in shared mode.
*
* @throws CameraAccessException if the camera is disabled by device policy,
* too many camera devices are already open, or the cameraId does not match
@@ -1051,7 +1094,8 @@
*/
private CameraDevice openCameraDeviceUserAsync(String cameraId,
CameraDevice.StateCallback callback, Executor executor,
- final int oomScoreOffset, int rotationOverride) throws CameraAccessException {
+ final int oomScoreOffset, int rotationOverride, boolean sharedMode)
+ throws CameraAccessException {
CameraCharacteristics characteristics = getCameraCharacteristics(cameraId);
CameraDevice device = null;
synchronized (mLock) {
@@ -1070,7 +1114,7 @@
characteristics,
this,
mContext.getApplicationInfo().targetSdkVersion,
- mContext, cameraDeviceSetup);
+ mContext, cameraDeviceSetup, sharedMode);
ICameraDeviceCallbacks callbacks = deviceImpl.getCallbacks();
try {
@@ -1091,7 +1135,7 @@
mContext.getApplicationInfo().targetSdkVersion,
rotationOverride,
clientAttribution,
- getDevicePolicyFromContext(mContext));
+ getDevicePolicyFromContext(mContext), sharedMode);
} catch (ServiceSpecificException e) {
if (e.errorCode == ICameraService.ERROR_DEPRECATED_HAL) {
throw new AssertionError("Should've gone down the shim path");
@@ -1218,7 +1262,8 @@
@NonNull final CameraDevice.StateCallback callback, @Nullable Handler handler)
throws CameraAccessException {
- openCameraImpl(cameraId, callback, CameraDeviceImpl.checkAndWrapHandler(handler));
+ openCameraImpl(cameraId, callback, CameraDeviceImpl.checkAndWrapHandler(handler),
+ /*oomScoreOffset*/0, getRotationOverride(mContext), /*sharedMode*/false);
}
/**
@@ -1258,7 +1303,7 @@
/*oomScoreOffset*/0,
overrideToPortrait
? ICameraService.ROTATION_OVERRIDE_OVERRIDE_TO_PORTRAIT
- : ICameraService.ROTATION_OVERRIDE_NONE);
+ : ICameraService.ROTATION_OVERRIDE_NONE, /*sharedMode*/false);
}
/**
@@ -1303,10 +1348,57 @@
if (executor == null) {
throw new IllegalArgumentException("executor was null");
}
- openCameraImpl(cameraId, callback, executor);
+ openCameraImpl(cameraId, callback, executor, /*oomScoreOffset*/0,
+ getRotationOverride(mContext), /*sharedMode*/false);
}
/**
+ * Opens a shared connection to a camera with the given ID.
+ *
+ * <p>The behavior of this method matches that of
+ * {@link #openCamera(String, Executor, StateCallback)}, except that it opens the camera in
+ * shared mode where more than one client can access the camera at the same time.</p>
+ *
+ * @param cameraId The unique identifier of the camera device to open.
+ * @param executor The executor which will be used when invoking the callback.
+ * @param callback The callback which is invoked once the camera is opened
+ *
+ * @throws CameraAccessException if the camera is disabled by device policy, or is being used
+ * by a higher-priority client in non-shared mode or the device
+ * has reached its maximal resource and cannot open this camera
+ * device.
+ *
+ * @throws IllegalArgumentException if cameraId, the callback or the executor was null,
+ * or the cameraId does not match any currently or previously
+ * available camera device.
+ *
+ * @throws SecurityException if the application does not have permission to
+ * access the camera
+ *
+ * @see #getCameraIdList
+ * @see android.app.admin.DevicePolicyManager#setCameraDisabled
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.SYSTEM_CAMERA,
+ android.Manifest.permission.CAMERA,
+ })
+ @FlaggedApi(Flags.FLAG_CAMERA_MULTI_CLIENT)
+ public void openSharedCamera(@NonNull String cameraId,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull final CameraDevice.StateCallback callback)
+ throws CameraAccessException {
+ if (executor == null) {
+ throw new IllegalArgumentException("executor was null");
+ }
+ openCameraImpl(cameraId, callback, executor, /*oomScoreOffset*/0,
+ getRotationOverride(mContext), /*sharedMode*/true);
+ }
+
+
+ /**
* Open a connection to a camera with the given ID. Also specify what oom score must be offset
* by cameraserver for this client. This api can be useful for system
* components which want to assume a lower priority (for camera arbitration) than other clients
@@ -1372,29 +1464,35 @@
"oomScoreOffset < 0, cannot increase priority of camera client");
}
openCameraImpl(cameraId, callback, executor, oomScoreOffset,
- getRotationOverride(mContext));
+ getRotationOverride(mContext), /*sharedMode*/false);
}
/**
* Open a connection to a camera with the given ID, on behalf of another application.
- * Also specify the minimum oom score and process state the application
- * should have, as seen by the cameraserver.
*
- * <p>The behavior of this method matches that of {@link #openCamera}, except that it allows
- * the caller to specify the UID to use for permission/etc verification. This can only be
- * done by services trusted by the camera subsystem to act on behalf of applications and
- * to forward the real UID.</p>
- *
+ * @param cameraId
+ * The unique identifier of the camera device to open
+ * @param callback
+ * The callback which is invoked once the camera is opened
+ * @param executor
+ * The executor which will be used when invoking the callback.
* @param oomScoreOffset
* The minimum oom score that cameraservice must see for this client.
* @param rotationOverride
* The type of rotation override (none, override_to_portrait, rotation_only)
* that should be followed for this camera id connection
+ * @param sharedMode
+ * Parameter specifying if the camera should be opened in shared mode.
+ *
+ * @throws CameraAccessException if the camera is disabled by device policy,
+ * has been disconnected, or is being used by a higher-priority camera API client in
+ * non shared mode.
+ *
* @hide
*/
public void openCameraImpl(@NonNull String cameraId,
@NonNull final CameraDevice.StateCallback callback, @NonNull Executor executor,
- int oomScoreOffset, int rotationOverride)
+ int oomScoreOffset, int rotationOverride, boolean sharedMode)
throws CameraAccessException {
if (cameraId == null) {
@@ -1407,24 +1505,7 @@
}
openCameraDeviceUserAsync(cameraId, callback, executor, oomScoreOffset,
- rotationOverride);
- }
-
- /**
- * Open a connection to a camera with the given ID, on behalf of another application.
- *
- * <p>The behavior of this method matches that of {@link #openCamera}, except that it allows
- * the caller to specify the UID to use for permission/etc verification. This can only be
- * done by services trusted by the camera subsystem to act on behalf of applications and
- * to forward the real UID.</p>
- *
- * @hide
- */
- public void openCameraImpl(@NonNull String cameraId,
- @NonNull final CameraDevice.StateCallback callback, @NonNull Executor executor)
- throws CameraAccessException {
- openCameraImpl(cameraId, callback, executor, /*oomScoreOffset*/0,
- getRotationOverride(mContext));
+ rotationOverride, sharedMode);
}
/**
@@ -2541,6 +2622,10 @@
}
@Override
public void onCameraClosed(String id, int deviceId) {
+ }
+ @Override
+ public void onCameraOpenedInSharedMode(String id, String clientPackageId,
+ int deviceId, boolean primaryClient) {
}};
String[] cameraIds;
@@ -3325,6 +3410,11 @@
}
@Override
+ public void onCameraOpenedInSharedMode(String cameraId, String clientPackageId,
+ int deviceId, boolean primaryClient) {
+ }
+
+ @Override
public void onCameraOpened(String cameraId, String clientPackageId, int deviceId) {
synchronized (mLock) {
onCameraOpenedLocked(new DeviceCameraInfo(cameraId, deviceId), clientPackageId);
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index 8d36fbd..d2fcfd6 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -2121,6 +2121,38 @@
public static final int AUTOMOTIVE_LOCATION_EXTRA_RIGHT = 10;
//
+ // Enumeration values for CameraCharacteristics#SHARED_SESSION_COLOR_SPACE
+ //
+
+ /**
+ * @see CameraCharacteristics#SHARED_SESSION_COLOR_SPACE
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_CAMERA_MULTI_CLIENT)
+ public static final int SHARED_SESSION_COLOR_SPACE_UNSPECIFIED = -1;
+
+ /**
+ * @see CameraCharacteristics#SHARED_SESSION_COLOR_SPACE
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_CAMERA_MULTI_CLIENT)
+ public static final int SHARED_SESSION_COLOR_SPACE_SRGB = 0;
+
+ /**
+ * @see CameraCharacteristics#SHARED_SESSION_COLOR_SPACE
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_CAMERA_MULTI_CLIENT)
+ public static final int SHARED_SESSION_COLOR_SPACE_DISPLAY_P3 = 7;
+
+ /**
+ * @see CameraCharacteristics#SHARED_SESSION_COLOR_SPACE
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_CAMERA_MULTI_CLIENT)
+ public static final int SHARED_SESSION_COLOR_SPACE_BT2020_HLG = 16;
+
+ //
// Enumeration values for CaptureRequest#COLOR_CORRECTION_MODE
//
diff --git a/core/java/android/hardware/camera2/CameraSharedCaptureSession.java b/core/java/android/hardware/camera2/CameraSharedCaptureSession.java
new file mode 100644
index 0000000..5426d4d
--- /dev/null
+++ b/core/java/android/hardware/camera2/CameraSharedCaptureSession.java
@@ -0,0 +1,180 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.camera2;
+
+import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.view.Surface;
+
+import com.android.internal.camera.flags.Flags;
+
+import java.util.List;
+import java.util.concurrent.Executor;
+
+/**
+ * A shared capture session for a {@link CameraDevice}, when a camera device is opened in shared
+ * mode possibly by multiple clients at the same time.
+ *
+ * <p>An active shared capture session is a special type of capture session used exclusively
+ * for shared camera access by multiple applications, provided the camera device supports this
+ * mode. To determine if a camera device supports shared mode, use the
+ * {@link android.hardware.camera2.CameraManager#isCameraDeviceSharingSupported} API.
+ * If supported, multiple clients can open the camera by calling
+ * {@link android.hardware.camera2.CameraManager#openSharedCamera} and create a shared capture
+ * session by calling {@link CameraDevice#createCaptureSession(SessionConfiguration)} and using
+ * session type as {@link android.hardware.camera2.params.SessionConfiguration#SESSION_SHARED}</p>
+ *
+ * <p>When an application has opened a camera device in shared mode, it can only create a shared
+ * capture session using session type as
+ * {@link android.hardware.camera2.params.SessionConfiguration#SESSION_SHARED}. Any other session
+ * type value will trigger {@link IllegalArgumentException}. Once the configuration is complete and
+ * the session is ready to actually capture data, the provided
+ * {@link CameraCaptureSession.StateCallback}'s
+ * {@link CameraCaptureSession.StateCallback#onConfigured} callback will be called and will
+ * receive a CameraCaptureSession (castable to {@link CameraSharedCaptureSession}).</p>
+ *
+ * <p>Shared capture sessions uses a predefined configuration detailed in
+ * {@link CameraCharacteristics#SHARED_SESSION_CONFIGURATION}. Using different configuration values
+ * when creating session will result in an {@link IllegalArgumentException}.</p>
+ *
+ * <p>When camera is opened in shared mode, the highest priority client among all the clients will
+ * be the primary client while the others would be secondary clients. Clients will know if they are
+ * primary or secondary by the device state callback
+ * {@link CameraDevice.StateCallback#onOpenedInSharedMode}. Once the camera has been opened in
+ * shared mode, their access priorities of being a primary or secondary client can change if
+ * another higher priority client opens the camera later. Once the camera has been opened,
+ * any change in primary client status will be shared by the device state callback
+ * {@link CameraDevice.StateCallback#onClientSharedAccessPriorityChanged}.</p>
+ *
+ * <p>The priority of client access is determined by considering two factors: its current process
+ * state and its "out of memory" score. Clients operating in the background are assigned a lower
+ * priority. In contrast, clients running in the foreground, along with system-level clients, are
+ * given a higher priority.</p>
+ *
+ * <p>Primary clients can create capture requests, modify any capture parameters and send them to
+ * the capture session for a one-shot capture or as a repeating request using the following apis:
+ * </p>
+ *
+ * <ul>
+ *
+ * <li>{@link CameraSharedCaptureSession#capture}</li>
+ *
+ * <li>{@link CameraSharedCaptureSession#captureSingleRequest}</li>
+ *
+ * <li>{@link CameraSharedCaptureSession#setRepeatingRequest}</li>
+ *
+ * <li>{@link CameraSharedCaptureSession#setSingleRepeatingRequest}</li>
+ *
+ * <li>{@link CameraSharedCaptureSession#stopRepeating}</li>
+ *
+ * </ul>
+ *
+ * <p>Secondary clients cannot create a capture request and modify any capture parameters. However,
+ * they can start the camera streaming to desired surface targets using
+ * {@link CameraSharedCaptureSession#startStreaming}, which will apply default parameters. Once the
+ * streaming has successfully started, then they can stop the streaming using
+ * {@link CameraSharedCaptureSession#stopStreaming}.</p>
+ *
+ * <p>The following APIs are not supported in shared capture sessions by either the primary or
+ * secondary client.</p>
+ *
+ * <ul>
+ *
+ * <li>{@link CameraSharedCaptureSession#captureBurst}</li>
+ *
+ * <li>{@link CameraSharedCaptureSession#captureBurstRequests}</li>
+ *
+ * <li>{@link CameraSharedCaptureSession#setRepeatingBurst}</li>
+ *
+ * <li>{@link CameraSharedCaptureSession#setRepeatingBurstRequests}</li>
+ *
+ * <li>{@link CameraSharedCaptureSession#switchToOffline}</li>
+ *
+ * <li>{@link CameraSharedCaptureSession#updateOutputConfiguration}</li>
+ *
+ * <li>{@link CameraSharedCaptureSession#finalizeOutputConfigurations}</li>
+ *
+ * <li>{@link CameraSharedCaptureSession#prepare}</li>
+ *
+ * </ul>
+ *
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_CAMERA_MULTI_CLIENT)
+@SystemApi
+public abstract class CameraSharedCaptureSession extends CameraCaptureSession {
+
+ /**
+ * Request start of the streaming of camera images by this shared capture session.
+ *
+ * <p>With this method, the camera device will continually capture images
+ * using the settings provided by primary client if there is ongoing repeating request
+ * by the primary client or default settings if no ongoing streaming request in progress.</p>
+ *
+ * <p> startStreaming has lower priority than the capture requests submitted
+ * through {@link #capture} by primary client, so if {@link #capture} is called when a
+ * streaming is active, the capture request will be processed before any further
+ * streaming requests are processed.</p>
+ *
+ * <p>To stop the streaming, call {@link #stopStreaming}</p>
+ *
+ * <p>Calling this method will replace any earlier streaming set up by this method.</p>
+ *
+ * @param surfaces List of target surfaces to use for streaming.
+ * @param executor The executor which will be used for invoking the listener.
+ * @param listener The callback object to notify the status and progress of the image capture.
+ *
+ * @return int A unique capture sequence ID used by
+ * {@link CaptureCallback#onCaptureSequenceCompleted}.
+ *
+ * @throws CameraAccessException if the camera device is no longer connected or has
+ * encountered a fatal error
+ * @throws IllegalStateException if this session is no longer active, either because the session
+ * was explicitly closed, a new session has been created
+ * or the camera device has been closed.
+ * @throws IllegalArgumentException If the request references no surfaces or references surfaces
+ * that are not currently configured as outputs; or
+ * the executor is null, or the listener is null.
+ * @see #stopStreaming
+ *
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_CAMERA_MULTI_CLIENT)
+ @SystemApi
+ public abstract int startStreaming(@NonNull List<Surface> surfaces,
+ @NonNull @CallbackExecutor Executor executor, @NonNull CaptureCallback listener)
+ throws CameraAccessException;
+
+ /**
+ * <p>Cancel any ongoing streaming started by {@link #startStreaming}</p>
+ *
+ * @throws CameraAccessException if the camera device is no longer connected or has
+ * encountered a fatal error
+ * @throws IllegalStateException if this session is no longer active, either because the session
+ * was explicitly closed, a new session has been created
+ * or the camera device has been closed.
+ *
+ * @see #startStreaming
+ *
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_CAMERA_MULTI_CLIENT)
+ @SystemApi
+ public abstract void stopStreaming() throws CameraAccessException;
+}
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index 8407258..ea70abb 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -18,6 +18,7 @@
import static com.android.internal.util.function.pooled.PooledLambda.obtainRunnable;
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.compat.CompatChanges;
@@ -41,12 +42,15 @@
import android.hardware.camera2.ICameraDeviceUser;
import android.hardware.camera2.ICameraOfflineSession;
import android.hardware.camera2.TotalCaptureResult;
+import android.hardware.camera2.params.DynamicRangeProfiles;
import android.hardware.camera2.params.ExtensionSessionConfiguration;
import android.hardware.camera2.params.InputConfiguration;
import android.hardware.camera2.params.MultiResolutionStreamConfigurationMap;
import android.hardware.camera2.params.MultiResolutionStreamInfo;
import android.hardware.camera2.params.OutputConfiguration;
import android.hardware.camera2.params.SessionConfiguration;
+import android.hardware.camera2.params.SharedSessionConfiguration;
+import android.hardware.camera2.params.SharedSessionConfiguration.SharedOutputConfiguration;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.hardware.camera2.utils.SubmitInfo;
import android.hardware.camera2.utils.SurfaceUtils;
@@ -188,6 +192,8 @@
private ExecutorService mOfflineSwitchService;
private CameraOfflineSessionImpl mOfflineSessionImpl;
+ private boolean mSharedMode;
+ private boolean mIsPrimaryClient;
// Runnables for all state transitions, except error, which needs the
// error code argument
@@ -208,6 +214,25 @@
}
};
+ private final Runnable mCallOnOpenedInSharedMode = new Runnable() {
+ @Override
+ public void run() {
+ if (!Flags.cameraMultiClient()) {
+ return;
+ }
+ StateCallbackKK sessionCallback = null;
+ synchronized (mInterfaceLock) {
+ if (mRemoteDevice == null) return; // Camera already closed
+
+ sessionCallback = mSessionStateCallback;
+ }
+ if (sessionCallback != null) {
+ sessionCallback.onOpenedInSharedMode(CameraDeviceImpl.this, mIsPrimaryClient);
+ }
+ mDeviceCallback.onOpenedInSharedMode(CameraDeviceImpl.this, mIsPrimaryClient);
+ }
+ };
+
private final Runnable mCallOnUnconfigured = new Runnable() {
@Override
public void run() {
@@ -322,6 +347,32 @@
}
});
}
+
+ public void onOpenedInSharedMode(@NonNull CameraDevice camera, boolean primaryClient) {
+ if (!Flags.cameraMultiClient()) {
+ return;
+ }
+ mClientExecutor.execute(new Runnable() {
+ @Override
+ public void run() {
+ mClientStateCallback.onOpenedInSharedMode(camera, primaryClient);
+ }
+ });
+ }
+
+ public void onClientSharedAccessPriorityChanged(@NonNull CameraDevice camera,
+ boolean primaryClient) {
+ if (!Flags.cameraMultiClient()) {
+ return;
+ }
+ mClientExecutor.execute(new Runnable() {
+ @Override
+ public void run() {
+ mClientStateCallback.onClientSharedAccessPriorityChanged(camera, primaryClient);
+ }
+ });
+ }
+
@Override
public void onOpened(@NonNull CameraDevice camera) {
mClientExecutor.execute(new Runnable() {
@@ -358,7 +409,8 @@
@NonNull CameraManager manager,
int appTargetSdkVersion,
Context ctx,
- @Nullable CameraDevice.CameraDeviceSetup cameraDeviceSetup) {
+ @Nullable CameraDevice.CameraDeviceSetup cameraDeviceSetup,
+ boolean sharedMode) {
if (cameraId == null || callback == null || executor == null || characteristics == null
|| manager == null) {
throw new IllegalArgumentException("Null argument given");
@@ -375,6 +427,7 @@
mAppTargetSdkVersion = appTargetSdkVersion;
mContext = ctx;
mCameraDeviceSetup = cameraDeviceSetup;
+ mSharedMode = sharedMode;
final int MAX_TAG_LEN = 23;
String tag = String.format("CameraDevice-JV-%s", mCameraId);
@@ -438,7 +491,12 @@
}
}
- mDeviceExecutor.execute(mCallOnOpened);
+ if (Flags.cameraMultiClient() && mSharedMode) {
+ mIsPrimaryClient = mRemoteDevice.isPrimaryClient();
+ mDeviceExecutor.execute(mCallOnOpenedInSharedMode);
+ } else {
+ mDeviceExecutor.execute(mCallOnOpened);
+ }
mDeviceExecutor.execute(mCallOnUnconfigured);
mRemoteDeviceInit = true;
@@ -576,7 +634,11 @@
stopRepeating();
try {
- waitUntilIdle();
+ // if device is opened in shared mode, there can be multiple clients accessing the
+ // camera device. So do not wait for idle if the device is opened in shared mode.
+ if (!mSharedMode) {
+ waitUntilIdle();
+ }
mRemoteDevice.beginConfigure();
@@ -764,6 +826,54 @@
checkAndWrapHandler(handler), operatingMode, /*sessionParams*/ null);
}
+ private boolean checkSharedOutputConfiguration(OutputConfiguration outConfig) {
+ if (!Flags.cameraMultiClient()) {
+ return false;
+ }
+ SharedSessionConfiguration sharedSessionConfiguration =
+ mCharacteristics.get(CameraCharacteristics.SHARED_SESSION_CONFIGURATION);
+ if (sharedSessionConfiguration == null) {
+ return false;
+ }
+
+ List<SharedOutputConfiguration> sharedConfigs =
+ sharedSessionConfiguration.getOutputStreamsInformation();
+ for (SharedOutputConfiguration sharedConfig : sharedConfigs) {
+ if (outConfig.getConfiguredSize().equals(sharedConfig.getSize())
+ && (outConfig.getConfiguredFormat() == sharedConfig.getFormat())
+ && (outConfig.getSurfaceGroupId() == OutputConfiguration.SURFACE_GROUP_ID_NONE)
+ && (outConfig.getSurfaceType() == sharedConfig.getSurfaceType())
+ && (outConfig.getMirrorMode() == sharedConfig.getMirrorMode())
+ && (outConfig.getUsage() == sharedConfig.getUsage())
+ && (outConfig.isReadoutTimestampEnabled()
+ == sharedConfig.isReadoutTimestampEnabled())
+ && (outConfig.getTimestampBase() == sharedConfig.getTimestampBase())
+ && (outConfig.getStreamUseCase() == sharedConfig.getStreamUseCase())
+ && (outConfig.getColorSpace().equals(
+ sharedSessionConfiguration.getColorSpace()))
+ && (outConfig.getDynamicRangeProfile()
+ == DynamicRangeProfiles.STANDARD)
+ && (outConfig.getConfiguredDataspace() == sharedConfig.getDataspace())
+ && (Objects.equals(outConfig.getPhysicalCameraId(),
+ sharedConfig.getPhysicalCameraId()))
+ && (outConfig.getSensorPixelModes().isEmpty())
+ && (!outConfig.isShared())) {
+ //Found valid config, return true
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean checkSharedSessionConfiguration(List<OutputConfiguration> outputConfigs) {
+ for (OutputConfiguration out : outputConfigs) {
+ if (!checkSharedOutputConfiguration(out)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
@Override
public void createCaptureSession(SessionConfiguration config)
throws CameraAccessException {
@@ -778,6 +888,14 @@
if (config.getExecutor() == null) {
throw new IllegalArgumentException("Invalid executor");
}
+ if (mSharedMode) {
+ if (config.getSessionType() != SessionConfiguration.SESSION_SHARED) {
+ throw new IllegalArgumentException("Invalid session type");
+ }
+ if (!checkSharedSessionConfiguration(outputConfigs)) {
+ throw new IllegalArgumentException("Invalid output configurations");
+ }
+ }
createCaptureSessionInternal(config.getInputConfiguration(), outputConfigs,
config.getStateCallback(), config.getExecutor(), config.getSessionType(),
config.getSessionParameters());
@@ -801,6 +919,11 @@
throw new IllegalArgumentException("Constrained high speed session doesn't support"
+ " input configuration yet.");
}
+ boolean isSharedSession = (operatingMode == ICameraDeviceUser.SHARED_MODE);
+ if (isSharedSession && inputConfig != null) {
+ throw new IllegalArgumentException("Shared capture session doesn't support"
+ + " input configuration yet.");
+ }
if (mCurrentExtensionSession != null) {
mCurrentExtensionSession.commitStats();
@@ -860,6 +983,10 @@
newSession = new CameraConstrainedHighSpeedCaptureSessionImpl(mNextSessionId++,
callback, executor, this, mDeviceExecutor, configureSuccess,
mCharacteristics);
+ } else if (isSharedSession) {
+ newSession = new CameraSharedCaptureSessionImpl(mNextSessionId++,
+ callback, executor, this, mDeviceExecutor, configureSuccess,
+ mIsPrimaryClient);
} else {
newSession = new CameraCaptureSessionImpl(mNextSessionId++, input,
callback, executor, this, mDeviceExecutor, configureSuccess);
@@ -1882,6 +2009,40 @@
}
}
+ /**
+ * Callback when client access priorities change when camera is opened in shared mode.
+ */
+ @FlaggedApi(Flags.FLAG_CAMERA_MULTI_CLIENT)
+ public void onClientSharedAccessPriorityChanged(boolean primaryClient) {
+ if (DEBUG) {
+ Log.d(TAG, String.format(
+ "onClientSharedAccessPriorityChanged received, primary client = "
+ + primaryClient));
+ }
+ synchronized (mInterfaceLock) {
+ if (mRemoteDevice == null && mRemoteDeviceInit) {
+ return; // Camera already closed, user is not interested in this callback anymore.
+ }
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mDeviceExecutor.execute(obtainRunnable(
+ CameraDeviceImpl::notifyClientSharedAccessPriorityChanged, this,
+ primaryClient).recycleOnUse());
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ }
+
+ @FlaggedApi(Flags.FLAG_CAMERA_MULTI_CLIENT)
+ private void notifyClientSharedAccessPriorityChanged(boolean primaryClient) {
+ if (!CameraDeviceImpl.this.isClosed()) {
+ mIsPrimaryClient = primaryClient;
+ mDeviceCallback.onClientSharedAccessPriorityChanged(CameraDeviceImpl.this,
+ primaryClient);
+ }
+ }
+
public void onDeviceError(final int errorCode, CaptureResultExtras resultExtras) {
if (DEBUG) {
Log.d(TAG, String.format(
@@ -2447,6 +2608,12 @@
}
@Override
+ @FlaggedApi(Flags.FLAG_CAMERA_MULTI_CLIENT)
+ public void onClientSharedAccessPriorityChanged(boolean primaryClient) {
+ CameraDeviceImpl.this.onClientSharedAccessPriorityChanged(primaryClient);
+ }
+
+ @Override
public void onPrepared(int streamId) {
final OutputConfiguration output;
final StateCallbackKK sessionCallback;
diff --git a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
index 1cc0856..c0a5928 100644
--- a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
+++ b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
@@ -63,6 +63,7 @@
import android.hardware.camera2.params.RecommendedStreamConfiguration;
import android.hardware.camera2.params.RecommendedStreamConfigurationMap;
import android.hardware.camera2.params.ReprocessFormatsMap;
+import android.hardware.camera2.params.SharedSessionConfiguration;
import android.hardware.camera2.params.StreamConfiguration;
import android.hardware.camera2.params.StreamConfigurationDuration;
import android.hardware.camera2.params.StreamConfigurationMap;
@@ -866,6 +867,15 @@
return (T) metadata.getLensIntrinsicSamples();
}
});
+ sGetCommandMap.put(
+ CameraCharacteristics.SHARED_SESSION_CONFIGURATION.getNativeKey(),
+ new GetCommand() {
+ @Override
+ @SuppressWarnings("unchecked")
+ public <T> T getValue(CameraMetadataNative metadata, Key<T> key) {
+ return (T) metadata.getSharedSessionConfiguration();
+ }
+ });
}
private int[] getAvailableFormats() {
@@ -1658,6 +1668,22 @@
listHighResolution);
}
+ private SharedSessionConfiguration getSharedSessionConfiguration() {
+ if (!Flags.cameraMultiClient()) {
+ return null;
+ }
+ Integer sharedSessionColorSpace = getBase(
+ CameraCharacteristics.SHARED_SESSION_COLOR_SPACE);
+ long[] sharedOutputConfigurations = getBase(
+ CameraCharacteristics.SHARED_SESSION_OUTPUT_CONFIGURATIONS);
+
+ if ((sharedSessionColorSpace == null) || (sharedOutputConfigurations == null)) {
+ return null;
+ }
+
+ return new SharedSessionConfiguration(sharedSessionColorSpace, sharedOutputConfigurations);
+ }
+
private StreamConfigurationMap getStreamConfigurationMapMaximumResolution() {
StreamConfiguration[] configurations = getBase(
CameraCharacteristics.SCALER_AVAILABLE_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION);
diff --git a/core/java/android/hardware/camera2/impl/CameraOfflineSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraOfflineSessionImpl.java
index eb2ff88..1769c46 100644
--- a/core/java/android/hardware/camera2/impl/CameraOfflineSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraOfflineSessionImpl.java
@@ -16,11 +16,13 @@
package android.hardware.camera2.impl;
+import static com.android.internal.util.Preconditions.checkNotNull;
+
+import android.annotation.FlaggedApi;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
-import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CameraOfflineSession;
import android.hardware.camera2.CameraOfflineSession.CameraOfflineSessionCallback;
import android.hardware.camera2.CaptureFailure;
@@ -40,15 +42,15 @@
import android.util.SparseArray;
import android.view.Surface;
+import com.android.internal.camera.flags.Flags;
+
import java.util.AbstractMap.SimpleEntry;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
-import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.Executor;
-
-import static com.android.internal.util.Preconditions.*;
+import java.util.concurrent.atomic.AtomicBoolean;
public class CameraOfflineSessionImpl extends CameraOfflineSession
implements IBinder.DeathRecipient {
@@ -176,6 +178,12 @@
}
@Override
+ @FlaggedApi(Flags.FLAG_CAMERA_MULTI_CLIENT)
+ public void onClientSharedAccessPriorityChanged(boolean primaryClient) {
+ Log.v(TAG, "onClientSharedAccessPriorityChanged primaryClient = " + primaryClient);
+ }
+
+ @Override
public void onDeviceIdle() {
synchronized(mInterfaceLock) {
if (mRemoteSession == null) {
diff --git a/core/java/android/hardware/camera2/impl/CameraSharedCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraSharedCaptureSessionImpl.java
new file mode 100644
index 0000000..a1f31c0
--- /dev/null
+++ b/core/java/android/hardware/camera2/impl/CameraSharedCaptureSessionImpl.java
@@ -0,0 +1,242 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.camera2.impl;
+
+import android.annotation.FlaggedApi;
+import android.hardware.camera2.CameraAccessException;
+import android.hardware.camera2.CameraCaptureSession;
+import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CameraSharedCaptureSession;
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.params.OutputConfiguration;
+import android.os.ConditionVariable;
+import android.os.Handler;
+import android.view.Surface;
+
+import com.android.internal.camera.flags.Flags;
+
+import java.util.List;
+import java.util.concurrent.Executor;
+
+/**
+ * Standard implementation of CameraSharedCaptureSession.
+ *
+ * <p>
+ * Mostly just forwards calls to an instance of CameraCaptureSessionImpl,
+ * but implements the few necessary behavior changes and additional methods required
+ * for the shared session mode.
+ * </p>
+ */
+@FlaggedApi(Flags.FLAG_CAMERA_MULTI_CLIENT)
+public class CameraSharedCaptureSessionImpl
+ extends CameraSharedCaptureSession implements CameraCaptureSessionCore {
+ private static final String TAG = "CameraSharedCaptureSessionImpl";
+ private final CameraCaptureSessionImpl mSessionImpl;
+ private final ConditionVariable mInitialized = new ConditionVariable();
+ private boolean mIsPrimary;
+
+ /**
+ * Create a new CameraCaptureSession.
+ */
+ CameraSharedCaptureSessionImpl(int id,
+ CameraCaptureSession.StateCallback callback, Executor stateExecutor,
+ android.hardware.camera2.impl.CameraDeviceImpl deviceImpl,
+ Executor deviceStateExecutor, boolean configureSuccess, boolean isPrimary) {
+ CameraCaptureSession.StateCallback wrapperCallback = new WrapperCallback(callback);
+ mSessionImpl = new CameraCaptureSessionImpl(id, /*input*/null, wrapperCallback,
+ stateExecutor, deviceImpl, deviceStateExecutor, configureSuccess);
+ mIsPrimary = isPrimary;
+ mInitialized.open();
+ }
+
+ @Override
+ public int startStreaming(List<Surface> surfaces, Executor executor, CaptureCallback listener)
+ throws CameraAccessException {
+ // Todo: Need to add implementation.
+ return 0;
+ }
+
+ @Override
+ public void stopStreaming() throws CameraAccessException {
+ // Todo: Need to add implementation.
+ }
+
+ @Override
+ public void close() {
+ mSessionImpl.close();
+ }
+
+ @Override
+ public Surface getInputSurface() {
+ return null;
+ }
+
+ @Override
+ public boolean isReprocessable() {
+ return false;
+ }
+
+ @Override
+ public void abortCaptures() throws CameraAccessException {
+ if (mIsPrimary) {
+ mSessionImpl.abortCaptures();
+ }
+ }
+
+ @Override
+ public int setRepeatingRequest(CaptureRequest request, CaptureCallback listener,
+ Handler handler) throws CameraAccessException {
+ if (mIsPrimary) {
+ return mSessionImpl.setRepeatingRequest(request, listener, handler);
+ }
+ throw new UnsupportedOperationException("Shared capture session only supports this method"
+ + " for primary clients");
+ }
+
+ @Override
+ public void stopRepeating() throws CameraAccessException {
+ if (mIsPrimary) {
+ mSessionImpl.stopRepeating();
+ }
+ }
+
+ @Override
+ public int capture(CaptureRequest request, CaptureCallback listener, Handler handler)
+ throws CameraAccessException {
+ if (mIsPrimary) {
+ return mSessionImpl.capture(request, listener, handler);
+ }
+ throw new UnsupportedOperationException("Shared capture session only supports this method"
+ + " for primary clients");
+ }
+
+ @Override
+ public void tearDown(Surface surface) throws CameraAccessException {
+ mSessionImpl.tearDown(surface);
+ }
+
+ @Override
+ public CameraDevice getDevice() {
+ return mSessionImpl.getDevice();
+ }
+
+ @Override
+ public boolean isAborting() {
+ return mSessionImpl.isAborting();
+ }
+
+ @Override
+ public CameraDeviceImpl.StateCallbackKK getDeviceStateCallback() {
+ return mSessionImpl.getDeviceStateCallback();
+ }
+
+ @Override
+ public void replaceSessionClose() {
+ mSessionImpl.replaceSessionClose();
+ }
+
+ @Override
+ public int setRepeatingBurst(List<CaptureRequest> requests, CaptureCallback listener,
+ Handler handler) throws CameraAccessException {
+ throw new UnsupportedOperationException("Shared Capture session doesn't support"
+ + " this method");
+ }
+
+ @Override
+ public int captureBurst(List<CaptureRequest> requests, CaptureCallback listener,
+ Handler handler) throws CameraAccessException {
+ throw new UnsupportedOperationException("Shared Capture session doesn't support"
+ + " this method");
+ }
+
+ @Override
+ public void updateOutputConfiguration(OutputConfiguration config)
+ throws CameraAccessException {
+ throw new UnsupportedOperationException("Shared capture session doesn't support"
+ + " this method");
+ }
+
+ @Override
+ public void finalizeOutputConfigurations(List<OutputConfiguration> deferredOutputConfigs)
+ throws CameraAccessException {
+ throw new UnsupportedOperationException("Shared capture session doesn't support"
+ + " this method");
+ }
+
+ @Override
+ public void prepare(Surface surface) throws CameraAccessException {
+ throw new UnsupportedOperationException("Shared capture session doesn't support"
+ + " this method");
+ }
+
+ @Override
+ public void prepare(int maxCount, Surface surface) throws CameraAccessException {
+ throw new UnsupportedOperationException("Shared capture session doesn't support"
+ + " this method");
+ }
+
+ @Override
+ public void closeWithoutDraining() {
+ throw new UnsupportedOperationException("Shared capture session doesn't support"
+ + " this method");
+ }
+
+ private class WrapperCallback extends StateCallback {
+ private final StateCallback mCallback;
+
+ WrapperCallback(StateCallback callback) {
+ mCallback = callback;
+ }
+
+ @Override
+ public void onConfigured(CameraCaptureSession session) {
+ mInitialized.block();
+ mCallback.onConfigured(CameraSharedCaptureSessionImpl.this);
+ }
+
+ @Override
+ public void onConfigureFailed(CameraCaptureSession session) {
+ mInitialized.block();
+ mCallback.onConfigureFailed(CameraSharedCaptureSessionImpl.this);
+ }
+
+ @Override
+ public void onReady(CameraCaptureSession session) {
+ mCallback.onReady(CameraSharedCaptureSessionImpl.this);
+ }
+
+ @Override
+ public void onActive(CameraCaptureSession session) {
+ mCallback.onActive(CameraSharedCaptureSessionImpl.this);
+ }
+
+ @Override
+ public void onCaptureQueueEmpty(CameraCaptureSession session) {
+ mCallback.onCaptureQueueEmpty(CameraSharedCaptureSessionImpl.this);
+ }
+
+ @Override
+ public void onClosed(CameraCaptureSession session) {
+ mCallback.onClosed(CameraSharedCaptureSessionImpl.this);
+ }
+
+ @Override
+ public void onSurfacePrepared(CameraCaptureSession session, Surface surface) {
+ mCallback.onSurfacePrepared(CameraSharedCaptureSessionImpl.this,
+ surface);
+ }
+ }
+}
diff --git a/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java b/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java
index aec2cff..831c75ec 100644
--- a/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java
+++ b/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java
@@ -301,4 +301,16 @@
}
}
+ /**
+ * API to check if the client is primary client when camera device is opened in shared mode.
+ */
+ public boolean isPrimaryClient() throws CameraAccessException {
+ try {
+ return mRemoteDevice.isPrimaryClient();
+ } catch (ServiceSpecificException e) {
+ throw ExceptionUtils.throwAsPublicException(e);
+ } catch (RemoteException e) {
+ throw ExceptionUtils.throwAsPublicException(e);
+ }
+ }
}
diff --git a/core/java/android/hardware/camera2/params/OutputConfiguration.java b/core/java/android/hardware/camera2/params/OutputConfiguration.java
index d38be9b..e12c463 100644
--- a/core/java/android/hardware/camera2/params/OutputConfiguration.java
+++ b/core/java/android/hardware/camera2/params/OutputConfiguration.java
@@ -29,6 +29,7 @@
import android.graphics.ColorSpace;
import android.graphics.ImageFormat;
import android.graphics.ImageFormat.Format;
+import android.hardware.DataSpace.NamedDataSpace;
import android.hardware.HardwareBuffer;
import android.hardware.HardwareBuffer.Usage;
import android.hardware.camera2.CameraCaptureSession;
@@ -1729,6 +1730,79 @@
}
/**
+ * Get the configured format associated with this {@link OutputConfiguration}.
+ *
+ * @return {@link android.graphics.ImageFormat#Format} associated with this
+ * {@link OutputConfiguration}.
+ *
+ * @hide
+ */
+ public @Format int getConfiguredFormat() {
+ return mConfiguredFormat;
+ }
+
+ /**
+ * Get the usage flag associated with this {@link OutputConfiguration}.
+ *
+ * @return {@link HardwareBuffer#Usage} associated with this {@link OutputConfiguration}.
+ *
+ * @hide
+ */
+ public @Usage long getUsage() {
+ return mUsage;
+ }
+
+ /**
+ * Get the surface type associated with this {@link OutputConfiguration}.
+ *
+ * @return The surface type associated with this {@link OutputConfiguration}.
+ *
+ * @see #SURFACE_TYPE_SURFACE_VIEW
+ * @see #SURFACE_TYPE_SURFACE_TEXTURE
+ * @see #SURFACE_TYPE_MEDIA_RECORDER
+ * @see #SURFACE_TYPE_MEDIA_CODEC
+ * @see #SURFACE_TYPE_IMAGE_READER
+ * @see #SURFACE_TYPE_UNKNOWN
+ * @hide
+ */
+ public int getSurfaceType() {
+ return mSurfaceType;
+ }
+
+ /**
+ * Get the sensor pixel modes associated with this {@link OutputConfiguration}.
+ *
+ * @return List of {@link #SensorPixelMode} associated with this {@link OutputConfiguration}.
+ *
+ * @hide
+ */
+ public @NonNull List<Integer> getSensorPixelModes() {
+ return mSensorPixelModesUsed;
+ }
+
+ /**
+ * Get the sharing mode associated with this {@link OutputConfiguration}.
+ *
+ * @return true if surface sharing is enabled with this {@link OutputConfiguration}.
+ *
+ * @hide
+ */
+ public boolean isShared() {
+ return mIsShared;
+ }
+
+ /**
+ * Get the dataspace associated with this {@link OutputConfiguration}.
+ *
+ * @return {@link Dataspace#NamedDataSpace} for this {@link OutputConfiguration}.
+ *
+ * @hide
+ */
+ public @NamedDataSpace int getConfiguredDataspace() {
+ return mConfiguredDataspace;
+ }
+
+ /**
* Get the physical camera ID associated with this {@link OutputConfiguration}.
*
* <p>If this OutputConfiguration isn't targeting a physical camera of a logical
diff --git a/core/java/android/hardware/camera2/params/SessionConfiguration.java b/core/java/android/hardware/camera2/params/SessionConfiguration.java
index 50c6b5b..82aa64b 100644
--- a/core/java/android/hardware/camera2/params/SessionConfiguration.java
+++ b/core/java/android/hardware/camera2/params/SessionConfiguration.java
@@ -23,6 +23,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
import android.graphics.ColorSpace;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCharacteristics;
@@ -78,6 +79,19 @@
CameraDevice.SESSION_OPERATION_MODE_CONSTRAINED_HIGH_SPEED;
/**
+ * A shared session type containing instances of {@link OutputConfiguration} from a set of
+ * predefined stream configurations. A shared session can be shared among multiple clients.
+ * Shared session does not have any {@link InputConfiguration} as it does not support
+ * reprocessable sessions.
+ *
+ * @see CameraDevice#createCaptureSession(SessionConfiguration)
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_CAMERA_MULTI_CLIENT)
+ @SystemApi
+ public static final int SESSION_SHARED = CameraDevice.SESSION_OPERATION_MODE_SHARED;
+
+ /**
* First vendor-specific session mode
* @hide
*/
diff --git a/core/java/android/hardware/camera2/params/SharedSessionConfiguration.java b/core/java/android/hardware/camera2/params/SharedSessionConfiguration.java
new file mode 100644
index 0000000..cdcc92c
--- /dev/null
+++ b/core/java/android/hardware/camera2/params/SharedSessionConfiguration.java
@@ -0,0 +1,312 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.camera2.params;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
+import android.graphics.ColorSpace;
+import android.graphics.ImageFormat.Format;
+import android.hardware.DataSpace.NamedDataSpace;
+import android.hardware.HardwareBuffer.Usage;
+import android.hardware.camera2.params.OutputConfiguration.MirrorMode;
+import android.hardware.camera2.params.OutputConfiguration.StreamUseCase;
+import android.hardware.camera2.params.OutputConfiguration.TimestampBase;
+import android.util.Log;
+import android.util.Size;
+
+import com.android.internal.camera.flags.Flags;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Immutable class to store the shared session configuration
+ * {@link CameraCharacteristics#SHARED_SESSION_CONFIGURATION} to set up
+ * {@link android.view.Surface Surfaces} for creating a
+ * {@link android.hardware.camera2.CameraSharedCaptureSession capture session} using
+ * {@link android.hardware.camera2.CameraDevice#createCaptureSession(SessionConfiguration)} and
+ * {@link android.hardware.camera2.params.SessionConfiguration#SESSION_SHARED
+ * shared capture session} when camera has been opened in shared mode using
+ * {@link #openSharedCamera(String, Executor, StateCallback)}.
+ *
+ * <p>This is the authoritative list for all output configurations that are supported by a camera
+ * device when opened in shared mode.</p>
+ *
+ * <p>An instance of this object is available from {@link CameraCharacteristics} using
+ * the {@link CameraCharacteristics#SHARED_SESSION_CONFIGURATION} key and the
+ * {@link CameraCharacteristics#get} method.</p>
+ *
+ * <pre><code>{@code
+ * CameraCharacteristics characteristics = cameraManager.getCameraCharacteristics(cameraId);
+ * StreamConfigurationMap configs = characteristics.get(
+ * CameraCharacteristics.SHARED_SESSION_CONFIGURATION);
+ * }</code></pre>
+ *
+ * @see CameraCharacteristics#SHARED_SESSION_CONFIGURATION
+ * @see CameraDevice#createCaptureSession(SessionConfiguration)
+ * @see SessionConfiguration#SESSION_SHARED
+ * @see CameraManager#openSharedCamera(String, Executor, StateCallback)
+ *
+ * @hide
+ */
+@SystemApi
+@FlaggedApi(Flags.FLAG_CAMERA_MULTI_CLIENT)
+public final class SharedSessionConfiguration {
+ private static final String TAG = "SharedSessionConfiguration";
+ // Metadata android.info.availableSharedOutputConfigurations has list of shared output
+ // configurations. Each output configuration has minimum of 11 entries of size long
+ // followed by the physical camera id if present.
+ // See android.info.availableSharedOutputConfigurations for details.
+ private static final int SHARED_OUTPUT_CONFIG_NUM_OF_ENTRIES = 11;
+ /**
+ * Immutable class to store shared output stream information.
+ */
+ public static final class SharedOutputConfiguration {
+ private final int mSurfaceType;
+ private final Size mSize;
+ private final int mFormat;
+ private final int mDataspace;
+ private final long mStreamUseCase;
+ private String mPhysicalCameraId;
+ private final long mUsage;
+ private int mTimestampBase;
+ private int mMirrorMode;
+ private boolean mReadoutTimestampEnabled;
+
+ /**
+ * Create a new {@link SharedOutputConfiguration}.
+ *
+ * @param surfaceType Surface Type for this output configuration.
+ * @param sz Size for this output configuration.
+ * @param format {@link android.graphics.ImageFormat#Format} associated with this
+ * {@link OutputConfiguration}.
+ * @param mirrorMode {@link OutputConfiguration#MirrorMode} for this output configuration.
+ * @param readoutTimeStampEnabled Flag indicating whether readout timestamp is enabled
+ * for this output configuration.
+ * @param timestampBase {@link OutputConfiguration#TimestampBase} for this output
+ * configuration.
+ * @param dataspace {@link Dataspace#NamedDataSpace} for this output configuration.
+ * @param usage {@link HardwareBuffer#Usage} for this output configuration.
+ * @param streamUseCase {@link OutputConfiguration#StreamUseCase} for this output
+ * configuration.
+ * @param physicalCamId Physical Camera Id for this output configuration.
+ *
+ * @hide
+ */
+ public SharedOutputConfiguration(int surfaceType, @NonNull Size sz, @Format int format,
+ @MirrorMode int mirrorMode, boolean readoutTimeStampEnabled,
+ @TimestampBase int timestampBase, @NamedDataSpace int dataspace, @Usage long usage,
+ @StreamUseCase long streamUseCase, @Nullable String physicalCamId) {
+ mSurfaceType = surfaceType;
+ mSize = sz;
+ mFormat = format;
+ mMirrorMode = mirrorMode;
+ mReadoutTimestampEnabled = readoutTimeStampEnabled;
+ mTimestampBase = timestampBase;
+ mDataspace = dataspace;
+ mUsage = usage;
+ mStreamUseCase = streamUseCase;
+ mPhysicalCameraId = physicalCamId;
+ }
+
+ /**
+ * Returns the surface type configured for the shared output configuration.
+ * @return SURFACE_TYPE_UNKNOWN = -1
+ * SURFACE_TYPE_SURFACE_VIEW = 0
+ * SURFACE_TYPE_SURFACE_TEXTURE = 1
+ * SURFACE_TYPE_MEDIA_RECORDER = 2
+ * SURFACE_TYPE_MEDIA_CODEC = 3
+ * SURFACE_TYPE_IMAGE_READER = 4
+ */
+ public int getSurfaceType() {
+ return mSurfaceType;
+ }
+
+ /**
+ * Returns the format of the shared output configuration.
+ * @return format The format of the configured output. This must be one of the
+ * {@link android.graphics.ImageFormat} or {@link android.graphics.PixelFormat}
+ * constants. Note that not all formats are supported by the camera device.
+ */
+ public @Format int getFormat() {
+ return mFormat;
+ }
+
+ /**
+ * Returns the configured size for the shared output configuration.
+ * @return surfaceSize Size for the shared output configuration
+ *
+ */
+ public @NonNull Size getSize() {
+ return mSize;
+ }
+
+ /**
+ * Return datatspace configured for the shared output configuration.
+ *
+ * @return {@link Dataspace#NamedDataSpace} configured for shared session
+ */
+ public @NamedDataSpace int getDataspace() {
+ return mDataspace;
+ }
+
+ /**
+ * Get the mirroring mode configured for the shared output configuration.
+ *
+ * @return {@link OutputConfiguration#MirrorMode} configured for the shared session
+ */
+ public @MirrorMode int getMirrorMode() {
+ return mMirrorMode;
+ }
+
+ /**
+ * Get the stream use case configured for the shared output configuration.
+ *
+ * @return {@link OutputConfiguration#StreamUseCase} configured for the shared session
+ */
+ public @StreamUseCase long getStreamUseCase() {
+ return mStreamUseCase;
+ }
+
+ /**
+ * Get the timestamp base configured for the shared output configuration.
+ *
+ * @return {@link OutputConfiguration#TimestampBase} configured for the shared session
+ */
+ public @TimestampBase int getTimestampBase() {
+ return mTimestampBase;
+ }
+
+ /** Whether readout timestamp is used for this shared output configuration.
+ *
+ */
+ public boolean isReadoutTimestampEnabled() {
+ return mReadoutTimestampEnabled;
+ }
+
+ /** Returns the usage if set for this shared output configuration.
+ *
+ * @return {@link HardwareBuffer#Usage} flags if set for shared output configuration with
+ * the ImageReader output surface.
+ */
+ public @Usage long getUsage() {
+ return mUsage;
+ }
+
+ public @Nullable String getPhysicalCameraId() {
+ return mPhysicalCameraId;
+ }
+ }
+
+ /**
+ * Create a new {@link SharedSessionConfiguration}.
+ *
+ * <p>The array parameters ownership is passed to this object after creation; do not
+ * write to them after this constructor is invoked.</p>
+ *
+ * @param sharedColorSpace the colorspace to be used for the shared output configurations.
+ * @param sharedOutputConfigurations a non-{@code null} array of metadata
+ * android.info.availableSharedOutputConfigurations
+ *
+ * @hide
+ */
+ public SharedSessionConfiguration(int sharedColorSpace,
+ @NonNull long[] sharedOutputConfigurations) {
+ mColorSpace = sharedColorSpace;
+ byte physicalCameraIdLen;
+ int surfaceType, width, height, format, mirrorMode, timestampBase, dataspace;
+ long usage, streamUseCase;
+ boolean isReadOutTimestampEnabled;
+ // Parse metadata android.info.availableSharedOutputConfigurations which contains
+ // list of shared output configurations.
+ int numOfEntries = sharedOutputConfigurations.length;
+ int i = 0;
+ while (numOfEntries >= SharedSessionConfiguration.SHARED_OUTPUT_CONFIG_NUM_OF_ENTRIES) {
+ surfaceType = (int) sharedOutputConfigurations[i];
+ width = (int) sharedOutputConfigurations[i + 1];
+ height = (int) sharedOutputConfigurations[i + 2];
+ format = (int) sharedOutputConfigurations[i + 3];
+ mirrorMode = (int) sharedOutputConfigurations[i + 4];
+ isReadOutTimestampEnabled = (sharedOutputConfigurations[i + 5] != 0);
+ timestampBase = (int) sharedOutputConfigurations[i + 6];
+ dataspace = (int) sharedOutputConfigurations[i + 7];
+ usage = sharedOutputConfigurations[i + 8];
+ streamUseCase = sharedOutputConfigurations[i + 9];
+ physicalCameraIdLen = (byte) sharedOutputConfigurations[i + 10];
+ numOfEntries -= SharedSessionConfiguration.SHARED_OUTPUT_CONFIG_NUM_OF_ENTRIES;
+ i += SharedSessionConfiguration.SHARED_OUTPUT_CONFIG_NUM_OF_ENTRIES;
+ if (numOfEntries < physicalCameraIdLen) {
+ Log.e(TAG, "Number of remaining data in shared configuration is less than"
+ + " physical camera id length . Malformed metadata"
+ + " android.info.availableSharedOutputConfigurations.");
+ break;
+ }
+ StringBuilder physicalCameraId = new StringBuilder();
+ long asciiValue;
+ for (int j = 0; j < physicalCameraIdLen; j++) {
+ asciiValue = sharedOutputConfigurations[i + j];
+ if (asciiValue == 0) { // Check for null terminator
+ break;
+ }
+ physicalCameraId.append((char) asciiValue);
+ }
+ SharedOutputConfiguration outputInfo;
+ outputInfo = new SharedOutputConfiguration(surfaceType, new Size(width, height),
+ format, mirrorMode, isReadOutTimestampEnabled, timestampBase,
+ dataspace, usage, streamUseCase, physicalCameraId.toString());
+ mOutputStreamConfigurations.add(outputInfo);
+ i += physicalCameraIdLen;
+ numOfEntries -= physicalCameraIdLen;
+ }
+ if (numOfEntries != 0) {
+ Log.e(TAG, "Unexpected entries left in shared output configuration."
+ + " Malformed metadata android.info.availableSharedOutputConfigurations.");
+ }
+ }
+
+ /**
+ * Return the shared session color space which is configured.
+ *
+ * @return the shared session color space
+ */
+ @SuppressLint("MethodNameUnits")
+ public @Nullable ColorSpace getColorSpace() {
+ if (mColorSpace != ColorSpaceProfiles.UNSPECIFIED) {
+ return ColorSpace.get(ColorSpace.Named.values()[mColorSpace]);
+ } else {
+ return null;
+ }
+ }
+ /**
+ * Get information about each shared output configuarion in the shared session.
+ *
+ * @return Non-modifiable list of output configuration.
+ *
+ */
+ public @NonNull List<SharedOutputConfiguration> getOutputStreamsInformation() {
+ return Collections.unmodifiableList(mOutputStreamConfigurations);
+ }
+
+ private int mColorSpace;
+ private final ArrayList<SharedOutputConfiguration> mOutputStreamConfigurations =
+ new ArrayList<SharedOutputConfiguration>();
+}
+
diff --git a/core/java/android/hardware/contexthub/HubDiscoveryInfo.java b/core/java/android/hardware/contexthub/HubDiscoveryInfo.java
new file mode 100644
index 0000000..581040d
--- /dev/null
+++ b/core/java/android/hardware/contexthub/HubDiscoveryInfo.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.contexthub;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.chre.flags.Flags;
+import android.hardware.location.ContextHubManager;
+
+/**
+ * Class that represents the result of from an hub endpoint discovery.
+ *
+ * <p>The type is returned from an endpoint discovery query via {@link
+ * ContextHubManager#findEndpoints}.
+ *
+ * <p>Application may use the values {@link #getHubEndpointInfo} to retrieve the {@link
+ * HubEndpointInfo} that describes the endpoint that matches the query.
+ *
+ * <p>Application may use the values {@link #getHubServiceInfo()} to retrieve the {@link
+ * HubServiceInfo} that describes the service that matches the query.
+ *
+ * @hide
+ */
+@SystemApi
+@FlaggedApi(Flags.FLAG_OFFLOAD_API)
+public class HubDiscoveryInfo {
+ @NonNull private final HubEndpointInfo mEndpointInfo;
+ @Nullable private final HubServiceInfo mServiceInfo;
+
+ /** @hide */
+ public HubDiscoveryInfo(@NonNull HubEndpointInfo endpointInfo) {
+ mEndpointInfo = endpointInfo;
+ mServiceInfo = null;
+ }
+
+ /** @hide */
+ public HubDiscoveryInfo(
+ @NonNull HubEndpointInfo endpointInfo, @NonNull HubServiceInfo serviceInfo) {
+ mEndpointInfo = endpointInfo;
+ mServiceInfo = serviceInfo;
+ }
+
+ /** Get the {@link HubEndpointInfo} for the endpoint found. */
+ @NonNull
+ public HubEndpointInfo getHubEndpointInfo() {
+ return mEndpointInfo;
+ }
+
+ /**
+ * Get the {@link HubServiceInfo} for the endpoint found. The value will be null if there is no
+ * service info specified in the query.
+ */
+ @Nullable
+ public HubServiceInfo getHubServiceInfo() {
+ return mServiceInfo;
+ }
+}
diff --git a/core/java/android/hardware/contexthub/HubEndpoint.java b/core/java/android/hardware/contexthub/HubEndpoint.java
new file mode 100644
index 0000000..078b4d4
--- /dev/null
+++ b/core/java/android/hardware/contexthub/HubEndpoint.java
@@ -0,0 +1,559 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.contexthub;
+
+import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.chre.flags.Flags;
+import android.content.Context;
+import android.hardware.location.IContextHubService;
+import android.hardware.location.IContextHubTransactionCallback;
+import android.os.RemoteException;
+import android.util.Log;
+import android.util.SparseArray;
+
+import androidx.annotation.GuardedBy;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.Executor;
+
+/**
+ * An object representing an endpoint exposed to ContextHub and VendorHub. The object encapsulates
+ * the lifecycle and message callbacks for an endpoint.
+ *
+ * @hide
+ */
+@SystemApi
+@FlaggedApi(Flags.FLAG_OFFLOAD_API)
+public class HubEndpoint {
+ private static final String TAG = "HubEndpoint";
+
+ private final Object mLock = new Object();
+ private final HubEndpointInfo mPendingHubEndpointInfo;
+ @Nullable private final IHubEndpointLifecycleCallback mLifecycleCallback;
+ @Nullable private final IHubEndpointMessageCallback mMessageCallback;
+ @NonNull private final Executor mLifecycleCallbackExecutor;
+ @NonNull private final Executor mMessageCallbackExecutor;
+
+ @GuardedBy("mLock")
+ private final SparseArray<HubEndpointSession> mActiveSessions = new SparseArray<>();
+
+ private final IContextHubEndpointCallback mServiceCallback =
+ new IContextHubEndpointCallback.Stub() {
+ @Override
+ public void onSessionOpenRequest(
+ int sessionId,
+ HubEndpointInfo initiator,
+ @Nullable HubServiceInfo serviceInfo)
+ throws RemoteException {
+ HubEndpointSession activeSession;
+ synchronized (mLock) {
+ activeSession = mActiveSessions.get(sessionId);
+ // TODO(b/378974199): Consider refactor these assertions
+ if (activeSession != null) {
+ Log.i(
+ TAG,
+ "onSessionOpenComplete: session already exists, id="
+ + sessionId);
+ return;
+ }
+ }
+
+ if (mLifecycleCallback != null) {
+ mLifecycleCallbackExecutor.execute(
+ () ->
+ processSessionOpenRequestResult(
+ sessionId,
+ initiator,
+ serviceInfo,
+ mLifecycleCallback.onSessionOpenRequest(
+ initiator, serviceInfo)));
+ }
+ }
+
+ private void processSessionOpenRequestResult(
+ int sessionId,
+ HubEndpointInfo initiator,
+ @Nullable HubServiceInfo serviceInfo,
+ HubEndpointSessionResult result) {
+ if (result == null) {
+ throw new IllegalArgumentException(
+ "HubEndpointSessionResult shouldn't be null.");
+ }
+
+ if (result.isAccepted()) {
+ acceptSession(sessionId, initiator, serviceInfo);
+ } else {
+ Log.i(
+ TAG,
+ "Session "
+ + sessionId
+ + " from "
+ + initiator
+ + " was rejected, reason="
+ + result.getReason());
+ rejectSession(sessionId);
+ }
+ }
+
+ private void acceptSession(
+ int sessionId,
+ HubEndpointInfo initiator,
+ @Nullable HubServiceInfo serviceInfo) {
+ if (mServiceToken == null || mAssignedHubEndpointInfo == null) {
+ // No longer registered?
+ return;
+ }
+
+ // Retrieve the active session
+ HubEndpointSession activeSession;
+ synchronized (mLock) {
+ activeSession = mActiveSessions.get(sessionId);
+ // TODO(b/378974199): Consider refactor these assertions
+ if (activeSession != null) {
+ Log.e(
+ TAG,
+ "onSessionOpenRequestResult: session already exists, id="
+ + sessionId);
+ return;
+ }
+
+ activeSession =
+ new HubEndpointSession(
+ sessionId,
+ HubEndpoint.this,
+ mAssignedHubEndpointInfo,
+ initiator,
+ serviceInfo);
+ try {
+ // oneway call to notify system service that the request is completed
+ mServiceToken.openSessionRequestComplete(sessionId);
+ } catch (RemoteException e) {
+ Log.e(TAG, "onSessionOpenRequestResult: ", e);
+ return;
+ }
+
+ mActiveSessions.put(sessionId, activeSession);
+ }
+
+ // Execute the callback
+ activeSession.setOpened();
+ if (mLifecycleCallback != null) {
+ final HubEndpointSession finalActiveSession = activeSession;
+ mLifecycleCallbackExecutor.execute(
+ () -> mLifecycleCallback.onSessionOpened(finalActiveSession));
+ }
+ }
+
+ private void rejectSession(int sessionId) {
+ if (mServiceToken == null || mAssignedHubEndpointInfo == null) {
+ // No longer registered?
+ return;
+ }
+
+ try {
+ mServiceToken.closeSession(
+ sessionId,
+ IHubEndpointLifecycleCallback
+ .REASON_OPEN_ENDPOINT_SESSION_REQUEST_REJECTED);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ @Override
+ public void onSessionOpenComplete(int sessionId) throws RemoteException {
+ final HubEndpointSession activeSession;
+
+ // Retrieve the active session
+ synchronized (mLock) {
+ activeSession = mActiveSessions.get(sessionId);
+ }
+ // TODO(b/378974199): Consider refactor these assertions
+ if (activeSession == null) {
+ Log.i(
+ TAG,
+ "onSessionOpenComplete: no pending session open request? id="
+ + sessionId);
+ return;
+ }
+
+ // Execute the callback
+ activeSession.setOpened();
+ if (mLifecycleCallback != null) {
+ mLifecycleCallbackExecutor.execute(
+ () -> mLifecycleCallback.onSessionOpened(activeSession));
+ }
+ }
+
+ @Override
+ public void onSessionClosed(int sessionId, int reason) throws RemoteException {
+ final HubEndpointSession activeSession;
+
+ // Retrieve the active session
+ synchronized (mLock) {
+ activeSession = mActiveSessions.get(sessionId);
+ }
+ // TODO(b/378974199): Consider refactor these assertions
+ if (activeSession == null) {
+ Log.i(TAG, "onSessionClosed: session not active, id=" + sessionId);
+ return;
+ }
+
+ // Execute the callback
+ if (mLifecycleCallback != null) {
+ mLifecycleCallbackExecutor.execute(
+ () -> {
+ mLifecycleCallback.onSessionClosed(activeSession, reason);
+
+ // Remove the session object first to call
+ activeSession.setClosed();
+ synchronized (mLock) {
+ mActiveSessions.remove(sessionId);
+ }
+ });
+ }
+ }
+
+ @Override
+ public void onMessageReceived(int sessionId, HubMessage message)
+ throws RemoteException {
+ final HubEndpointSession activeSession;
+
+ // Retrieve the active session
+ synchronized (mLock) {
+ activeSession = mActiveSessions.get(sessionId);
+ }
+ if (activeSession == null) {
+ Log.i(TAG, "onMessageReceived: session not active, id=" + sessionId);
+ }
+
+ if (activeSession == null || mMessageCallback == null) {
+ if (message.getDeliveryParams().isResponseRequired()) {
+ try {
+ mServiceToken.sendMessageDeliveryStatus(
+ sessionId,
+ message.getMessageSequenceNumber(),
+ ErrorCode.DESTINATION_NOT_FOUND);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+ return;
+ }
+
+ // Execute the callback
+ mMessageCallbackExecutor.execute(
+ () -> {
+ mMessageCallback.onMessageReceived(activeSession, message);
+ if (message.getDeliveryParams().isResponseRequired()) {
+ try {
+ mServiceToken.sendMessageDeliveryStatus(
+ sessionId,
+ message.getMessageSequenceNumber(),
+ ErrorCode.OK);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+ });
+ }
+ };
+
+ /** Binder returned from system service, non-null while registered. */
+ @Nullable private IContextHubEndpoint mServiceToken;
+
+ /** HubEndpointInfo with the assigned endpoint id from system service. */
+ @Nullable private HubEndpointInfo mAssignedHubEndpointInfo;
+
+ private HubEndpoint(
+ @NonNull HubEndpointInfo pendingEndpointInfo,
+ @Nullable IHubEndpointLifecycleCallback endpointLifecycleCallback,
+ @NonNull Executor lifecycleCallbackExecutor,
+ @Nullable IHubEndpointMessageCallback endpointMessageCallback,
+ @NonNull Executor messageCallbackExecutor) {
+ mPendingHubEndpointInfo = pendingEndpointInfo;
+
+ mLifecycleCallback = endpointLifecycleCallback;
+ mLifecycleCallbackExecutor = lifecycleCallbackExecutor;
+ mMessageCallback = endpointMessageCallback;
+ mMessageCallbackExecutor = messageCallbackExecutor;
+ }
+
+ /** @hide */
+ public void register(IContextHubService service) {
+ // TODO(b/378974199): Consider refactor these assertions
+ if (mServiceToken != null) {
+ // Already registered
+ return;
+ }
+ try {
+ IContextHubEndpoint serviceToken =
+ service.registerEndpoint(mPendingHubEndpointInfo, mServiceCallback);
+ mAssignedHubEndpointInfo = serviceToken.getAssignedHubEndpointInfo();
+ mServiceToken = serviceToken;
+ } catch (RemoteException e) {
+ Log.e(TAG, "registerEndpoint: failed to register endpoint", e);
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /** @hide */
+ public void unregister() {
+ IContextHubEndpoint serviceToken = mServiceToken;
+ // TODO(b/378974199): Consider refactor these assertions
+ if (serviceToken == null) {
+ // Not yet registered
+ return;
+ }
+
+ try {
+ synchronized (mLock) {
+ // Don't call HubEndpointSession.close() here.
+ for (int i = 0; i < mActiveSessions.size(); i++) {
+ mActiveSessions.get(mActiveSessions.keyAt(i)).setClosed();
+ }
+ mActiveSessions.clear();
+ }
+ mServiceToken.unregister();
+ } catch (RemoteException e) {
+ Log.e(TAG, "unregisterEndpoint: failed to unregister endpoint", e);
+ e.rethrowFromSystemServer();
+ } finally {
+ mServiceToken = null;
+ mAssignedHubEndpointInfo = null;
+ }
+ }
+
+ /** @hide */
+ public void openSession(HubEndpointInfo destinationInfo, @Nullable HubServiceInfo serviceInfo) {
+ // TODO(b/378974199): Consider refactor these assertions
+ if (mServiceToken == null || mAssignedHubEndpointInfo == null) {
+ // No longer registered?
+ return;
+ }
+
+ HubEndpointSession newSession;
+ try {
+ // Request system service to assign session id.
+ int sessionId = mServiceToken.openSession(destinationInfo, serviceInfo);
+
+ // Save the newly created session
+ synchronized (mLock) {
+ newSession =
+ new HubEndpointSession(
+ sessionId,
+ HubEndpoint.this,
+ destinationInfo,
+ mAssignedHubEndpointInfo,
+ serviceInfo);
+ mActiveSessions.put(sessionId, newSession);
+ }
+ } catch (RemoteException e) {
+ // Move this to toString
+ Log.e(TAG, "openSession: failed to open session to " + destinationInfo, e);
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /** @hide */
+ public void closeSession(HubEndpointSession session) {
+ IContextHubEndpoint serviceToken = mServiceToken;
+ // TODO(b/378974199): Consider refactor these assertions
+ if (serviceToken == null || mAssignedHubEndpointInfo == null) {
+ // Not registered
+ return;
+ }
+
+ synchronized (mLock) {
+ if (!mActiveSessions.contains(session.getId())) {
+ // Already closed?
+ return;
+ }
+ session.setClosed();
+ mActiveSessions.remove(session.getId());
+ }
+
+ try {
+ // Oneway notification to system service
+ serviceToken.closeSession(
+ session.getId(),
+ IHubEndpointLifecycleCallback.REASON_CLOSE_ENDPOINT_SESSION_REQUESTED);
+ } catch (RemoteException e) {
+ Log.e(TAG, "closeSession: failed to close session " + session, e);
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ void sendMessage(
+ HubEndpointSession session,
+ HubMessage message,
+ @Nullable IContextHubTransactionCallback transactionCallback) {
+ IContextHubEndpoint serviceToken = mServiceToken;
+ if (serviceToken == null) {
+ // Not registered
+ return;
+ }
+
+ try {
+ serviceToken.sendMessage(session.getId(), message, transactionCallback);
+ } catch (RemoteException e) {
+ Log.e(TAG, "sendMessage: failed to send message session=" + session, e);
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ public int getVersion() {
+ return mPendingHubEndpointInfo.getVersion();
+ }
+
+ @Nullable
+ public String getTag() {
+ return mPendingHubEndpointInfo.getTag();
+ }
+
+ @NonNull
+ public Collection<HubServiceInfo> getServiceInfoCollection() {
+ return mPendingHubEndpointInfo.getServiceInfoCollection();
+ }
+
+ @Nullable
+ public IHubEndpointLifecycleCallback getLifecycleCallback() {
+ return mLifecycleCallback;
+ }
+
+ @Nullable
+ public IHubEndpointMessageCallback getMessageCallback() {
+ return mMessageCallback;
+ }
+
+ /** Builder for a {@link HubEndpoint} object. */
+ public static final class Builder {
+ private final String mPackageName;
+
+ @Nullable private IHubEndpointLifecycleCallback mLifecycleCallback;
+
+ @NonNull private Executor mLifecycleCallbackExecutor;
+
+ @Nullable private IHubEndpointMessageCallback mMessageCallback;
+ @NonNull private Executor mMessageCallbackExecutor;
+
+ private int mVersion;
+ @Nullable private String mTag;
+
+ private List<HubServiceInfo> mServiceInfos = Collections.emptyList();
+
+ /** Create a builder for {@link HubEndpoint} */
+ public Builder(@NonNull Context context) {
+ mPackageName = context.getPackageName();
+ mVersion = (int) context.getApplicationInfo().longVersionCode;
+ mLifecycleCallbackExecutor = context.getMainExecutor();
+ mMessageCallbackExecutor = context.getMainExecutor();
+ }
+
+ /**
+ * Set the version for the endpoint. Default is 0.
+ *
+ * @hide
+ */
+ @NonNull
+ public Builder setVersion(int version) {
+ mVersion = version;
+ return this;
+ }
+
+ /**
+ * Set a tag string. The tag can be used to further identify the creator of the endpoint.
+ * Endpoints created by the same package share the same name but should have different tags.
+ */
+ @NonNull
+ public Builder setTag(@NonNull String tag) {
+ mTag = tag;
+ return this;
+ }
+
+ /** Attach a callback interface for lifecycle events for this Endpoint */
+ @NonNull
+ public Builder setLifecycleCallback(
+ @NonNull IHubEndpointLifecycleCallback lifecycleCallback) {
+ mLifecycleCallback = lifecycleCallback;
+ return this;
+ }
+
+ /**
+ * Attach a callback interface for lifecycle events for this Endpoint with a specified
+ * executor
+ */
+ @NonNull
+ public Builder setLifecycleCallback(
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull IHubEndpointLifecycleCallback lifecycleCallback) {
+ mLifecycleCallbackExecutor = executor;
+ mLifecycleCallback = lifecycleCallback;
+ return this;
+ }
+
+ /** Attach a callback interface for message events for this Endpoint */
+ @NonNull
+ public Builder setMessageCallback(@NonNull IHubEndpointMessageCallback messageCallback) {
+ mMessageCallback = messageCallback;
+ return this;
+ }
+
+ /**
+ * Attach a callback interface for message events for this Endpoint with a specified
+ * executor
+ */
+ @NonNull
+ public Builder setMessageCallback(
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull IHubEndpointMessageCallback messageCallback) {
+ mMessageCallbackExecutor = executor;
+ mMessageCallback = messageCallback;
+ return this;
+ }
+
+ /**
+ * Add a service to the available services from this endpoint. The {@link HubServiceInfo}
+ * object can be built with {@link HubServiceInfo.Builder}.
+ */
+ @NonNull
+ public Builder setServiceInfoCollection(
+ @NonNull Collection<HubServiceInfo> hubServiceInfos) {
+ // Make a copy first
+ mServiceInfos = new ArrayList<>(hubServiceInfos);
+ return this;
+ }
+
+ /** Build the {@link HubEndpoint} object. */
+ @NonNull
+ public HubEndpoint build() {
+ return new HubEndpoint(
+ new HubEndpointInfo(mPackageName, mVersion, mTag, mServiceInfos),
+ mLifecycleCallback,
+ mLifecycleCallbackExecutor,
+ mMessageCallback,
+ mMessageCallbackExecutor);
+ }
+ }
+}
diff --git a/core/java/android/text/ClientFlags.java b/core/java/android/hardware/contexthub/HubEndpointInfo.aidl
similarity index 60%
copy from core/java/android/text/ClientFlags.java
copy to core/java/android/hardware/contexthub/HubEndpointInfo.aidl
index ca88764..025b2b1 100644
--- a/core/java/android/text/ClientFlags.java
+++ b/core/java/android/hardware/contexthub/HubEndpointInfo.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,16 +14,7 @@
* limitations under the License.
*/
-package android.text;
+package android.hardware.contexthub;
-/**
- * An aconfig feature flags that can be accessible from application process without
- * ContentProvider IPCs.
- *
- * When you add new flags, you have to add flag string to {@link TextFlags#TEXT_ACONFIGS_FLAGS}.
- *
- * TODO(nona): Remove this class.
- * @hide
- */
-public class ClientFlags {
-}
+/** @hide */
+parcelable HubEndpointInfo;
diff --git a/core/java/android/hardware/contexthub/HubEndpointInfo.java b/core/java/android/hardware/contexthub/HubEndpointInfo.java
new file mode 100644
index 0000000..5265d56
--- /dev/null
+++ b/core/java/android/hardware/contexthub/HubEndpointInfo.java
@@ -0,0 +1,330 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.contexthub;
+
+import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
+import android.chre.flags.Flags;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Parcelable representing an endpoint from ContextHub or VendorHub.
+ *
+ * <p>HubEndpointInfo contains information about an endpoint, including its name, tag and other
+ * information. A HubEndpointInfo object can be used to accurately identify a specific endpoint.
+ * Application can use this object to identify and describe an endpoint.
+ *
+ * <p>See: {@link android.hardware.location.ContextHubManager#findEndpoints} for how to retrieve
+ * {@link HubEndpointInfo} for endpoints on a hub.
+ *
+ * @hide
+ */
+@SystemApi
+@FlaggedApi(Flags.FLAG_OFFLOAD_API)
+public final class HubEndpointInfo implements Parcelable {
+ /**
+ * A unique identifier for one endpoint. A unique identifier for one endpoint consists of two
+ * parts: (1) a unique long number for a hub and (2) a long number for the endpoint, unique
+ * within a hub. This class overrides equality methods and can be used to compare if two
+ * endpoints are the same.
+ */
+ public static class HubEndpointIdentifier {
+ private final long mEndpointId;
+ private final long mHubId;
+
+ /** @hide */
+ public HubEndpointIdentifier(long hubId, long endpointId) {
+ mEndpointId = endpointId;
+ mHubId = hubId;
+ }
+
+ /** @hide */
+ public HubEndpointIdentifier(android.hardware.contexthub.EndpointId halEndpointId) {
+ mEndpointId = halEndpointId.id;
+ mHubId = halEndpointId.hubId;
+ }
+
+ /** Get the endpoint portion of the identifier. */
+ public long getEndpoint() {
+ return mEndpointId;
+ }
+
+ /** Get the hub portion of the identifier. */
+ public long getHub() {
+ return mHubId;
+ }
+
+ /**
+ * Create an invalid endpoint id, to represent endpoint that are not yet registered with the
+ * HAL.
+ *
+ * @hide
+ */
+ public static HubEndpointIdentifier invalid() {
+ return new HubEndpointIdentifier(
+ android.hardware.contexthub.HubInfo.HUB_ID_INVALID,
+ android.hardware.contexthub.EndpointId.ENDPOINT_ID_INVALID);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mEndpointId, mHubId);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof HubEndpointIdentifier other)) {
+ return false;
+ }
+ if (other.mHubId != mHubId) {
+ return false;
+ }
+ return other.mEndpointId == mEndpointId;
+ }
+ }
+
+ /** This endpoint is from the Android framework */
+ public static final int TYPE_FRAMEWORK = 1;
+
+ /** This endpoint is from an Android app */
+ public static final int TYPE_APP = 2;
+
+ /** This endpoint is from an Android native program. */
+ public static final int TYPE_NATIVE = 3;
+
+ /** This endpoint is from a nanoapp. */
+ public static final int TYPE_NANOAPP = 4;
+
+ /** This endpoint is a generic endpoint served by a hub (not from a nanoapp). */
+ public static final int TYPE_HUB_ENDPOINT = 5;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({
+ TYPE_FRAMEWORK,
+ TYPE_APP,
+ TYPE_NATIVE,
+ TYPE_NANOAPP,
+ TYPE_HUB_ENDPOINT,
+ })
+ public @interface EndpointType {}
+
+ private final HubEndpointIdentifier mId;
+ @EndpointType private final int mType;
+ private final String mName;
+ private final int mVersion;
+ @Nullable private final String mTag;
+
+ @NonNull private final List<String> mRequiredPermissions;
+ @NonNull private final List<HubServiceInfo> mHubServiceInfos;
+
+ /** @hide */
+ public HubEndpointInfo(android.hardware.contexthub.EndpointInfo endpointInfo) {
+ mId = new HubEndpointIdentifier(endpointInfo.id.hubId, endpointInfo.id.id);
+ mType = endpointInfo.type;
+ mName = endpointInfo.name;
+ mVersion = endpointInfo.version;
+ mTag = endpointInfo.tag;
+ mRequiredPermissions = Arrays.asList(endpointInfo.requiredPermissions);
+ mHubServiceInfos = new ArrayList<>(endpointInfo.services.length);
+ for (int i = 0; i < endpointInfo.services.length; i++) {
+ mHubServiceInfos.add(new HubServiceInfo(endpointInfo.services[i]));
+ }
+ }
+
+ /** @hide */
+ public HubEndpointInfo(
+ String name,
+ int version,
+ @Nullable String tag,
+ @NonNull List<HubServiceInfo> hubServiceInfos) {
+ mId = HubEndpointIdentifier.invalid();
+ mType = TYPE_APP;
+ mName = name;
+ mVersion = version;
+ mTag = tag;
+ mRequiredPermissions = Collections.emptyList();
+ mHubServiceInfos = hubServiceInfos;
+ }
+
+ private HubEndpointInfo(Parcel in) {
+ long hubId = in.readLong();
+ long endpointId = in.readLong();
+ mId = new HubEndpointIdentifier(hubId, endpointId);
+ mType = in.readInt();
+ mName = in.readString();
+ mVersion = in.readInt();
+ mTag = in.readString();
+ mRequiredPermissions = new ArrayList<>();
+ in.readStringList(mRequiredPermissions);
+ mHubServiceInfos = new ArrayList<>();
+ in.readTypedList(mHubServiceInfos, HubServiceInfo.CREATOR);
+ }
+
+ /** Parcel implementation details */
+ @Override
+ public int describeContents() {
+ int flags = 0;
+ for (HubServiceInfo serviceInfo : mHubServiceInfos) {
+ flags |= serviceInfo.describeContents();
+ }
+ return flags;
+ }
+
+ /** Parcel implementation details */
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeLong(mId.getHub());
+ dest.writeLong(mId.getEndpoint());
+ dest.writeInt(mType);
+ dest.writeString(mName);
+ dest.writeInt(mVersion);
+ dest.writeString(mTag);
+ dest.writeStringList(mRequiredPermissions);
+ dest.writeTypedList(mHubServiceInfos, flags);
+ }
+
+ /** Get a unique identifier for this endpoint. */
+ @NonNull
+ public HubEndpointIdentifier getIdentifier() {
+ return mId;
+ }
+
+ /**
+ * Get the type of this endpoint. Application may use this field to get more information about
+ * who registered this endpoint for diagnostic purposes.
+ *
+ * <p>Type can be one of {@link HubEndpointInfo#TYPE_APP}, {@link
+ * HubEndpointInfo#TYPE_FRAMEWORK}, {@link HubEndpointInfo#TYPE_NANOAPP}, {@link
+ * HubEndpointInfo#TYPE_NATIVE} or {@link HubEndpointInfo#TYPE_HUB_ENDPOINT}.
+ */
+ public int getType() {
+ return mType;
+ }
+
+ /** Get the human-readable name of this endpoint (for debugging purposes). */
+ @NonNull
+ public String getName() {
+ return mName;
+ }
+
+ /**
+ * Get the version of this endpoint.
+ *
+ * <p>Monotonically increasing version number. The two sides of an endpoint session can use this
+ * version number to identify the other side and determine compatibility with each other. The
+ * interpretation of the version number is specific to the implementation of an endpoint.
+ *
+ * <p>The version number should not be used to compare endpoints implementation freshness for
+ * different endpoint types.
+ *
+ * <p>Depending on type of the endpoint, the following values (and formats) are used:
+ *
+ * <ol>
+ * <li>{@link #TYPE_FRAMEWORK}: android.os.Build.VERSION.SDK_INT_FULL
+ * <li>{@link #TYPE_APP}: versionCode
+ * <li>{@link #TYPE_NATIVE}: unspecified format (supplied by endpoint code)
+ * <li>{@link #TYPE_NANOAPP}: nanoapp version, typically following 0xMMmmpppp scheme where MM
+ * = major version, mm = minor version, pppp = patch version
+ * <li>{@link #TYPE_HUB_ENDPOINT}: unspecified format (supplied by endpoint code), following
+ * nanoapp versioning scheme is recommended
+ * </ol>
+ */
+ public int getVersion() {
+ return mVersion;
+ }
+
+ /**
+ * Get the tag that further identifies the submodule that created this endpoint. For example, a
+ * single application could provide multiple endpoints. These endpoints will share the same
+ * name, but will have different tags. This tag can be used to identify the submodule within the
+ * application that provided the endpoint.
+ */
+ @Nullable
+ public String getTag() {
+ return mTag;
+ }
+
+ /**
+ * Get the list of required permissions in order to talk to this endpoint.
+ *
+ * <p>This list is enforced by the Context Hub Service. The app would need to have the required
+ * permissions list to open a session with this particular endpoint. Otherwise this will be
+ * rejected by as permission failures.
+ *
+ * <p>This is mostly for allowing app to check what permission it needs first internally. App
+ * will need to request permissions grant at runtime if not already granted. See {@link
+ * android.content.Context#checkPermission} for more details.
+ *
+ * <p>See {@link android.Manifest.permission} for a list of standard Android permissions as
+ * possible values.
+ */
+ @SuppressLint("RequiresPermission")
+ @NonNull
+ public Collection<String> getRequiredPermissions() {
+ return Collections.unmodifiableList(mRequiredPermissions);
+ }
+
+ /**
+ * Get the list of services provided by this endpoint.
+ *
+ * <p>See {@link HubServiceInfo} for more information.
+ */
+ @NonNull
+ public Collection<HubServiceInfo> getServiceInfoCollection() {
+ return Collections.unmodifiableList(mHubServiceInfos);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder out = new StringBuilder();
+ out.append("Endpoint [0x");
+ out.append(Long.toHexString(mId.getEndpoint()));
+ out.append("@ Hub 0x");
+ out.append(Long.toHexString(mId.getHub()));
+ out.append("] Name=");
+ out.append(mName);
+ out.append(", Tag=");
+ out.append(mTag);
+ return out.toString();
+ }
+
+ public static final @android.annotation.NonNull Creator<HubEndpointInfo> CREATOR =
+ new Creator<>() {
+ public HubEndpointInfo createFromParcel(Parcel in) {
+ return new HubEndpointInfo(in);
+ }
+
+ public HubEndpointInfo[] newArray(int size) {
+ return new HubEndpointInfo[size];
+ }
+ };
+}
diff --git a/core/java/android/hardware/contexthub/HubEndpointSession.java b/core/java/android/hardware/contexthub/HubEndpointSession.java
new file mode 100644
index 0000000..cf952cb
--- /dev/null
+++ b/core/java/android/hardware/contexthub/HubEndpointSession.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.contexthub;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.chre.flags.Flags;
+import android.hardware.location.ContextHubTransaction;
+import android.hardware.location.ContextHubTransactionHelper;
+import android.hardware.location.IContextHubTransactionCallback;
+import android.util.CloseGuard;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * An object representing a communication session between two different hub endpoints.
+ *
+ * @hide
+ */
+@SystemApi
+@FlaggedApi(Flags.FLAG_OFFLOAD_API)
+public class HubEndpointSession implements AutoCloseable {
+ private final CloseGuard mCloseGuard = new CloseGuard();
+
+ private final int mId;
+
+ @NonNull private final HubEndpoint mHubEndpoint;
+ @NonNull private final HubEndpointInfo mInitiator;
+ @NonNull private final HubEndpointInfo mDestination;
+ @Nullable private final HubServiceInfo mServiceInfo;
+
+ private final AtomicBoolean mIsClosed = new AtomicBoolean(true);
+
+ /** @hide */
+ HubEndpointSession(
+ int id,
+ @NonNull HubEndpoint hubEndpoint,
+ @NonNull HubEndpointInfo destination,
+ @NonNull HubEndpointInfo initiator,
+ @Nullable HubServiceInfo serviceInfo) {
+ mId = id;
+ mHubEndpoint = hubEndpoint;
+ mDestination = destination;
+ mInitiator = initiator;
+ mServiceInfo = serviceInfo;
+ }
+
+ /**
+ * Send a message to the peer endpoint in this session.
+ *
+ * @param message The message object constructed with {@link HubMessage#createMessage}.
+ * @return For messages that does not require a response, the transaction will immediately
+ * complete. For messages that requires a response, the transaction will complete after
+ * receiving the response for the message.
+ */
+ @NonNull
+ public ContextHubTransaction<Void> sendMessage(@NonNull HubMessage message) {
+ if (mIsClosed.get()) {
+ throw new IllegalStateException("Session is already closed.");
+ }
+
+ boolean isResponseRequired = message.getDeliveryParams().isResponseRequired();
+ ContextHubTransaction<Void> ret =
+ new ContextHubTransaction<>(
+ isResponseRequired
+ ? ContextHubTransaction.TYPE_HUB_MESSAGE_REQUIRES_RESPONSE
+ : ContextHubTransaction.TYPE_HUB_MESSAGE_DEFAULT);
+ if (!isResponseRequired) {
+ // If the message doesn't require acknowledgement, respond with success immediately
+ // TODO(b/379162322): Improve handling of synchronous failures.
+ mHubEndpoint.sendMessage(this, message, null);
+ ret.setResponse(
+ new ContextHubTransaction.Response<>(
+ ContextHubTransaction.RESULT_SUCCESS, null));
+ } else {
+ IContextHubTransactionCallback callback =
+ ContextHubTransactionHelper.createTransactionCallback(ret);
+ // Sequence number will be assigned at the service
+ mHubEndpoint.sendMessage(this, message, callback);
+ }
+ return ret;
+ }
+
+ /** @hide */
+ public int getId() {
+ return mId;
+ }
+
+ /** @hide */
+ public void setOpened() {
+ mIsClosed.set(false);
+ mCloseGuard.open("close");
+ }
+
+ /** @hide */
+ public void setClosed() {
+ mIsClosed.set(true);
+ mCloseGuard.close();
+ }
+
+ /**
+ * Closes the connection for this session between an endpoint and the Context Hub Service.
+ *
+ * <p>When this function is invoked, the messaging associated with this session is invalidated.
+ * All futures messages targeted for this client are dropped.
+ */
+ public void close() {
+ if (!mIsClosed.getAndSet(true)) {
+ mCloseGuard.close();
+ mHubEndpoint.closeSession(this);
+ }
+ }
+
+ /**
+ * Get the {@link HubServiceInfo} associated with this session. Null value indicates that there
+ * is no service associated to this session.
+ *
+ * <p>For hub initiated sessions, the object was previously used in as an argument for open
+ * request in {@link IHubEndpointLifecycleCallback#onSessionOpenRequest}.
+ *
+ * <p>For app initiated sessions, the object was previously used in an open request in {@link
+ * android.hardware.location.ContextHubManager#openSession}
+ */
+ @Nullable
+ public HubServiceInfo getServiceInfo() {
+ return mServiceInfo;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder stringBuilder = new StringBuilder();
+ stringBuilder.append("Session [");
+ stringBuilder.append(mId);
+ stringBuilder.append("]: [");
+ stringBuilder.append(mInitiator);
+ stringBuilder.append("]->[");
+ stringBuilder.append(mDestination);
+ stringBuilder.append("]");
+ return stringBuilder.toString();
+ }
+
+ /** @hide */
+ protected void finalize() throws Throwable {
+ try {
+ // Note that guard could be null if the constructor threw.
+ if (mCloseGuard != null) {
+ mCloseGuard.warnIfOpen();
+ }
+ close();
+ } finally {
+ super.finalize();
+ }
+ }
+}
diff --git a/core/java/android/hardware/contexthub/HubEndpointSessionResult.java b/core/java/android/hardware/contexthub/HubEndpointSessionResult.java
new file mode 100644
index 0000000..1f2bdb9
--- /dev/null
+++ b/core/java/android/hardware/contexthub/HubEndpointSessionResult.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.contexthub;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.chre.flags.Flags;
+
+/**
+ * Return type of {@link IHubEndpointLifecycleCallback#onSessionOpenRequest}. The value determines
+ * whether a open session request from the remote is accepted or not.
+ *
+ * @hide
+ */
+@SystemApi
+@FlaggedApi(Flags.FLAG_OFFLOAD_API)
+public class HubEndpointSessionResult {
+ private final boolean mAccepted;
+
+ @Nullable private final String mReason;
+
+ private HubEndpointSessionResult(boolean accepted, @Nullable String reason) {
+ mAccepted = accepted;
+ mReason = reason;
+ }
+
+ /**
+ * Retrieve the decision of the session request.
+ *
+ * @return Whether a session request was accepted or not, previously set with {@link #accept()}
+ * or {@link #reject(String)}.
+ */
+ public boolean isAccepted() {
+ return mAccepted;
+ }
+
+ /**
+ * Retrieve the decision of the session request.
+ *
+ * @return The reason previously set in {@link #reject(String)}. If the result was {@link
+ * #accept()}, the reason will be null.
+ */
+ @Nullable
+ public String getReason() {
+ return mReason;
+ }
+
+ /** Accept the request. */
+ @NonNull
+ public static HubEndpointSessionResult accept() {
+ return new HubEndpointSessionResult(true, null);
+ }
+
+ /**
+ * Reject the request with a reason.
+ *
+ * @param reason Reason why the request was rejected, for diagnostic purposes.
+ */
+ @NonNull
+ public static HubEndpointSessionResult reject(@NonNull String reason) {
+ return new HubEndpointSessionResult(false, reason);
+ }
+}
diff --git a/core/java/android/text/ClientFlags.java b/core/java/android/hardware/contexthub/HubMessage.aidl
similarity index 61%
copy from core/java/android/text/ClientFlags.java
copy to core/java/android/hardware/contexthub/HubMessage.aidl
index ca88764..86afce2 100644
--- a/core/java/android/text/ClientFlags.java
+++ b/core/java/android/hardware/contexthub/HubMessage.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,16 +14,9 @@
* limitations under the License.
*/
-package android.text;
+package android.hardware.contexthub;
/**
- * An aconfig feature flags that can be accessible from application process without
- * ContentProvider IPCs.
- *
- * When you add new flags, you have to add flag string to {@link TextFlags#TEXT_ACONFIGS_FLAGS}.
- *
- * TODO(nona): Remove this class.
* @hide
*/
-public class ClientFlags {
-}
+parcelable HubMessage;
diff --git a/core/java/android/hardware/contexthub/HubMessage.java b/core/java/android/hardware/contexthub/HubMessage.java
new file mode 100644
index 0000000..dc8a8c5
--- /dev/null
+++ b/core/java/android/hardware/contexthub/HubMessage.java
@@ -0,0 +1,289 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.contexthub;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.chre.flags.Flags;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import libcore.util.HexEncoding;
+
+import java.util.Arrays;
+import java.util.Objects;
+
+/**
+ * A class describing general messages send through the Context Hub Service.
+ *
+ * @hide
+ */
+@SystemApi
+@FlaggedApi(Flags.FLAG_OFFLOAD_API)
+public final class HubMessage implements Parcelable {
+ private static final int DEBUG_LOG_NUM_BYTES = 16;
+
+ private final int mMessageType;
+ private final byte[] mMessageBody;
+
+ private final DeliveryParams mDeliveryParams;
+ private int mMessageSequenceNumber;
+
+ /**
+ * Configurable options for message delivery. This option can be passed into {@link
+ * HubEndpointSession#sendMessage} to specify the behavior of message delivery.
+ */
+ public static class DeliveryParams {
+ private boolean mResponseRequired;
+
+ private DeliveryParams(boolean responseRequired) {
+ mResponseRequired = responseRequired;
+ }
+
+ /** Get the acknowledgement requirement. */
+ public boolean isResponseRequired() {
+ return mResponseRequired;
+ }
+
+ /**
+ * Set the response requirement for a message. Message sent with this option will have a
+ * {@link android.hardware.location.ContextHubTransaction.Response} when the peer received
+ * the message. Default is false.
+ */
+ @NonNull
+ public DeliveryParams setResponseRequired(boolean required) {
+ mResponseRequired = required;
+ return this;
+ }
+
+ /** Construct a default delivery option. */
+ @NonNull
+ public static DeliveryParams makeBasic() {
+ return new DeliveryParams(false);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder out = new StringBuilder();
+ out.append("DeliveryParams[");
+ out.append("responseRequired = ").append(mResponseRequired);
+ out.append("]");
+ return out.toString();
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mResponseRequired);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == this) {
+ return true;
+ }
+
+ if (obj instanceof DeliveryParams other) {
+ return other.mResponseRequired == mResponseRequired;
+ }
+
+ return false;
+ }
+ }
+
+ private HubMessage(int messageType, byte[] messageBody, DeliveryParams deliveryParams) {
+ mMessageType = messageType;
+ mMessageBody = messageBody;
+ mDeliveryParams = deliveryParams;
+ }
+
+ /**
+ * Creates a HubMessage object to send to through an endpoint.
+ *
+ * @param messageType the endpoint & service dependent message type
+ * @param messageBody the byte array message contents
+ * @return the HubMessage object
+ */
+ @NonNull
+ public static HubMessage createMessage(int messageType, @NonNull byte[] messageBody) {
+ return new HubMessage(messageType, messageBody, DeliveryParams.makeBasic());
+ }
+
+ /**
+ * Creates a HubMessage object to send to through an endpoint.
+ *
+ * @param messageType the endpoint & service dependent message type
+ * @param messageBody the byte array message contents
+ * @param deliveryParams The message delivery parameters. See {@link HubMessage.DeliveryParams}
+ * for more details.
+ * @return the HubMessage object
+ */
+ @NonNull
+ public static HubMessage createMessage(
+ int messageType, @NonNull byte[] messageBody, @NonNull DeliveryParams deliveryParams) {
+ return new HubMessage(messageType, messageBody, deliveryParams);
+ }
+
+ /**
+ * Retrieve the message type.
+ *
+ * @return the type of the message
+ */
+ public int getMessageType() {
+ return mMessageType;
+ }
+
+ /**
+ * Retrieve the body of the message. The body can be an empty byte array.
+ *
+ * @return the byte array contents of the message
+ */
+ @NonNull
+ public byte[] getMessageBody() {
+ return mMessageBody;
+ }
+
+ /**
+ * Retrieve the {@link DeliveryParams} object specifying the behavior of message delivery.
+ *
+ * @hide
+ */
+ public DeliveryParams getDeliveryParams() {
+ return mDeliveryParams;
+ }
+
+ /**
+ * Assign a message sequence number. This should only be called by the system service.
+ *
+ * @hide
+ */
+ public void setMessageSequenceNumber(int messageSequenceNumber) {
+ mMessageSequenceNumber = messageSequenceNumber;
+ }
+
+ /**
+ * Returns the message sequence number. The default value is 0.
+ *
+ * @return the message sequence number of the message
+ * @hide
+ */
+ public int getMessageSequenceNumber() {
+ return mMessageSequenceNumber;
+ }
+
+ private HubMessage(@NonNull Parcel in) {
+ mMessageType = in.readInt();
+
+ int msgSize = in.readInt();
+ mMessageBody = new byte[msgSize];
+ in.readByteArray(mMessageBody);
+
+ mDeliveryParams = DeliveryParams.makeBasic();
+ mDeliveryParams.setResponseRequired(in.readInt() == 1);
+ mMessageSequenceNumber = in.readInt();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel out, int flags) {
+ out.writeInt(mMessageType);
+
+ out.writeInt(mMessageBody.length);
+ out.writeByteArray(mMessageBody);
+
+ out.writeInt(mDeliveryParams.isResponseRequired() ? 1 : 0);
+ out.writeInt(mMessageSequenceNumber);
+ }
+
+ public static final @NonNull Creator<HubMessage> CREATOR =
+ new Creator<>() {
+ @Override
+ public HubMessage createFromParcel(Parcel in) {
+ return new HubMessage(in);
+ }
+
+ @Override
+ public HubMessage[] newArray(int size) {
+ return new HubMessage[size];
+ }
+ };
+
+ @NonNull
+ @Override
+ public String toString() {
+ int length = mMessageBody.length;
+
+ StringBuilder out = new StringBuilder();
+ out.append("HubMessage[type = ").append(mMessageType);
+ out.append(", length = ").append(mMessageBody.length);
+ out.append(", messageSequenceNumber = ").append(mMessageSequenceNumber);
+ out.append(", deliveryParams = ").append(mDeliveryParams);
+ out.append("](");
+
+ if (length > 0) {
+ out.append("data = 0x");
+ }
+ for (int i = 0; i < Math.min(length, DEBUG_LOG_NUM_BYTES); i++) {
+ out.append(HexEncoding.encodeToString(mMessageBody[i], true /* upperCase */));
+
+ if ((i + 1) % 4 == 0) {
+ out.append(" ");
+ }
+ }
+ if (length > DEBUG_LOG_NUM_BYTES) {
+ out.append("...");
+ }
+ out.append(")");
+
+ return out.toString();
+ }
+
+ @Override
+ public boolean equals(@Nullable Object object) {
+ if (object == this) {
+ return true;
+ }
+
+ boolean isEqual = false;
+ if (object instanceof HubMessage other) {
+ isEqual =
+ (other.getMessageType() == mMessageType)
+ && Arrays.equals(other.getMessageBody(), mMessageBody)
+ && (other.getDeliveryParams().equals(mDeliveryParams))
+ && (other.getMessageSequenceNumber() == mMessageSequenceNumber);
+ }
+
+ return isEqual;
+ }
+
+ @Override
+ public int hashCode() {
+ if (!Flags.fixApiCheck()) {
+ return super.hashCode();
+ }
+
+ return Objects.hash(
+ mMessageType,
+ Arrays.hashCode(mMessageBody),
+ mDeliveryParams,
+ mMessageSequenceNumber);
+ }
+}
diff --git a/core/java/android/text/ClientFlags.java b/core/java/android/hardware/contexthub/HubServiceInfo.aidl
similarity index 61%
copy from core/java/android/text/ClientFlags.java
copy to core/java/android/hardware/contexthub/HubServiceInfo.aidl
index ca88764..98b1bba 100644
--- a/core/java/android/text/ClientFlags.java
+++ b/core/java/android/hardware/contexthub/HubServiceInfo.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,16 +14,9 @@
* limitations under the License.
*/
-package android.text;
+package android.hardware.contexthub;
/**
- * An aconfig feature flags that can be accessible from application process without
- * ContentProvider IPCs.
- *
- * When you add new flags, you have to add flag string to {@link TextFlags#TEXT_ACONFIGS_FLAGS}.
- *
- * TODO(nona): Remove this class.
* @hide
*/
-public class ClientFlags {
-}
+parcelable HubServiceInfo;
diff --git a/core/java/android/hardware/contexthub/HubServiceInfo.java b/core/java/android/hardware/contexthub/HubServiceInfo.java
new file mode 100644
index 0000000..c7fe77c
--- /dev/null
+++ b/core/java/android/hardware/contexthub/HubServiceInfo.java
@@ -0,0 +1,263 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.contexthub;
+
+import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.chre.flags.Flags;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.ParcelableHolder;
+
+import androidx.annotation.NonNull;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Collection;
+import java.util.Objects;
+
+/**
+ * A class describing services provided by endpoints.
+ *
+ * <p>An endpoint can provide zero or more service. See {@link
+ * HubEndpoint.Builder#setServiceInfoCollection(Collection)} and {@link
+ * HubEndpointInfo#getServiceInfoCollection()}.
+ *
+ * <p>An endpoint session can be service-less or associated to one service.See {@link
+ * HubEndpointSession#getServiceInfo()}.
+ *
+ * @hide
+ */
+@SystemApi
+@FlaggedApi(Flags.FLAG_OFFLOAD_API)
+public final class HubServiceInfo implements Parcelable {
+ /** Customized format for messaging. Fully customized and opaque messaging format. */
+ public static final int FORMAT_CUSTOM = 0;
+
+ /**
+ * Binder-based messaging. The host endpoint is defining this service in Stable AIDL. Messages
+ * between endpoints that uses this service will be using the binder marhsalling format.
+ */
+ public static final int FORMAT_AIDL = 1;
+
+ /**
+ * Pigweed RPC messaging with Protobuf. This endpoint is a Pigweed RPC. Messages between
+ * endpoints will use Pigweed RPC marshalling format (protobuf).
+ */
+ public static final int FORMAT_PW_RPC_PROTOBUF = 2;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({
+ FORMAT_CUSTOM,
+ FORMAT_AIDL,
+ FORMAT_PW_RPC_PROTOBUF,
+ })
+ public @interface ServiceFormat {}
+
+ @NonNull private final String mServiceDescriptor;
+
+ @ServiceFormat private final int mFormat;
+ private final int mMajorVersion;
+ private final int mMinorVersion;
+
+ @NonNull private final ParcelableHolder mExtendedInfo;
+
+ /** @hide */
+ public HubServiceInfo(android.hardware.contexthub.Service service) {
+ mServiceDescriptor = service.serviceDescriptor;
+ mFormat = service.format;
+ mMajorVersion = service.majorVersion;
+ mMinorVersion = service.minorVersion;
+ mExtendedInfo = service.extendedInfo;
+ }
+
+ private HubServiceInfo(Parcel in) {
+ mServiceDescriptor = Objects.requireNonNull(in.readString());
+ mFormat = in.readInt();
+ mMajorVersion = in.readInt();
+ mMinorVersion = in.readInt();
+ mExtendedInfo = ParcelableHolder.CREATOR.createFromParcel(in);
+ }
+
+ public HubServiceInfo(
+ @NonNull String serviceDescriptor,
+ @ServiceFormat int format,
+ int majorVersion,
+ int minorVersion,
+ @NonNull ParcelableHolder extendedInfo) {
+ mServiceDescriptor = serviceDescriptor;
+ mFormat = format;
+ mMajorVersion = majorVersion;
+ mMinorVersion = minorVersion;
+ mExtendedInfo = extendedInfo;
+ }
+
+ /** Get the unique identifier of this service. See {@link Builder} for more information. */
+ @NonNull
+ public String getServiceDescriptor() {
+ return mServiceDescriptor;
+ }
+
+ /**
+ * Get the type of the service.
+ *
+ * <p>The value can be one of {@link HubServiceInfo#FORMAT_CUSTOM}, {@link
+ * HubServiceInfo#FORMAT_AIDL} or {@link HubServiceInfo#FORMAT_PW_RPC_PROTOBUF}.
+ */
+ public int getFormat() {
+ return mFormat;
+ }
+
+ /** Get the major version of this service. */
+ public int getMajorVersion() {
+ return mMajorVersion;
+ }
+
+ /** Get the minor version of this service. */
+ public int getMinorVersion() {
+ return mMinorVersion;
+ }
+
+ /** Get the {@link ParcelableHolder} for the extended information about the service. */
+ @NonNull
+ public ParcelableHolder getExtendedInfo() {
+ return mExtendedInfo;
+ }
+
+ /** Parcel implementation details */
+ @Override
+ public int describeContents() {
+ // Passthrough describeContents flags for mExtendedInfo because we don't have FD otherwise.
+ return mExtendedInfo.describeContents();
+ }
+
+ /** Parcel implementation details */
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeString(mServiceDescriptor);
+ dest.writeInt(mFormat);
+ dest.writeInt(mMajorVersion);
+ dest.writeInt(mMinorVersion);
+ mExtendedInfo.writeToParcel(dest, flags);
+ }
+
+ /** Builder for a {@link HubServiceInfo} object. */
+ public static final class Builder {
+ @NonNull private final String mServiceDescriptor;
+
+ @ServiceFormat private final int mFormat;
+ private final int mMajorVersion;
+ private final int mMinorVersion;
+
+ private final ParcelableHolder mExtendedInfo =
+ new ParcelableHolder(Parcelable.PARCELABLE_STABILITY_VINTF);
+
+ /**
+ * Create a builder for {@link HubServiceInfo} with a service descriptor.
+ *
+ * <p>Service descriptor should uniquely identify the interface (scoped to type). Convention
+ * of the descriptor depend on interface type.
+ *
+ * <p>Examples:
+ *
+ * <ol>
+ * <li>AOSP-defined AIDL: android.hardware.something.IFoo/default
+ * <li>Vendor-defined AIDL: com.example.something.IBar/default
+ * <li>Pigweed RPC with Protobuf: com.example.proto.ExampleService
+ * </ol>
+ *
+ * @param serviceDescriptor The service descriptor.
+ * @param format One of {@link HubServiceInfo#FORMAT_CUSTOM}, {@link
+ * HubServiceInfo#FORMAT_AIDL} or {@link HubServiceInfo#FORMAT_PW_RPC_PROTOBUF}.
+ * @param majorVersion Breaking changes should be a major version bump.
+ * @param minorVersion Monotonically increasing minor version.
+ * @throws IllegalArgumentException if one or more fields are not valid.
+ */
+ public Builder(
+ @NonNull String serviceDescriptor,
+ @ServiceFormat int format,
+ int majorVersion,
+ int minorVersion) {
+ if (format != FORMAT_CUSTOM
+ && format != FORMAT_AIDL
+ && format != FORMAT_PW_RPC_PROTOBUF) {
+ throw new IllegalArgumentException("Invalid format type.");
+ }
+ mFormat = format;
+
+ if (majorVersion < 0) {
+ throw new IllegalArgumentException(
+ "Major version cannot be set to negative number.");
+ }
+ mMajorVersion = majorVersion;
+
+ if (minorVersion < 0) {
+ throw new IllegalArgumentException(
+ "Minor version cannot be set to negative number.");
+ }
+ mMinorVersion = minorVersion;
+
+ if (serviceDescriptor.isBlank()) {
+ throw new IllegalArgumentException("Invalid service descriptor.");
+ }
+ mServiceDescriptor = serviceDescriptor;
+ }
+
+ /**
+ * Set the extended information of this service.
+ *
+ * @param extendedInfo Parcelable with extended information about this service. The
+ * parcelable needs to have at least VINTF stability. Null can be used to clear a
+ * previously set value.
+ * @throws android.os.BadParcelableException if the parcelable cannot be used.
+ */
+ @NonNull
+ public Builder setExtendedInfo(@Nullable Parcelable extendedInfo) {
+ mExtendedInfo.setParcelable(extendedInfo);
+ return this;
+ }
+
+ /**
+ * Build the {@link HubServiceInfo} object.
+ *
+ * @throws IllegalStateException if the Builder is missing required info.
+ */
+ @NonNull
+ public HubServiceInfo build() {
+ if (mMajorVersion < 0 || mMinorVersion < 0) {
+ throw new IllegalStateException("Major and minor version must be set.");
+ }
+ return new HubServiceInfo(
+ mServiceDescriptor, mFormat, mMajorVersion, mMinorVersion, mExtendedInfo);
+ }
+ }
+
+ /** Parcel implementation details */
+ @NonNull
+ public static final Parcelable.Creator<HubServiceInfo> CREATOR =
+ new Parcelable.Creator<>() {
+ public HubServiceInfo createFromParcel(Parcel in) {
+ return new HubServiceInfo(in);
+ }
+
+ public HubServiceInfo[] newArray(int size) {
+ return new HubServiceInfo[size];
+ }
+ };
+}
diff --git a/core/java/android/hardware/contexthub/IContextHubEndpoint.aidl b/core/java/android/hardware/contexthub/IContextHubEndpoint.aidl
new file mode 100644
index 0000000..1c98b4b
--- /dev/null
+++ b/core/java/android/hardware/contexthub/IContextHubEndpoint.aidl
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.contexthub;
+
+import android.hardware.contexthub.HubEndpointInfo;
+import android.hardware.contexthub.HubMessage;
+import android.hardware.contexthub.HubServiceInfo;
+import android.hardware.location.IContextHubTransactionCallback;
+
+/**
+ * @hide
+ */
+interface IContextHubEndpoint {
+ /**
+ * Retrieve the up-to-date EndpointInfo, with assigned endpoint id.
+ */
+ HubEndpointInfo getAssignedHubEndpointInfo();
+
+ /**
+ * Request system service to open a session with a specific destination.
+ *
+ * @param destination A valid HubEndpointInfo representing the destination.
+ *
+ * @throws IllegalArgumentException If the HubEndpointInfo is not valid.
+ * @throws IllegalStateException If there are too many opened sessions.
+ */
+ int openSession(in HubEndpointInfo destination, in @nullable HubServiceInfo serviceInfo);
+
+ /**
+ * Request system service to close a specific session
+ *
+ * @param sessionId An integer identifying the session, assigned by system service
+ * @param reason An integer identifying the reason
+ *
+ * @throws IllegalStateException If the session wasn't opened.
+ */
+ void closeSession(int sessionId, int reason);
+
+ /**
+ * Callback when a session is opened. This callback is the status callback for a previous
+ * IContextHubEndpointCallback.onSessionOpenRequest().
+ *
+ * @param sessionId The integer representing the communication session, previously set in
+ * onSessionOpenRequest().
+ *
+ * @throws IllegalStateException If the session wasn't opened.
+ */
+ void openSessionRequestComplete(int sessionId);
+
+ /**
+ * Unregister this endpoint from the HAL, invalidate the EndpointInfo previously assigned.
+ */
+ void unregister();
+
+ /**
+ * Send a message parcelable to system service for a specific session.
+ *
+ * @param sessionId The integer representing the communication session, previously set in
+ * IContextHubEndpoint.openSession(). This id is assigned by the HAL.
+ * @param message The HubMessage parcelable that represents the message and its delivery options.
+ * @param transactionCallback Nullable. If the hub message requires a reply, the transactionCallback
+ * will be set to non-null.
+ */
+ void sendMessage(int sessionId, in HubMessage message,
+ in @nullable IContextHubTransactionCallback transactionCallback);
+
+ /**
+ * Send a message delivery status to system service for a specific message
+ *
+ * @param sessionId The integer representing the communication session, previously set in
+ * IContextHubEndpoint.openSession(). This id is assigned by the HAL.
+ * @param messageSeqNumber The message sequence number, this should match a previously received HubMessage.
+ * @param errorCode The message delivery status detail.
+ */
+ void sendMessageDeliveryStatus(int sessionId, int messageSeqNumber, byte errorCode);
+}
diff --git a/core/java/android/hardware/contexthub/IContextHubEndpointCallback.aidl b/core/java/android/hardware/contexthub/IContextHubEndpointCallback.aidl
new file mode 100644
index 0000000..1ae5fb9
--- /dev/null
+++ b/core/java/android/hardware/contexthub/IContextHubEndpointCallback.aidl
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.contexthub;
+
+import android.hardware.contexthub.HubEndpointInfo;
+import android.hardware.contexthub.HubMessage;
+import android.hardware.contexthub.HubServiceInfo;
+
+/**
+ * @hide
+ */
+oneway interface IContextHubEndpointCallback {
+ /**
+ * Request from system service to open a session, requested by a specific initiator.
+ *
+ * @param sessionId An integer identifying the session, assigned by the initiator
+ * @param initiator HubEndpointInfo representing the requester
+ * @param serviceInfo Nullable HubServiceInfo representing the service associated with this session
+ */
+ void onSessionOpenRequest(int sessionId, in HubEndpointInfo initiator, in @nullable HubServiceInfo serviceInfo);
+
+ /**
+ * Request from system service to close a specific session
+ *
+ * @param sessionId An integer identifying the session
+ * @param reason An integer identifying the reason
+ */
+ void onSessionClosed(int sessionId, int reason);
+
+ /**
+ * Notifies the system service that the session requested by IContextHubEndpoint.openSession
+ * is ready to use.
+ *
+ * @param sessionId The integer representing the communication session, previously set in
+ * IContextHubEndpoint.openSession(). This id is assigned by the HAL.
+ */
+ void onSessionOpenComplete(int sessionId);
+
+ /**
+ * Message notification from system service for a specific session
+
+ * @param sessionId The integer representing the communication session, previously set in
+ * IContextHubEndpoint.openSession(). This id is assigned by the HAL.
+ * @param message The HubMessage parcelable that represents the message.
+ */
+ void onMessageReceived(int sessionId, in HubMessage message);
+}
diff --git a/core/java/android/hardware/contexthub/IHubEndpointLifecycleCallback.java b/core/java/android/hardware/contexthub/IHubEndpointLifecycleCallback.java
new file mode 100644
index 0000000..4688439
--- /dev/null
+++ b/core/java/android/hardware/contexthub/IHubEndpointLifecycleCallback.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.contexthub;
+
+import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.chre.flags.Flags;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Interface for listening to lifecycle events of a hub endpoint.
+ *
+ * @hide
+ */
+@SystemApi
+@FlaggedApi(Flags.FLAG_OFFLOAD_API)
+public interface IHubEndpointLifecycleCallback {
+ /** Unknown reason. */
+ int REASON_UNSPECIFIED = 0;
+
+ /** The peer rejected the request to open this endpoint session. */
+ int REASON_OPEN_ENDPOINT_SESSION_REQUEST_REJECTED = 3;
+
+ /** The peer closed this endpoint session. */
+ int REASON_CLOSE_ENDPOINT_SESSION_REQUESTED = 4;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({
+ REASON_UNSPECIFIED,
+ REASON_OPEN_ENDPOINT_SESSION_REQUEST_REJECTED,
+ REASON_CLOSE_ENDPOINT_SESSION_REQUESTED,
+ })
+ @interface EndpointLifecycleReason {}
+
+ /**
+ * Called when an endpoint is requesting a session be opened with another endpoint.
+ *
+ * @param requester The {@link HubEndpointInfo} object representing the requester
+ * @param serviceInfo The {@link HubServiceInfo} object representing the service associated with
+ * this session. Null indicates that there is no service associated with this session.
+ */
+ @NonNull
+ HubEndpointSessionResult onSessionOpenRequest(
+ @NonNull HubEndpointInfo requester, @Nullable HubServiceInfo serviceInfo);
+
+ /**
+ * Called when a communication session is opened and ready to be used.
+ *
+ * @param session The {@link HubEndpointSession} object that can be used for communication
+ */
+ void onSessionOpened(@NonNull HubEndpointSession session);
+
+ /**
+ * Called when a communication session is requested to be closed, or the peer endpoint rejected
+ * the session open request.
+ *
+ * @param session The {@link HubEndpointSession} object that is now closed and shouldn't be
+ * used.
+ * @param reason The reason why this session was closed.
+ */
+ void onSessionClosed(@NonNull HubEndpointSession session, @EndpointLifecycleReason int reason);
+}
diff --git a/core/java/android/hardware/contexthub/IHubEndpointMessageCallback.java b/core/java/android/hardware/contexthub/IHubEndpointMessageCallback.java
new file mode 100644
index 0000000..fde7017
--- /dev/null
+++ b/core/java/android/hardware/contexthub/IHubEndpointMessageCallback.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.contexthub;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.chre.flags.Flags;
+
+/**
+ * An interface used to deliver messages to an opened endpoint session.
+ *
+ * <p>This interface can be attached to an endpoint through {@link
+ * HubEndpoint.Builder#setMessageCallback} method. Methods in this interface will only be called
+ * when the endpoint is currently registered and has an open session. The endpoint will receive
+ * session lifecycle callbacks through {@link IHubEndpointLifecycleCallback}.
+ *
+ * @hide
+ */
+@SystemApi
+@FlaggedApi(Flags.FLAG_OFFLOAD_API)
+public interface IHubEndpointMessageCallback {
+ /**
+ * Callback interface for receiving messages for a particular endpoint session.
+ *
+ * @param session The session this message is sent through. Previously specified in a {@link
+ * IHubEndpointLifecycleCallback#onSessionOpened(HubEndpointSession)} call.
+ * @param message The {@link HubMessage} object representing a message received by the endpoint
+ * that registered this callback interface. This message is constructed by the
+ */
+ void onMessageReceived(@NonNull HubEndpointSession session, @NonNull HubMessage message);
+}
diff --git a/core/java/android/hardware/contexthub/OWNERS b/core/java/android/hardware/contexthub/OWNERS
new file mode 100644
index 0000000..a65a2bf
--- /dev/null
+++ b/core/java/android/hardware/contexthub/OWNERS
@@ -0,0 +1,2 @@
+# ContextHub team
+file:platform/system/chre:/OWNERS
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 28da644..25327a9 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -21,6 +21,8 @@
import static android.view.Display.HdrCapabilities.HdrType;
import static android.view.Display.INVALID_DISPLAY;
+import static com.android.server.display.feature.flags.Flags.FLAG_DISPLAY_LISTENER_PERFORMANCE_IMPROVEMENTS;
+
import android.Manifest;
import android.annotation.FlaggedApi;
import android.annotation.FloatRange;
@@ -59,6 +61,7 @@
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
+import com.android.server.display.feature.flags.Flags;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -100,6 +103,7 @@
private final WeakDisplayCache mDisplayCache = new WeakDisplayCache();
private int mDisplayIdToMirror = INVALID_DISPLAY;
+ private AmbientDisplayConfiguration mAmbientDisplayConfiguration;
/**
* Broadcast receiver that indicates when the Wifi display status changes.
@@ -576,6 +580,8 @@
EVENT_FLAG_DISPLAY_ADDED,
EVENT_FLAG_DISPLAY_CHANGED,
EVENT_FLAG_DISPLAY_REMOVED,
+ EVENT_FLAG_DISPLAY_REFRESH_RATE,
+ EVENT_FLAG_DISPLAY_STATE
})
@Retention(RetentionPolicy.SOURCE)
public @interface EventFlag {}
@@ -596,8 +602,8 @@
*
* @see #registerDisplayListener(DisplayListener, Handler, long)
*
- * @hide
*/
+ @FlaggedApi(FLAG_DISPLAY_LISTENER_PERFORMANCE_IMPROVEMENTS)
public static final long EVENT_FLAG_DISPLAY_ADDED = 1L << 0;
/**
@@ -605,8 +611,8 @@
*
* @see #registerDisplayListener(DisplayListener, Handler, long)
*
- * @hide
*/
+ @FlaggedApi(FLAG_DISPLAY_LISTENER_PERFORMANCE_IMPROVEMENTS)
public static final long EVENT_FLAG_DISPLAY_REMOVED = 1L << 1;
/**
@@ -614,10 +620,27 @@
*
* @see #registerDisplayListener(DisplayListener, Handler, long)
*
- * @hide
*/
+ @FlaggedApi(FLAG_DISPLAY_LISTENER_PERFORMANCE_IMPROVEMENTS)
public static final long EVENT_FLAG_DISPLAY_CHANGED = 1L << 2;
+
+ /**
+ * Event flag to register for a display's refresh rate changes.
+ *
+ * @see #registerDisplayListener(DisplayListener, Handler, long)
+ */
+ @FlaggedApi(FLAG_DISPLAY_LISTENER_PERFORMANCE_IMPROVEMENTS)
+ public static final long EVENT_FLAG_DISPLAY_REFRESH_RATE = 1L << 3;
+
+ /**
+ * Event flag to register for a display state changes.
+ *
+ * @see #registerDisplayListener(DisplayListener, Handler, long)
+ */
+ @FlaggedApi(FLAG_DISPLAY_LISTENER_PERFORMANCE_IMPROVEMENTS)
+ public static final long EVENT_FLAG_DISPLAY_STATE = 1L << 4;
+
/**
* Event flag to register for a display's brightness changes. This notification is sent
* through the {@link DisplayListener#onDisplayChanged} callback method. New brightness
@@ -787,9 +810,6 @@
* if the listener should be invoked on the calling thread's looper.
* @param eventFlags A bitmask of the event types for which this listener is subscribed.
*
- * @see #EVENT_FLAG_DISPLAY_ADDED
- * @see #EVENT_FLAG_DISPLAY_CHANGED
- * @see #EVENT_FLAG_DISPLAY_REMOVED
* @see #registerDisplayListener(DisplayListener, Handler)
* @see #unregisterDisplayListener
*
@@ -806,18 +826,31 @@
* Registers a display listener to receive notifications about given display event types.
*
* @param listener The listener to register.
+ * @param executor Executor for the thread that will be receiving the callbacks. Cannot be null.
+ * @param eventFlags A bitmask of the event types for which this listener is subscribed.
+ *
+ * @see #registerDisplayListener(DisplayListener, Handler)
+ * @see #unregisterDisplayListener
+ *
+ */
+ @FlaggedApi(FLAG_DISPLAY_LISTENER_PERFORMANCE_IMPROVEMENTS)
+ public void registerDisplayListener(@NonNull Executor executor, @EventFlag long eventFlags,
+ @NonNull DisplayListener listener) {
+ mGlobal.registerDisplayListener(listener, executor,
+ mGlobal.mapFlagsToInternalEventFlag(eventFlags, 0),
+ ActivityThread.currentPackageName());
+ }
+
+ /**
+ * Registers a display listener to receive notifications about given display event types.
+ *
+ * @param listener The listener to register.
* @param handler The handler on which the listener should be invoked, or null
* if the listener should be invoked on the calling thread's looper.
* @param eventFlags A bitmask of the event types for which this listener is subscribed.
* @param privateEventFlags A bitmask of the private event types for which this listener
* is subscribed.
*
- * @see #EVENT_FLAG_DISPLAY_ADDED
- * @see #EVENT_FLAG_DISPLAY_CHANGED
- * @see #EVENT_FLAG_DISPLAY_REMOVED
- * @see #PRIVATE_EVENT_FLAG_DISPLAY_BRIGHTNESS
- * @see #PRIVATE_EVENT_FLAG_DISPLAY_CONNECTION_CHANGED
- * @see #PRIVATE_EVENT_FLAG_HDR_SDR_RATIO_CHANGED
* @see #registerDisplayListener(DisplayListener, Handler)
* @see #unregisterDisplayListener
*
@@ -1582,6 +1615,17 @@
}
/**
+ * Returns whether this device supports Always On Display.
+ *
+ * @hide
+ */
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_IS_ALWAYS_ON_AVAILABLE_API)
+ public boolean isAlwaysOnDisplayCurrentlyAvailable() {
+ return getAmbientDisplayConfiguration().alwaysOnAvailableForUser(mContext.getUserId());
+ }
+
+ /**
* Returns whether device supports seamless refresh rate switching.
*
* Match content frame rate setting has three options: seamless, non-seamless and never.
@@ -1643,6 +1687,15 @@
}
}
+ private AmbientDisplayConfiguration getAmbientDisplayConfiguration() {
+ synchronized (this) {
+ if (mAmbientDisplayConfiguration == null) {
+ mAmbientDisplayConfiguration = new AmbientDisplayConfiguration(mContext);
+ }
+ }
+ return mAmbientDisplayConfiguration;
+ }
+
/**
* Creates a VirtualDisplay that will mirror the content of displayIdToMirror
* @param name The name for the virtual display
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index 03b44f6..1e66bee 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -17,6 +17,7 @@
package android.hardware.display;
+import static android.app.PropertyInvalidatedCache.MODULE_SYSTEM;
import static android.hardware.display.DisplayManager.EventFlag;
import static android.Manifest.permission.MANAGE_DISPLAYS;
import static android.view.Display.HdrCapabilities.HdrType;
@@ -62,6 +63,7 @@
import android.view.Surface;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.display.feature.flags.Flags;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -108,6 +110,8 @@
EVENT_DISPLAY_HDR_SDR_RATIO_CHANGED,
EVENT_DISPLAY_CONNECTED,
EVENT_DISPLAY_DISCONNECTED,
+ EVENT_DISPLAY_REFRESH_RATE_CHANGED,
+ EVENT_DISPLAY_STATE_CHANGED
})
@Retention(RetentionPolicy.SOURCE)
public @interface DisplayEvent {}
@@ -119,6 +123,8 @@
public static final int EVENT_DISPLAY_HDR_SDR_RATIO_CHANGED = 5;
public static final int EVENT_DISPLAY_CONNECTED = 6;
public static final int EVENT_DISPLAY_DISCONNECTED = 7;
+ public static final int EVENT_DISPLAY_REFRESH_RATE_CHANGED = 8;
+ public static final int EVENT_DISPLAY_STATE_CHANGED = 9;
@LongDef(prefix = {"INTERNAL_EVENT_DISPLAY"}, flag = true, value = {
INTERNAL_EVENT_FLAG_DISPLAY_ADDED,
@@ -127,6 +133,8 @@
INTERNAL_EVENT_FLAG_DISPLAY_BRIGHTNESS_CHANGED,
INTERNAL_EVENT_FLAG_DISPLAY_HDR_SDR_RATIO_CHANGED,
INTERNAL_EVENT_FLAG_DISPLAY_CONNECTION_CHANGED,
+ INTERNAL_EVENT_FLAG_DISPLAY_REFRESH_RATE,
+ INTERNAL_EVENT_FLAG_DISPLAY_STATE
})
@Retention(RetentionPolicy.SOURCE)
public @interface InternalEventFlag {}
@@ -137,6 +145,8 @@
public static final long INTERNAL_EVENT_FLAG_DISPLAY_BRIGHTNESS_CHANGED = 1L << 3;
public static final long INTERNAL_EVENT_FLAG_DISPLAY_HDR_SDR_RATIO_CHANGED = 1L << 4;
public static final long INTERNAL_EVENT_FLAG_DISPLAY_CONNECTION_CHANGED = 1L << 5;
+ public static final long INTERNAL_EVENT_FLAG_DISPLAY_REFRESH_RATE = 1L << 6;
+ public static final long INTERNAL_EVENT_FLAG_DISPLAY_STATE = 1L << 7;
@UnsupportedAppUsage
private static DisplayManagerGlobal sInstance;
@@ -179,9 +189,11 @@
}
private PropertyInvalidatedCache<Integer, DisplayInfo> mDisplayCache =
- new PropertyInvalidatedCache<Integer, DisplayInfo>(
- 8, // size of display cache
- CACHE_KEY_DISPLAY_INFO_PROPERTY) {
+ new PropertyInvalidatedCache<>(
+ new PropertyInvalidatedCache.Args(MODULE_SYSTEM)
+ .maxEntries(8).api(CACHE_KEY_DISPLAY_INFO_API).isolateUids(false),
+ CACHE_KEY_DISPLAY_INFO_API, null) {
+
@Override
public DisplayInfo recompute(Integer id) {
try {
@@ -1427,6 +1439,18 @@
mListener.onDisplayDisconnected(displayId);
}
break;
+ case EVENT_DISPLAY_REFRESH_RATE_CHANGED:
+ if ((mInternalEventFlagsMask
+ & INTERNAL_EVENT_FLAG_DISPLAY_REFRESH_RATE) != 0) {
+ mListener.onDisplayChanged(displayId);
+ }
+ break;
+ case EVENT_DISPLAY_STATE_CHANGED:
+ if ((mInternalEventFlagsMask
+ & INTERNAL_EVENT_FLAG_DISPLAY_STATE) != 0) {
+ mListener.onDisplayChanged(displayId);
+ }
+ break;
}
if (DEBUG) {
Trace.endSection();
@@ -1493,18 +1517,17 @@
}
/**
- * Name of the property containing a unique token which changes every time we update the
- * system's display configuration.
+ * The API portion of the key that identifies the unique PropertyInvalidatedCache token which
+ * changes every time we update the system's display configuration.
*/
- public static final String CACHE_KEY_DISPLAY_INFO_PROPERTY =
- PropertyInvalidatedCache.createSystemCacheKey("display_info");
+ private static final String CACHE_KEY_DISPLAY_INFO_API = "display_info";
/**
* Invalidates the contents of the display info cache for all applications. Can only
* be called by system_server.
*/
public static void invalidateLocalDisplayInfoCaches() {
- PropertyInvalidatedCache.invalidateCache(CACHE_KEY_DISPLAY_INFO_PROPERTY);
+ PropertyInvalidatedCache.invalidateCache(MODULE_SYSTEM, CACHE_KEY_DISPLAY_INFO_API);
}
/**
@@ -1566,6 +1589,10 @@
return "EVENT_DISPLAY_CONNECTED";
case EVENT_DISPLAY_DISCONNECTED:
return "EVENT_DISPLAY_DISCONNECTED";
+ case EVENT_DISPLAY_REFRESH_RATE_CHANGED:
+ return "EVENT_DISPLAY_REFRESH_RATE_CHANGED";
+ case EVENT_DISPLAY_STATE_CHANGED:
+ return "EVENT_DISPLAY_STATE_CHANGED";
}
return "UNKNOWN";
}
@@ -1630,6 +1657,17 @@
baseEventMask |= INTERNAL_EVENT_FLAG_DISPLAY_REMOVED;
}
+ if (Flags.displayListenerPerformanceImprovements()) {
+ if ((eventFlags & DisplayManager.EVENT_FLAG_DISPLAY_REFRESH_RATE) != 0) {
+ baseEventMask |= INTERNAL_EVENT_FLAG_DISPLAY_REFRESH_RATE;
+ }
+
+ if ((eventFlags & DisplayManager.EVENT_FLAG_DISPLAY_STATE) != 0) {
+ baseEventMask |= INTERNAL_EVENT_FLAG_DISPLAY_STATE;
+ }
+ }
+
+
return baseEventMask;
}
}
diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java
index 399184c..68b6cfc 100644
--- a/core/java/android/hardware/display/DisplayManagerInternal.java
+++ b/core/java/android/hardware/display/DisplayManagerInternal.java
@@ -470,6 +470,31 @@
*/
public abstract boolean isDisplayReadyForMirroring(int displayId);
+
+ /**
+ * Used by the window manager to override the per-display screen brightness based on the
+ * current foreground activity.
+ *
+ * The key of the array is the displayId. If a displayId is missing from the array, this is
+ * equivalent to clearing any existing brightness overrides for that display.
+ *
+ * This method must only be called by the window manager.
+ */
+ public abstract void setScreenBrightnessOverrideFromWindowManager(
+ SparseArray<DisplayBrightnessOverrideRequest> brightnessOverrides);
+
+ /**
+ * Describes a request for overriding the brightness of a single display.
+ */
+ public static class DisplayBrightnessOverrideRequest {
+ // An override of the screen brightness.
+ // Set to PowerManager.BRIGHTNESS_INVALID if there's no override.
+ public float brightness = PowerManager.BRIGHTNESS_INVALID_FLOAT;
+
+ // Tag used to identify the app window requesting the brightness override.
+ public CharSequence tag;
+ }
+
/**
* Describes the requested power state of the display.
*
@@ -505,11 +530,11 @@
// nearby, turning it off temporarily until the object is moved away.
public boolean useProximitySensor;
- // An override of the screen brightness.
+ // A global override of the screen brightness, applied to all displays.
// Set to PowerManager.BRIGHTNESS_INVALID if there's no override.
public float screenBrightnessOverride;
- // Tag used to identify the app window requesting the brightness override.
+ // Tag used to identify the reason for the global brightness override.
public CharSequence screenBrightnessOverrideTag;
// An override of the screen auto-brightness adjustment factor in the range -1 (dimmer) to
diff --git a/core/java/android/hardware/flags/overlayproperties_flags.aconfig b/core/java/android/hardware/flags/flags.aconfig
similarity index 63%
rename from core/java/android/hardware/flags/overlayproperties_flags.aconfig
rename to core/java/android/hardware/flags/flags.aconfig
index 6c86108..5ca6c6b 100644
--- a/core/java/android/hardware/flags/overlayproperties_flags.aconfig
+++ b/core/java/android/hardware/flags/flags.aconfig
@@ -2,6 +2,15 @@
container: "system"
flag {
+ name: "luts_api"
+ is_exported: true
+ is_fixed_read_only: true
+ namespace: "core_graphics"
+ description: "public Luts related Apis"
+ bug: "349667978"
+}
+
+flag {
name: "overlayproperties_class_api"
is_exported: true
namespace: "core_graphics"
diff --git a/core/java/android/hardware/input/AidlInputGestureData.aidl b/core/java/android/hardware/input/AidlInputGestureData.aidl
index e33ec53..f7410d2 100644
--- a/core/java/android/hardware/input/AidlInputGestureData.aidl
+++ b/core/java/android/hardware/input/AidlInputGestureData.aidl
@@ -28,15 +28,18 @@
String appLaunchPackageName;
String appLaunchClassName;
+ @JavaDerive(equals=true)
parcelable KeyTrigger {
int keycode;
int modifierState;
}
+ @JavaDerive(equals=true)
parcelable TouchpadGestureTrigger {
int gestureType;
}
+ @JavaDerive(equals=true)
union Trigger {
KeyTrigger key;
TouchpadGestureTrigger touchpadGesture;
diff --git a/core/java/android/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl
index 3284761..ed510e4 100644
--- a/core/java/android/hardware/input/IInputManager.aidl
+++ b/core/java/android/hardware/input/IInputManager.aidl
@@ -281,4 +281,6 @@
AidlInputGestureData[] getCustomInputGestures(int userId, int tag);
AidlInputGestureData[] getAppLaunchBookmarks();
+
+ void resetLockedModifierState();
}
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index f824192..10224c1 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -260,7 +260,7 @@
}
/**
- * Custom input gesture error: Input gesture already exists
+ * Custom input gesture result success
*
* @hide
*/
@@ -1590,6 +1590,21 @@
}
/**
+ * Resets locked modifier state (i.e.. Caps Lock, Num Lock, Scroll Lock state)
+ *
+ * @hide
+ */
+ @TestApi
+ @SuppressLint("UnflaggedApi") // @TestApi without associated feature.
+ public void resetLockedModifierState() {
+ try {
+ mIm.resetLockedModifierState();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* A callback used to be notified about battery state changes for an input device. The
* {@link #onBatteryStateChanged(int, long, BatteryState)} method will be called once after the
* listener is successfully registered to provide the initial battery state of the device.
diff --git a/core/java/android/hardware/input/KeyGestureEvent.java b/core/java/android/hardware/input/KeyGestureEvent.java
index 24951c4..711dc3a 100644
--- a/core/java/android/hardware/input/KeyGestureEvent.java
+++ b/core/java/android/hardware/input/KeyGestureEvent.java
@@ -115,12 +115,14 @@
public static final int KEY_GESTURE_TYPE_TOGGLE_MOUSE_KEYS = 67;
public static final int KEY_GESTURE_TYPE_SNAP_LEFT_FREEFORM_WINDOW = 68;
public static final int KEY_GESTURE_TYPE_SNAP_RIGHT_FREEFORM_WINDOW = 69;
- public static final int KEY_GESTURE_TYPE_MAXIMIZE_FREEFORM_WINDOW = 70;
- public static final int KEY_GESTURE_TYPE_RESTORE_FREEFORM_WINDOW_SIZE = 71;
+ public static final int KEY_GESTURE_TYPE_MINIMIZE_FREEFORM_WINDOW = 70;
+ public static final int KEY_GESTURE_TYPE_TOGGLE_MAXIMIZE_FREEFORM_WINDOW = 71;
public static final int KEY_GESTURE_TYPE_MAGNIFIER_ZOOM_IN = 72;
public static final int KEY_GESTURE_TYPE_MAGNIFIER_ZOOM_OUT = 73;
public static final int KEY_GESTURE_TYPE_TOGGLE_MAGNIFICATION = 74;
public static final int KEY_GESTURE_TYPE_ACTIVATE_SELECT_TO_SPEAK = 75;
+ public static final int KEY_GESTURE_TYPE_MAXIMIZE_FREEFORM_WINDOW = 76;
+
public static final int FLAG_CANCELLED = 1;
@@ -205,12 +207,13 @@
KEY_GESTURE_TYPE_TOGGLE_MOUSE_KEYS,
KEY_GESTURE_TYPE_SNAP_LEFT_FREEFORM_WINDOW,
KEY_GESTURE_TYPE_SNAP_RIGHT_FREEFORM_WINDOW,
- KEY_GESTURE_TYPE_MAXIMIZE_FREEFORM_WINDOW,
- KEY_GESTURE_TYPE_RESTORE_FREEFORM_WINDOW_SIZE,
+ KEY_GESTURE_TYPE_MINIMIZE_FREEFORM_WINDOW,
+ KEY_GESTURE_TYPE_TOGGLE_MAXIMIZE_FREEFORM_WINDOW,
KEY_GESTURE_TYPE_MAGNIFIER_ZOOM_IN,
KEY_GESTURE_TYPE_MAGNIFIER_ZOOM_OUT,
KEY_GESTURE_TYPE_TOGGLE_MAGNIFICATION,
KEY_GESTURE_TYPE_ACTIVATE_SELECT_TO_SPEAK,
+ KEY_GESTURE_TYPE_MAXIMIZE_FREEFORM_WINDOW
})
@Retention(RetentionPolicy.SOURCE)
public @interface KeyGestureType {
@@ -557,14 +560,6 @@
return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__DESKTOP_MODE;
case KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION:
return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__MULTI_WINDOW_NAVIGATION;
- case KEY_GESTURE_TYPE_SNAP_LEFT_FREEFORM_WINDOW:
- return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__SNAP_LEFT_FREEFORM_WINDOW;
- case KEY_GESTURE_TYPE_SNAP_RIGHT_FREEFORM_WINDOW:
- return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__SNAP_RIGHT_FREEFORM_WINDOW;
- case KEY_GESTURE_TYPE_MAXIMIZE_FREEFORM_WINDOW:
- return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__MAXIMIZE_FREEFORM_WINDOW;
- case KEY_GESTURE_TYPE_RESTORE_FREEFORM_WINDOW_SIZE:
- return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__RESTORE_FREEFORM_WINDOW_SIZE;
default:
return LOG_EVENT_UNSPECIFIED;
}
@@ -777,10 +772,10 @@
return "KEY_GESTURE_TYPE_SNAP_LEFT_FREEFORM_WINDOW";
case KEY_GESTURE_TYPE_SNAP_RIGHT_FREEFORM_WINDOW:
return "KEY_GESTURE_TYPE_SNAP_RIGHT_FREEFORM_WINDOW";
- case KEY_GESTURE_TYPE_MAXIMIZE_FREEFORM_WINDOW:
- return "KEY_GESTURE_TYPE_SNAP_RIGHT_FREEFORM_WINDOW";
- case KEY_GESTURE_TYPE_RESTORE_FREEFORM_WINDOW_SIZE:
- return "KEY_GESTURE_TYPE_RESTORE_FREEFORM_WINDOW_SIZE";
+ case KEY_GESTURE_TYPE_MINIMIZE_FREEFORM_WINDOW:
+ return "KEY_GESTURE_TYPE_MINIMIZE_FREEFORM_WINDOW";
+ case KEY_GESTURE_TYPE_TOGGLE_MAXIMIZE_FREEFORM_WINDOW:
+ return "KEY_GESTURE_TYPE_TOGGLE_MAXIMIZE_FREEFORM_WINDOW";
case KEY_GESTURE_TYPE_MAGNIFIER_ZOOM_IN:
return "KEY_GESTURE_TYPE_MAGNIFIER_ZOOM_IN";
case KEY_GESTURE_TYPE_MAGNIFIER_ZOOM_OUT:
@@ -789,6 +784,8 @@
return "KEY_GESTURE_TYPE_TOGGLE_MAGNIFICATION";
case KEY_GESTURE_TYPE_ACTIVATE_SELECT_TO_SPEAK:
return "KEY_GESTURE_TYPE_ACTIVATE_SELECT_TO_SPEAK";
+ case KEY_GESTURE_TYPE_MAXIMIZE_FREEFORM_WINDOW:
+ return "KEY_GESTURE_TYPE_MAXIMIZE_FREEFORM_WINDOW";
default:
return Integer.toHexString(value);
}
diff --git a/core/java/android/hardware/input/input_framework.aconfig b/core/java/android/hardware/input/input_framework.aconfig
index 4b2f2c2..fee0749 100644
--- a/core/java/android/hardware/input/input_framework.aconfig
+++ b/core/java/android/hardware/input/input_framework.aconfig
@@ -170,4 +170,11 @@
namespace: "input"
description: "Adds key gestures for talkback and magnifier"
bug: "375277034"
-}
\ No newline at end of file
+}
+
+flag {
+ name: "can_window_override_power_gesture_api"
+ namespace: "wallet_integration"
+ description: "Adds new API in WindowManager class to check if the window can override the power key double tap behavior."
+ bug: "378736024"
+ }
\ No newline at end of file
diff --git a/core/java/android/hardware/location/ContextHubManager.java b/core/java/android/hardware/location/ContextHubManager.java
index 494bfc9..426cd69f 100644
--- a/core/java/android/hardware/location/ContextHubManager.java
+++ b/core/java/android/hardware/location/ContextHubManager.java
@@ -34,6 +34,11 @@
import android.content.Context;
import android.content.pm.PackageManager;
import android.hardware.contexthub.ErrorCode;
+import android.hardware.contexthub.HubDiscoveryInfo;
+import android.hardware.contexthub.HubEndpoint;
+import android.hardware.contexthub.HubEndpointInfo;
+import android.hardware.contexthub.HubServiceInfo;
+import android.hardware.contexthub.IHubEndpointLifecycleCallback;
import android.os.Handler;
import android.os.HandlerExecutor;
import android.os.Looper;
@@ -42,6 +47,7 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.Executor;
@@ -679,6 +685,65 @@
}
/**
+ * Find a list of endpoints that matches a specific ID.
+ *
+ * @param endpointId Statically generated ID for an endpoint.
+ * @return A list of {@link HubDiscoveryInfo} objects that represents the result of discovery.
+ */
+ @FlaggedApi(Flags.FLAG_OFFLOAD_API)
+ @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
+ @NonNull
+ public List<HubDiscoveryInfo> findEndpoints(long endpointId) {
+ // TODO(b/379323274): Consider improving these getters to avoid racing with nano app load
+ // timing.
+ try {
+ List<HubEndpointInfo> endpointInfos = mService.findEndpoints(endpointId);
+ List<HubDiscoveryInfo> results = new ArrayList<>(endpointInfos.size());
+ // Wrap with result type
+ for (HubEndpointInfo endpointInfo : endpointInfos) {
+ results.add(new HubDiscoveryInfo(endpointInfo));
+ }
+ return results;
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Find a list of endpoints that provides a specific service.
+ *
+ * @param serviceDescriptor Statically generated ID for an endpoint.
+ * @return A list of {@link HubDiscoveryInfo} objects that represents the result of discovery.
+ * @throws IllegalArgumentException if the serviceDescriptor is empty/null.
+ */
+ @FlaggedApi(Flags.FLAG_OFFLOAD_API)
+ @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
+ @NonNull
+ public List<HubDiscoveryInfo> findEndpoints(@NonNull String serviceDescriptor) {
+ // TODO(b/379323274): Consider improving these getters to avoid racing with nano app load
+ // timing.
+ if (serviceDescriptor.isBlank()) {
+ throw new IllegalArgumentException("Invalid service descriptor: " + serviceDescriptor);
+ }
+ try {
+ List<HubEndpointInfo> endpointInfos =
+ mService.findEndpointsWithService(serviceDescriptor);
+ List<HubDiscoveryInfo> results = new ArrayList<>(endpointInfos.size());
+ // Wrap with result type
+ for (HubEndpointInfo endpointInfo : endpointInfos) {
+ for (HubServiceInfo serviceInfo : endpointInfo.getServiceInfoCollection()) {
+ if (serviceInfo.getServiceDescriptor().equals(serviceDescriptor)) {
+ results.add(new HubDiscoveryInfo(endpointInfo, serviceInfo));
+ }
+ }
+ }
+ return results;
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Set a callback to receive messages from the context hub
*
* @param callback Callback object
@@ -1010,6 +1075,80 @@
}
/**
+ * Registers an endpoint and its callback with the Context Hub Service.
+ *
+ * <p>An endpoint is registered with the Context Hub Service and published to the HAL. When the
+ * registration succeeds, the endpoint can receive notifications through the provided callback.
+ *
+ * @param hubEndpoint {@link HubEndpoint} object created by {@link HubEndpoint.Builder}
+ */
+ @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
+ @FlaggedApi(Flags.FLAG_OFFLOAD_API)
+ public void registerEndpoint(@NonNull HubEndpoint hubEndpoint) {
+ hubEndpoint.register(mService);
+ }
+
+ /**
+ * Use a registered endpoint to connect to another endpoint (destination) without specifying a
+ * service.
+ *
+ * <p>Context Hub Service will create the endpoint session and notify the registered endpoint.
+ * The registered endpoint will receive callbacks on its {@link IHubEndpointLifecycleCallback}
+ * object regarding the lifecycle events of the session.
+ *
+ * @param hubEndpoint {@link HubEndpoint} object previously registered via {@link
+ * ContextHubManager#registerEndpoint(HubEndpoint)}.
+ * @param destination {@link HubEndpointInfo} object that represents an endpoint from previous
+ * endpoint discovery results (e.g. from {@link ContextHubManager#findEndpoints(long)}).
+ */
+ @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
+ @FlaggedApi(Flags.FLAG_OFFLOAD_API)
+ public void openSession(
+ @NonNull HubEndpoint hubEndpoint, @NonNull HubEndpointInfo destination) {
+ hubEndpoint.openSession(destination, null);
+ }
+
+ /**
+ * Use a registered endpoint to connect to another endpoint (destination) for a service
+ * described by a {@link HubServiceInfo} object.
+ *
+ * <p>Context Hub Service will create the endpoint session and notify the registered endpoint.
+ * The registered endpoint will receive callbacks on its {@link IHubEndpointLifecycleCallback}
+ * object regarding the lifecycle events of the session.
+ *
+ * @param hubEndpoint {@link HubEndpoint} object previously registered via {@link
+ * ContextHubManager#registerEndpoint(HubEndpoint)}.
+ * @param destination {@link HubEndpointInfo} object that represents an endpoint from previous
+ * endpoint discovery results (e.g. from {@link ContextHubManager#findEndpoints(long)}).
+ * @param serviceInfo {@link HubServiceInfo} object that describes the service associated with
+ * this session. The information will be sent to the destination as part of open request.
+ */
+ @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
+ @FlaggedApi(Flags.FLAG_OFFLOAD_API)
+ public void openSession(
+ @NonNull HubEndpoint hubEndpoint,
+ @NonNull HubEndpointInfo destination,
+ @NonNull HubServiceInfo serviceInfo) {
+ hubEndpoint.openSession(destination, serviceInfo);
+ }
+
+ /**
+ * Unregisters an endpoint and its callback with the Context Hub Service.
+ *
+ * <p>An endpoint is unregistered from the HAL. The endpoint object will no longer receive
+ * notification through the provided callback.
+ *
+ * @param hubEndpoint {@link HubEndpoint} object created by {@link HubEndpoint.Builder}. This
+ * should match a previously registered object via {@link
+ * ContextHubManager#registerEndpoint(HubEndpoint)}.
+ */
+ @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
+ @FlaggedApi(Flags.FLAG_OFFLOAD_API)
+ public void unregisterEndpoint(@NonNull HubEndpoint hubEndpoint) {
+ hubEndpoint.unregister();
+ }
+
+ /**
* Queries for the list of preloaded nanoapp IDs on the system.
*
* @param hubInfo The Context Hub to query a list of nanoapp IDs from.
@@ -1168,6 +1307,7 @@
requireNonNull(mainLooper, "mainLooper cannot be null");
mService = service;
mMainLooper = mainLooper;
+
try {
mService.registerCallback(mClientCallback);
} catch (RemoteException e) {
diff --git a/core/java/android/hardware/location/ContextHubTransaction.java b/core/java/android/hardware/location/ContextHubTransaction.java
index bd87b5c..ee55f81 100644
--- a/core/java/android/hardware/location/ContextHubTransaction.java
+++ b/core/java/android/hardware/location/ContextHubTransaction.java
@@ -51,18 +51,23 @@
/**
* Constants describing the type of a transaction through the Context Hub Service.
- * {@hide}
+ *
+ * @hide
*/
@Retention(RetentionPolicy.SOURCE)
- @IntDef(prefix = { "TYPE_" }, value = {
- TYPE_LOAD_NANOAPP,
- TYPE_UNLOAD_NANOAPP,
- TYPE_ENABLE_NANOAPP,
- TYPE_DISABLE_NANOAPP,
- TYPE_QUERY_NANOAPPS,
- TYPE_RELIABLE_MESSAGE,
- })
- public @interface Type { }
+ @IntDef(
+ prefix = {"TYPE_"},
+ value = {
+ TYPE_LOAD_NANOAPP,
+ TYPE_UNLOAD_NANOAPP,
+ TYPE_ENABLE_NANOAPP,
+ TYPE_DISABLE_NANOAPP,
+ TYPE_QUERY_NANOAPPS,
+ TYPE_RELIABLE_MESSAGE,
+ TYPE_HUB_MESSAGE_DEFAULT,
+ TYPE_HUB_MESSAGE_REQUIRES_RESPONSE,
+ })
+ public @interface Type {}
public static final int TYPE_LOAD_NANOAPP = 0;
public static final int TYPE_UNLOAD_NANOAPP = 1;
@@ -71,24 +76,34 @@
public static final int TYPE_QUERY_NANOAPPS = 4;
public static final int TYPE_RELIABLE_MESSAGE = 5;
+ @FlaggedApi(Flags.FLAG_OFFLOAD_API)
+ public static final int TYPE_HUB_MESSAGE_DEFAULT = 6;
+
+ @FlaggedApi(Flags.FLAG_OFFLOAD_API)
+ public static final int TYPE_HUB_MESSAGE_REQUIRES_RESPONSE = 7;
+
/**
* Constants describing the result of a transaction or request through the Context Hub Service.
- * {@hide}
+ *
+ * @hide
*/
@Retention(RetentionPolicy.SOURCE)
- @IntDef(prefix = { "RESULT_" }, value = {
- RESULT_SUCCESS,
- RESULT_FAILED_UNKNOWN,
- RESULT_FAILED_BAD_PARAMS,
- RESULT_FAILED_UNINITIALIZED,
- RESULT_FAILED_BUSY,
- RESULT_FAILED_AT_HUB,
- RESULT_FAILED_TIMEOUT,
- RESULT_FAILED_SERVICE_INTERNAL_FAILURE,
- RESULT_FAILED_HAL_UNAVAILABLE,
- RESULT_FAILED_NOT_SUPPORTED,
- })
+ @IntDef(
+ prefix = {"RESULT_"},
+ value = {
+ RESULT_SUCCESS,
+ RESULT_FAILED_UNKNOWN,
+ RESULT_FAILED_BAD_PARAMS,
+ RESULT_FAILED_UNINITIALIZED,
+ RESULT_FAILED_BUSY,
+ RESULT_FAILED_AT_HUB,
+ RESULT_FAILED_TIMEOUT,
+ RESULT_FAILED_SERVICE_INTERNAL_FAILURE,
+ RESULT_FAILED_HAL_UNAVAILABLE,
+ RESULT_FAILED_NOT_SUPPORTED,
+ })
public @interface Result {}
+
public static final int RESULT_SUCCESS = 0;
/**
* Generic failure mode.
@@ -143,7 +158,8 @@
*/
private R mContents;
- Response(@ContextHubTransaction.Result int result, R contents) {
+ /** @hide */
+ public Response(@ContextHubTransaction.Result int result, R contents) {
mResult = result;
mContents = contents;
}
@@ -206,7 +222,8 @@
*/
private boolean mIsResponseSet = false;
- ContextHubTransaction(@Type int type) {
+ /** @hide */
+ public ContextHubTransaction(@Type int type) {
mTransactionType = type;
}
@@ -338,16 +355,16 @@
/**
* Sets the response of the transaction.
*
- * This method should only be invoked by ContextHubManager as a result of a callback from
- * the Context Hub Service indicating the response from a transaction. This method should not be
+ * <p>This method should only be invoked by ContextHubManager as a result of a callback from the
+ * Context Hub Service indicating the response from a transaction. This method should not be
* invoked more than once.
*
* @param response the response to set
- *
* @throws IllegalStateException if this method is invoked multiple times
* @throws NullPointerException if the response is null
+ * @hide
*/
- /* package */ void setResponse(ContextHubTransaction.Response<T> response) {
+ public void setResponse(ContextHubTransaction.Response<T> response) {
synchronized (this) {
Objects.requireNonNull(response, "Response cannot be null");
if (mIsResponseSet) {
diff --git a/core/java/android/hardware/location/IContextHubService.aidl b/core/java/android/hardware/location/IContextHubService.aidl
index b0cc763..f9f41244 100644
--- a/core/java/android/hardware/location/IContextHubService.aidl
+++ b/core/java/android/hardware/location/IContextHubService.aidl
@@ -18,17 +18,20 @@
// Declare any non-default types here with import statements
import android.app.PendingIntent;
-import android.hardware.location.HubInfo;
+import android.hardware.contexthub.HubEndpointInfo;
+import android.hardware.contexthub.IContextHubEndpoint;
+import android.hardware.contexthub.IContextHubEndpointCallback;
import android.hardware.location.ContextHubInfo;
import android.hardware.location.ContextHubMessage;
-import android.hardware.location.NanoApp;
-import android.hardware.location.NanoAppBinary;
-import android.hardware.location.NanoAppFilter;
-import android.hardware.location.NanoAppInstanceInfo;
+import android.hardware.location.HubInfo;
import android.hardware.location.IContextHubCallback;
import android.hardware.location.IContextHubClient;
import android.hardware.location.IContextHubClientCallback;
import android.hardware.location.IContextHubTransactionCallback;
+import android.hardware.location.NanoApp;
+import android.hardware.location.NanoAppBinary;
+import android.hardware.location.NanoAppFilter;
+import android.hardware.location.NanoAppInstanceInfo;
/**
* @hide
@@ -122,4 +125,16 @@
// Enables or disables test mode
@EnforcePermission("ACCESS_CONTEXT_HUB")
boolean setTestMode(in boolean enable);
+
+ // Finds all endpoints that has a specific ID
+ @EnforcePermission("ACCESS_CONTEXT_HUB")
+ List<HubEndpointInfo> findEndpoints(long endpointId);
+
+ // Finds all endpoints that has a specific service
+ @EnforcePermission("ACCESS_CONTEXT_HUB")
+ List<HubEndpointInfo> findEndpointsWithService(String service);
+
+ // Register an endpoint with the context hub
+ @EnforcePermission("ACCESS_CONTEXT_HUB")
+ IContextHubEndpoint registerEndpoint(in HubEndpointInfo pendingEndpointInfo, in IContextHubEndpointCallback callback);
}
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index dadb5c38..977c5bd 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -407,6 +407,12 @@
private boolean mUsingCtrlShiftShortcut = false;
/**
+ * Last handwriting bounds used for stylus handwriting
+ * {@link #setStylusHandwritingRegion(Region)}.
+ */
+ private Region mLastHandwritingRegion;
+
+ /**
* Returns whether {@link InputMethodService} is responsible for rendering the back button and
* the IME switcher button or not when the gestural navigation is enabled.
*
@@ -1532,6 +1538,7 @@
return;
}
editorInfo.makeCompatible(getApplicationInfo().targetSdkVersion);
+ mLastHandwritingRegion = null;
getInputMethodInternal().restartInput(new RemoteInputConnection(ric, sessionId),
editorInfo);
}
@@ -2840,6 +2847,7 @@
mHandler.removeCallbacks(mFinishHwRunnable);
}
mFinishHwRunnable = null;
+ mLastHandwritingRegion = null;
final int requestId = mHandwritingRequestId.getAsInt();
mHandwritingRequestId = OptionalInt.empty();
@@ -3166,6 +3174,40 @@
registerDefaultOnBackInvokedCallback();
}
+ /**
+ * Sets a new stylus handwriting region as user continues to write on an editor on screen.
+ * Stylus strokes that are started within the {@code touchableRegion} are treated as
+ * continuation of handwriting and all the events outside are passed-through to the IME target
+ * app, causing stylus handwriting to finish {@link #finishStylusHandwriting()}.
+ * By default, {@link WindowManager#getMaximumWindowMetrics()} is handwritable and
+ * {@code touchableRegion} resets after each handwriting session.
+ * <p>
+ * For example, the IME can use this API to dynamically expand the stylus handwriting region on
+ * every stylus stroke as user continues to write on an editor. The region should grow around
+ * the last stroke so that a UI element below the IME window is still interactable when it is
+ * spaced sufficiently away (~2 character dimensions) from last stroke.
+ * </p>
+ * <p>
+ * Note: Setting handwriting touchable region is supported on IMEs that support stylus
+ * handwriting {@link InputMethodInfo#supportsStylusHandwriting()}.
+ * </p>
+ *
+ * @param handwritingRegion new stylus handwritable {@link Region} that can accept stylus touch.
+ */
+ @FlaggedApi(Flags.FLAG_ADAPTIVE_HANDWRITING_BOUNDS)
+ public final void setStylusHandwritingRegion(@NonNull Region handwritingRegion) {
+ if (handwritingRegion.equals(mLastHandwritingRegion)) {
+ Log.v(TAG, "Failed to set setStylusHandwritingRegion():"
+ + " same region set twice.");
+ return;
+ }
+
+ if (DEBUG) {
+ Log.d(TAG, "Setting new handwriting region for stylus handwriting "
+ + handwritingRegion + " from last " + mLastHandwritingRegion);
+ }
+ mLastHandwritingRegion = handwritingRegion;
+ }
/**
* Registers an {@link OnBackInvokedCallback} to handle back invocation when ahead-of-time
diff --git a/core/java/android/net/vcn/VcnFrameworkInitializer.java b/core/java/android/net/ConnectivityFrameworkInitializerBaklava.java
similarity index 86%
rename from core/java/android/net/vcn/VcnFrameworkInitializer.java
rename to core/java/android/net/ConnectivityFrameworkInitializerBaklava.java
index 8cb213b..1f0fa92 100644
--- a/core/java/android/net/vcn/VcnFrameworkInitializer.java
+++ b/core/java/android/net/ConnectivityFrameworkInitializerBaklava.java
@@ -14,15 +14,21 @@
* limitations under the License.
*/
-package android.net.vcn;
+package android.net;
+import static android.net.vcn.Flags.FLAG_MAINLINE_VCN_MODULE_API;
+
+import android.annotation.FlaggedApi;
import android.annotation.Nullable;
+import android.annotation.SystemApi;
import android.app.SystemServiceRegistry;
import android.compat.Compatibility;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledSince;
import android.content.Context;
import android.content.pm.PackageManager;
+import android.net.vcn.IVcnManagementService;
+import android.net.vcn.VcnManager;
import android.os.Build;
import android.os.SystemProperties;
@@ -31,8 +37,9 @@
*
* @hide
*/
-// TODO: Expose it as @SystemApi(client = MODULE_LIBRARIES)
-public final class VcnFrameworkInitializer {
+@FlaggedApi(FLAG_MAINLINE_VCN_MODULE_API)
+@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+public final class ConnectivityFrameworkInitializerBaklava {
/**
* Starting with {@link VANILLA_ICE_CREAM}, Telephony feature flags (e.g. {@link
* PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}) are being checked before returning managers
@@ -55,7 +62,7 @@
*/
private static final int VENDOR_API_FOR_ANDROID_V = 202404;
- private VcnFrameworkInitializer() {}
+ private ConnectivityFrameworkInitializerBaklava() {}
// Suppressing AndroidFrameworkCompatChange because we're querying vendor
// partition SDK level, not application's target SDK version (which BTW we
@@ -86,7 +93,10 @@
*
* @throws IllegalStateException if this is called anywhere besides {@link
* SystemServiceRegistry}.
+ * @hide
*/
+ @FlaggedApi(FLAG_MAINLINE_VCN_MODULE_API)
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
public static void registerServiceWrappers() {
SystemServiceRegistry.registerContextAwareService(
VcnManager.VCN_MANAGEMENT_SERVICE_STRING,
diff --git a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
index af93c96..3219ce8 100644
--- a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
+++ b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
@@ -16,6 +16,7 @@
package android.net.vcn;
import static android.net.ipsec.ike.IkeSessionParams.IKE_OPTION_MOBIKE;
+import static android.net.vcn.Flags.FLAG_MAINLINE_VCN_MODULE_API;
import static android.net.vcn.Flags.FLAG_SAFE_MODE_CONFIG;
import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_REQUIRED;
@@ -82,7 +83,15 @@
* </ul>
*/
public final class VcnGatewayConnectionConfig {
- /** @hide */
+ /**
+ * Minimum NAT timeout not set.
+ *
+ * <p>When the timeout is not set, the device will automatically choose a keepalive interval and
+ * may reduce the keepalive frequency for power-optimization.
+ */
+ @FlaggedApi(FLAG_MAINLINE_VCN_MODULE_API)
+ // This constant does not represent a minimum value. It indicates the value is not configured.
+ @SuppressLint("MinMaxConstant")
public static final int MIN_UDP_PORT_4500_NAT_TIMEOUT_UNSET = -1;
/** @hide */
@@ -773,7 +782,7 @@
*
* @param minUdpPort4500NatTimeoutSeconds the maximum keepalive timeout supported by the VCN
* Gateway Connection, generally the minimum duration a NAT mapping is cached on the VCN
- * Gateway.
+ * Gateway; or {@link MIN_UDP_PORT_4500_NAT_TIMEOUT_UNSET} to clear this value.
* @return this {@link Builder} instance, for chaining
*/
@NonNull
@@ -781,8 +790,10 @@
@IntRange(from = MIN_UDP_PORT_4500_NAT_TIMEOUT_SECONDS)
int minUdpPort4500NatTimeoutSeconds) {
Preconditions.checkArgument(
- minUdpPort4500NatTimeoutSeconds >= MIN_UDP_PORT_4500_NAT_TIMEOUT_SECONDS,
- "Timeout must be at least 120s");
+ minUdpPort4500NatTimeoutSeconds == MIN_UDP_PORT_4500_NAT_TIMEOUT_UNSET
+ || minUdpPort4500NatTimeoutSeconds
+ >= MIN_UDP_PORT_4500_NAT_TIMEOUT_SECONDS,
+ "Timeout must be at least 120s or MIN_UDP_PORT_4500_NAT_TIMEOUT_UNSET");
mMinUdpPort4500NatTimeoutSeconds = minUdpPort4500NatTimeoutSeconds;
return this;
diff --git a/core/java/android/net/vcn/VcnTransportInfo.java b/core/java/android/net/vcn/VcnTransportInfo.java
index 1fc91ee..3638429 100644
--- a/core/java/android/net/vcn/VcnTransportInfo.java
+++ b/core/java/android/net/vcn/VcnTransportInfo.java
@@ -17,13 +17,16 @@
package android.net.vcn;
import static android.net.NetworkCapabilities.REDACT_FOR_NETWORK_SETTINGS;
+import static android.net.vcn.Flags.FLAG_MAINLINE_VCN_MODULE_API;
import static android.net.vcn.VcnGatewayConnectionConfig.MIN_UDP_PORT_4500_NAT_TIMEOUT_SECONDS;
import static android.net.vcn.VcnGatewayConnectionConfig.MIN_UDP_PORT_4500_NAT_TIMEOUT_UNSET;
import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+import android.annotation.FlaggedApi;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SystemApi;
import android.net.NetworkCapabilities;
import android.net.TransportInfo;
import android.net.wifi.WifiInfo;
@@ -52,23 +55,29 @@
* @hide
*/
// TODO: Do not store WifiInfo and subscription ID in VcnTransportInfo anymore
-public class VcnTransportInfo implements TransportInfo, Parcelable {
+@FlaggedApi(FLAG_MAINLINE_VCN_MODULE_API)
+@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+public final class VcnTransportInfo implements TransportInfo, Parcelable {
@Nullable private final WifiInfo mWifiInfo;
private final int mSubId;
private final int mMinUdpPort4500NatTimeoutSeconds;
+ /** @hide */
public VcnTransportInfo(@NonNull WifiInfo wifiInfo) {
this(wifiInfo, INVALID_SUBSCRIPTION_ID, MIN_UDP_PORT_4500_NAT_TIMEOUT_UNSET);
}
+ /** @hide */
public VcnTransportInfo(@NonNull WifiInfo wifiInfo, int minUdpPort4500NatTimeoutSeconds) {
this(wifiInfo, INVALID_SUBSCRIPTION_ID, minUdpPort4500NatTimeoutSeconds);
}
+ /** @hide */
public VcnTransportInfo(int subId) {
this(null /* wifiInfo */, subId, MIN_UDP_PORT_4500_NAT_TIMEOUT_UNSET);
}
+ /** @hide */
public VcnTransportInfo(int subId, int minUdpPort4500NatTimeoutSeconds) {
this(null /* wifiInfo */, subId, minUdpPort4500NatTimeoutSeconds);
}
@@ -86,6 +95,7 @@
* <p>If the underlying Network for the associated VCN is Cellular, returns null.
*
* @return the WifiInfo if there is an underlying WiFi connection, else null.
+ * @hide
*/
@Nullable
public WifiInfo getWifiInfo() {
@@ -100,17 +110,27 @@
*
* @return the Subscription ID if a cellular underlying Network is present, else {@link
* android.telephony.SubscriptionManager#INVALID_SUBSCRIPTION_ID}.
+ * @hide
*/
public int getSubId() {
return mSubId;
}
/**
- * Get the VCN provided UDP port 4500 NAT timeout
+ * Get the minimum duration that the VCN Gateway guarantees to preserve a NAT mapping.
*
- * @return the UDP 4500 NAT timeout, or
+ * <p>To ensure uninterrupted connectivity, the device must send keepalive packets before the
+ * timeout. Failure to do so may result in the mapping being cleared and connection termination.
+ * This value is used as a power-optimization hint for other IKEv2/IPsec use cases (e.g. VPNs,
+ * or IWLAN) to reduce the necessary keepalive frequency, thus conserving power and data.
+ *
+ * @return the minimum duration that the VCN Gateway guarantees to preserve a NAT mapping, or
* VcnGatewayConnectionConfig.MIN_UDP_PORT_4500_NAT_TIMEOUT_UNSET if not set.
+ * @see VcnGatewayConnectionConfig.Builder#setMinUdpPort4500NatTimeoutSeconds(int)
+ * @hide
*/
+ @FlaggedApi(FLAG_MAINLINE_VCN_MODULE_API)
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
public int getMinUdpPort4500NatTimeoutSeconds() {
return mMinUdpPort4500NatTimeoutSeconds;
}
@@ -129,12 +149,21 @@
&& mMinUdpPort4500NatTimeoutSeconds == that.mMinUdpPort4500NatTimeoutSeconds;
}
- /** {@inheritDoc} */
+ /**
+ * {@inheritDoc}
+ *
+ * @hide
+ */
+ @FlaggedApi(FLAG_MAINLINE_VCN_MODULE_API)
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
@Override
public int describeContents() {
return 0;
}
+ /** @hide */
+ @FlaggedApi(FLAG_MAINLINE_VCN_MODULE_API)
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
@Override
@NonNull
public TransportInfo makeCopy(long redactions) {
@@ -149,6 +178,9 @@
mMinUdpPort4500NatTimeoutSeconds);
}
+ /** @hide */
+ @FlaggedApi(FLAG_MAINLINE_VCN_MODULE_API)
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
@Override
public long getApplicableRedactions() {
long redactions = REDACT_FOR_NETWORK_SETTINGS;
@@ -161,7 +193,13 @@
return redactions;
}
- /** {@inheritDoc} */
+ /**
+ * {@inheritDoc}
+ *
+ * @hide
+ */
+ @FlaggedApi(FLAG_MAINLINE_VCN_MODULE_API)
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeInt(mSubId);
@@ -174,7 +212,13 @@
return "VcnTransportInfo { mWifiInfo = " + mWifiInfo + ", mSubId = " + mSubId + " }";
}
- /** Implement the Parcelable interface */
+ /**
+ * Implement the Parcelable interface
+ *
+ * @hide
+ */
+ @FlaggedApi(FLAG_MAINLINE_VCN_MODULE_API)
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
public static final @NonNull Creator<VcnTransportInfo> CREATOR =
new Creator<VcnTransportInfo>() {
public VcnTransportInfo createFromParcel(Parcel in) {
@@ -201,37 +245,63 @@
}
};
- /** This class can be used to construct a {@link VcnTransportInfo}. */
+ /**
+ * This class can be used to construct a {@link VcnTransportInfo}.
+ *
+ * @hide
+ */
+ @FlaggedApi(FLAG_MAINLINE_VCN_MODULE_API)
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
public static final class Builder {
private int mMinUdpPort4500NatTimeoutSeconds = MIN_UDP_PORT_4500_NAT_TIMEOUT_UNSET;
- /** Construct Builder */
+ /**
+ * Construct Builder
+ *
+ * @hide
+ */
+ @FlaggedApi(FLAG_MAINLINE_VCN_MODULE_API)
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
public Builder() {}
/**
- * Sets the maximum supported IKEv2/IPsec NATT keepalive timeout.
+ * Set the minimum duration that the VCN Gateway guarantees to preserve a NAT mapping.
*
* <p>This is used as a power-optimization hint for other IKEv2/IPsec use cases (e.g. VPNs,
* or IWLAN) to reduce the necessary keepalive frequency, thus conserving power and data.
*
- * @param minUdpPort4500NatTimeoutSeconds the maximum keepalive timeout supported by the VCN
- * Gateway Connection, generally the minimum duration a NAT mapping is cached on the VCN
- * Gateway.
+ * @param minUdpPort4500NatTimeoutSeconds the minimum duration that the VCN Gateway
+ * guarantees to preserve a NAT mapping, or {@link MIN_UDP_PORT_4500_NAT_TIMEOUT_UNSET}
+ * to clear this value. To ensure uninterrupted connectivity, the device must send
+ * keepalive packets within this interval. Failure to do so may result in the mapping
+ * being cleared and connection termination.
* @return this {@link Builder} instance, for chaining
+ * @see VcnGatewayConnectionConfig.Builder#setMinUdpPort4500NatTimeoutSeconds(int)
+ * @hide
*/
+ @FlaggedApi(FLAG_MAINLINE_VCN_MODULE_API)
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
@NonNull
public Builder setMinUdpPort4500NatTimeoutSeconds(
@IntRange(from = MIN_UDP_PORT_4500_NAT_TIMEOUT_SECONDS)
int minUdpPort4500NatTimeoutSeconds) {
Preconditions.checkArgument(
- minUdpPort4500NatTimeoutSeconds >= MIN_UDP_PORT_4500_NAT_TIMEOUT_SECONDS,
- "Timeout must be at least 120s");
+ minUdpPort4500NatTimeoutSeconds == MIN_UDP_PORT_4500_NAT_TIMEOUT_UNSET
+ || minUdpPort4500NatTimeoutSeconds
+ >= MIN_UDP_PORT_4500_NAT_TIMEOUT_SECONDS,
+ "Timeout must be at least 120s or MIN_UDP_PORT_4500_NAT_TIMEOUT_UNSET");
mMinUdpPort4500NatTimeoutSeconds = minUdpPort4500NatTimeoutSeconds;
return Builder.this;
}
- /** Build a VcnTransportInfo instance */
+ /**
+ * Build a VcnTransportInfo instance
+ *
+ * @hide
+ */
+ @FlaggedApi(FLAG_MAINLINE_VCN_MODULE_API)
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
@NonNull
public VcnTransportInfo build() {
return new VcnTransportInfo(
diff --git a/core/java/android/os/BinderProxy.java b/core/java/android/os/BinderProxy.java
index 3b5a99e..01222cd 100644
--- a/core/java/android/os/BinderProxy.java
+++ b/core/java/android/os/BinderProxy.java
@@ -36,6 +36,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
@@ -651,28 +652,39 @@
private native boolean unlinkToDeathNative(DeathRecipient recipient, int flags);
/**
- * This list is to hold strong reference to the frozen state callbacks. The callbacks are only
- * weakly referenced by JNI so the strong references here are needed to keep the callbacks
- * around until the proxy is GC'ed.
+ * This map is to hold strong reference to the frozen state callbacks.
+ *
+ * The callbacks are only weakly referenced by JNI so the strong references here are needed to
+ * keep the callbacks around until the proxy is GC'ed.
+ *
+ * The key is the original callback passed into {@link #addFrozenStateChangeCallback}. The value
+ * is the wrapped callback created in {@link #addFrozenStateChangeCallback} to dispatch the
+ * calls on the desired executor.
*/
- private List<FrozenStateChangeCallback> mFrozenStateChangeCallbacks =
- Collections.synchronizedList(new ArrayList<>());
+ private Map<FrozenStateChangeCallback, FrozenStateChangeCallback> mFrozenStateChangeCallbacks =
+ Collections.synchronizedMap(new HashMap<>());
/**
* See {@link IBinder#addFrozenStateChangeCallback(FrozenStateChangeCallback)}
*/
- public void addFrozenStateChangeCallback(FrozenStateChangeCallback callback)
+ public void addFrozenStateChangeCallback(Executor executor, FrozenStateChangeCallback callback)
throws RemoteException {
- addFrozenStateChangeCallbackNative(callback);
- mFrozenStateChangeCallbacks.add(callback);
+ FrozenStateChangeCallback wrappedCallback = (who, state) ->
+ executor.execute(() -> callback.onFrozenStateChanged(who, state));
+ addFrozenStateChangeCallbackNative(wrappedCallback);
+ mFrozenStateChangeCallbacks.put(callback, wrappedCallback);
}
/**
* See {@link IBinder#removeFrozenStateChangeCallback}
*/
- public boolean removeFrozenStateChangeCallback(FrozenStateChangeCallback callback) {
- mFrozenStateChangeCallbacks.remove(callback);
- return removeFrozenStateChangeCallbackNative(callback);
+ public boolean removeFrozenStateChangeCallback(FrozenStateChangeCallback callback)
+ throws IllegalArgumentException {
+ FrozenStateChangeCallback wrappedCallback = mFrozenStateChangeCallbacks.remove(callback);
+ if (wrappedCallback == null) {
+ throw new IllegalArgumentException("callback not found");
+ }
+ return removeFrozenStateChangeCallbackNative(wrappedCallback);
}
private native void addFrozenStateChangeCallbackNative(FrozenStateChangeCallback callback)
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 102bdd0..c2e9260 100644
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -32,6 +32,7 @@
import android.content.Context;
import android.ravenwood.annotation.RavenwoodKeepWholeClass;
import android.sdk.Flags;
+import android.sysprop.BackportedFixesProperties;
import android.sysprop.DeviceProperties;
import android.sysprop.SocProperties;
import android.sysprop.TelephonyProperties;
@@ -1612,12 +1613,25 @@
* is not applicable on this device,
* otherwise {@link #BACKPORTED_FIX_STATUS_UNKNOWN}.
*/
-
@FlaggedApi(android.os.Flags.FLAG_API_FOR_BACKPORTED_FIXES)
public static @BackportedFixStatus int getBackportedFixStatus(long id) {
- // TODO: b/308461809 - query aliases from system prop
- // TODO: b/372518979 - use backported fix datastore.
- return BACKPORTED_FIX_STATUS_UNKNOWN;
+ if (id <= 0 || id > 1023) {
+ return BACKPORTED_FIX_STATUS_UNKNOWN;
+ }
+ return isBitSet(BackportedFixesProperties.alias_bitset(), (int) id)
+ ? BACKPORTED_FIX_STATUS_FIXED : BACKPORTED_FIX_STATUS_UNKNOWN;
+ }
+
+ private static boolean isBitSet(List<Long> bitsetLongArray, int bitIndex) {
+ // Because java.util.BitSet is not threadsafe do the calculations here instead.
+ if (bitIndex < 0) {
+ return false;
+ }
+ int arrayIndex = bitIndex >> 6;
+ if (bitsetLongArray.size() <= arrayIndex) {
+ return false;
+ }
+ return (bitsetLongArray.get(arrayIndex) & (1L << bitIndex)) != 0;
}
/**
diff --git a/core/java/android/os/Bundle.java b/core/java/android/os/Bundle.java
index 99e7d166..05bd10b 100644
--- a/core/java/android/os/Bundle.java
+++ b/core/java/android/os/Bundle.java
@@ -18,10 +18,12 @@
import static java.util.Objects.requireNonNull;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.util.ArrayMap;
import android.util.Size;
@@ -72,16 +74,18 @@
/**
* Status when the Bundle can <b>assert</b> that the underlying Parcel DOES NOT contain
* Binder object(s).
- *
* @hide
*/
+ @FlaggedApi(Flags.FLAG_ENABLE_HAS_BINDERS)
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
public static final int STATUS_BINDERS_NOT_PRESENT = 0;
/**
* Status when the Bundle can <b>assert</b> that there are Binder object(s) in the Parcel.
- *
* @hide
*/
+ @FlaggedApi(Flags.FLAG_ENABLE_HAS_BINDERS)
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
public static final int STATUS_BINDERS_PRESENT = 1;
/**
@@ -94,9 +98,10 @@
* object to the Bundle but it is not possible to assert this fact unless the Bundle is written
* to a Parcel.
* </p>
- *
* @hide
*/
+ @FlaggedApi(Flags.FLAG_ENABLE_HAS_BINDERS)
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
public static final int STATUS_BINDERS_UNKNOWN = 2;
/** @hide */
@@ -417,6 +422,8 @@
* Returns a status indicating whether the bundle contains any parcelled Binder objects.
* @hide
*/
+ @FlaggedApi(Flags.FLAG_ENABLE_HAS_BINDERS)
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
public @HasBinderStatus int hasBinders() {
if ((mFlags & FLAG_HAS_BINDERS_KNOWN) != 0) {
if ((mFlags & FLAG_HAS_BINDERS) != 0) {
diff --git a/core/java/android/os/CombinedMessageQueue/MessageQueue.java b/core/java/android/os/CombinedMessageQueue/MessageQueue.java
index 7529ab9..4c9f08d 100644
--- a/core/java/android/os/CombinedMessageQueue/MessageQueue.java
+++ b/core/java/android/os/CombinedMessageQueue/MessageQueue.java
@@ -19,6 +19,8 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.TestApi;
+import android.app.ActivityThread;
+import android.app.Instrumentation;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Process;
import android.os.UserHandle;
@@ -31,7 +33,6 @@
import android.util.proto.ProtoOutputStream;
import dalvik.annotation.optimization.NeverCompile;
-import dalvik.system.VMDebug;
import java.io.FileDescriptor;
import java.lang.annotation.Retention;
@@ -111,11 +112,34 @@
private native static void nativeSetFileDescriptorEvents(long ptr, int fd, int events);
MessageQueue(boolean quitAllowed) {
- mUseConcurrent = UserHandle.isCore(Process.myUid()) && !VMDebug.isDebuggingEnabled();
+ // Concurrent mode modifies behavior that is observable via reflection and is commonly used
+ // by tests.
+ // For now, we limit it to system processes to avoid breaking apps and their tests.
+ mUseConcurrent = UserHandle.isCore(Process.myUid());
+ // Even then, we don't use it if instrumentation is loaded as it breaks some
+ // platform tests.
+ final Instrumentation instrumentation = getInstrumentation();
+ mUseConcurrent &= instrumentation == null || !instrumentation.isInstrumenting();
+ // We can lift this restriction in the future after we've made it possible for test authors
+ // to test Looper and MessageQueue without resorting to reflection.
+
mQuitAllowed = quitAllowed;
mPtr = nativeInit();
}
+ @android.ravenwood.annotation.RavenwoodReplace(blockedBy = ActivityThread.class)
+ private static Instrumentation getInstrumentation() {
+ final ActivityThread activityThread = ActivityThread.currentActivityThread();
+ if (activityThread != null) {
+ return activityThread.getInstrumentation();
+ }
+ return null;
+ }
+
+ private static Instrumentation getInstrumentation$ravenwood() {
+ return null; // Instrumentation not supported on Ravenwood yet.
+ }
+
@Override
protected void finalize() throws Throwable {
try {
diff --git a/core/java/android/os/CpuHeadroomParams.java b/core/java/android/os/CpuHeadroomParams.java
new file mode 100644
index 0000000..f0d4f7d
--- /dev/null
+++ b/core/java/android/os/CpuHeadroomParams.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
+import android.os.health.SystemHealthManager;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Headroom request params used by {@link SystemHealthManager#getCpuHeadroom(CpuHeadroomParams)}.
+ */
+@FlaggedApi(Flags.FLAG_CPU_GPU_HEADROOMS)
+public final class CpuHeadroomParams {
+ final CpuHeadroomParamsInternal mInternal;
+
+ public CpuHeadroomParams() {
+ mInternal = new CpuHeadroomParamsInternal();
+ }
+
+ /** @hide */
+ @IntDef(flag = false, prefix = {"CPU_HEADROOM_CALCULATION_TYPE_"}, value = {
+ CPU_HEADROOM_CALCULATION_TYPE_MIN, // 0
+ CPU_HEADROOM_CALCULATION_TYPE_AVERAGE, // 1
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface CpuHeadroomCalculationType {
+ }
+
+ /**
+ * Calculates the headroom based on minimum value over a device-defined window.
+ */
+ public static final int CPU_HEADROOM_CALCULATION_TYPE_MIN = 0;
+
+ /**
+ * Calculates the headroom based on average value over a device-defined window.
+ */
+ public static final int CPU_HEADROOM_CALCULATION_TYPE_AVERAGE = 1;
+
+ /**
+ * Sets the headroom calculation type.
+ * <p>
+ *
+ * @throws IllegalArgumentException if the type is invalid.
+ */
+ public void setCalculationType(@CpuHeadroomCalculationType int calculationType) {
+ switch (calculationType) {
+ case CPU_HEADROOM_CALCULATION_TYPE_MIN:
+ case CPU_HEADROOM_CALCULATION_TYPE_AVERAGE:
+ mInternal.calculationType = (byte) calculationType;
+ return;
+ }
+ throw new IllegalArgumentException("Invalid calculation type: " + calculationType);
+ }
+
+ /**
+ * Gets the headroom calculation type.
+ * Default to {@link #CPU_HEADROOM_CALCULATION_TYPE_MIN} if not set.
+ */
+ public @CpuHeadroomCalculationType int getCalculationType() {
+ @CpuHeadroomCalculationType int validatedType = switch ((int) mInternal.calculationType) {
+ case CPU_HEADROOM_CALCULATION_TYPE_MIN, CPU_HEADROOM_CALCULATION_TYPE_AVERAGE ->
+ mInternal.calculationType;
+ default -> CPU_HEADROOM_CALCULATION_TYPE_MIN;
+ };
+ return validatedType;
+ }
+
+ /**
+ * @hide
+ */
+ public CpuHeadroomParamsInternal getInternal() {
+ return mInternal;
+ }
+}
diff --git a/core/java/android/os/CpuHeadroomParamsInternal.aidl b/core/java/android/os/CpuHeadroomParamsInternal.aidl
new file mode 100644
index 0000000..6cc4699
--- /dev/null
+++ b/core/java/android/os/CpuHeadroomParamsInternal.aidl
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.hardware.power.CpuHeadroomParams;
+
+/**
+ * Changes should be synced with match function of HintManagerService#CpuHeadroomCacheItem.
+ * {@hide}
+ */
+@JavaDerive(equals = true, toString = true)
+parcelable CpuHeadroomParamsInternal {
+ boolean usesDeviceHeadroom = false;
+ CpuHeadroomParams.CalculationType calculationType = CpuHeadroomParams.CalculationType.MIN;
+ CpuHeadroomParams.SelectionType selectionType = CpuHeadroomParams.SelectionType.ALL;
+}
+
diff --git a/core/java/android/os/GpuHeadroomParams.java b/core/java/android/os/GpuHeadroomParams.java
new file mode 100644
index 0000000..efb2a28
--- /dev/null
+++ b/core/java/android/os/GpuHeadroomParams.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
+import android.os.health.SystemHealthManager;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Headroom request params used by {@link SystemHealthManager#getGpuHeadroom(GpuHeadroomParams)}.
+ */
+@FlaggedApi(Flags.FLAG_CPU_GPU_HEADROOMS)
+public final class GpuHeadroomParams {
+ final GpuHeadroomParamsInternal mInternal;
+
+ public GpuHeadroomParams() {
+ mInternal = new GpuHeadroomParamsInternal();
+ }
+
+ /** @hide */
+ @IntDef(flag = false, prefix = {"GPU_HEADROOM_CALCULATION_TYPE_"}, value = {
+ GPU_HEADROOM_CALCULATION_TYPE_MIN, // 0
+ GPU_HEADROOM_CALCULATION_TYPE_AVERAGE, // 1
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface GpuHeadroomCalculationType {
+ }
+
+ /**
+ * Calculates the headroom based on minimum value over a device-defined window.
+ */
+ public static final int GPU_HEADROOM_CALCULATION_TYPE_MIN = 0;
+
+ /**
+ * Calculates the headroom based on average value over a device-defined window.
+ */
+ public static final int GPU_HEADROOM_CALCULATION_TYPE_AVERAGE = 1;
+
+ /**
+ * Sets the headroom calculation type.
+ * <p>
+ *
+ * @throws IllegalArgumentException if the type is invalid.
+ */
+ public void setCalculationType(@GpuHeadroomCalculationType int calculationType) {
+ switch (calculationType) {
+ case GPU_HEADROOM_CALCULATION_TYPE_MIN:
+ case GPU_HEADROOM_CALCULATION_TYPE_AVERAGE:
+ mInternal.calculationType = (byte) calculationType;
+ return;
+ }
+ throw new IllegalArgumentException("Invalid calculation type: " + calculationType);
+ }
+
+ /**
+ * Gets the headroom calculation type.
+ * Default to {@link #GPU_HEADROOM_CALCULATION_TYPE_MIN} if not set.
+ */
+ public @GpuHeadroomCalculationType int getCalculationType() {
+ @GpuHeadroomCalculationType int validatedType = switch ((int) mInternal.calculationType) {
+ case GPU_HEADROOM_CALCULATION_TYPE_MIN, GPU_HEADROOM_CALCULATION_TYPE_AVERAGE ->
+ mInternal.calculationType;
+ default -> GPU_HEADROOM_CALCULATION_TYPE_MIN;
+ };
+ return validatedType;
+ }
+
+ /**
+ * @hide
+ */
+ public GpuHeadroomParamsInternal getInternal() {
+ return mInternal;
+ }
+}
diff --git a/core/java/android/os/GpuHeadroomParamsInternal.aidl b/core/java/android/os/GpuHeadroomParamsInternal.aidl
new file mode 100644
index 0000000..20309e7
--- /dev/null
+++ b/core/java/android/os/GpuHeadroomParamsInternal.aidl
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.hardware.power.GpuHeadroomParams;
+
+/**
+ * Changes should be synced with match function of HintManagerService#GpuHeadroomCacheItem.
+ * {@hide}
+ */
+@JavaDerive(equals = true, toString = true)
+parcelable GpuHeadroomParamsInternal {
+ GpuHeadroomParams.CalculationType calculationType = GpuHeadroomParams.CalculationType.MIN;
+}
diff --git a/core/java/android/os/IBinder.java b/core/java/android/os/IBinder.java
index a997f4c..8cfd324 100644
--- a/core/java/android/os/IBinder.java
+++ b/core/java/android/os/IBinder.java
@@ -16,6 +16,7 @@
package android.os;
+import android.annotation.CallbackExecutor;
import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -25,6 +26,7 @@
import java.io.FileDescriptor;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.concurrent.Executor;
/**
* Base interface for a remotable object, the core part of a lightweight
@@ -397,12 +399,31 @@
@interface State {
}
+ /**
+ * Represents the frozen state of the remote process.
+ *
+ * While in this state, the remote process won't be able to receive and handle a
+ * transaction. Therefore, any asynchronous transactions will be buffered and delivered when
+ * the process is unfrozen, and any synchronous transactions will result in an error.
+ *
+ * Buffered transactions may be stale by the time that the process is unfrozen and handles
+ * them. To avoid overwhelming the remote process with stale events or overflowing their
+ * buffers, it's best to avoid sending binder transactions to a frozen process.
+ */
int STATE_FROZEN = 0;
+
+ /**
+ * Represents the unfrozen state of the remote process.
+ *
+ * In this state, the process hosting the object can execute and is not restricted
+ * by the freezer from using the CPU or responding to binder transactions.
+ */
int STATE_UNFROZEN = 1;
/**
* Interface for receiving a callback when the process hosting an IBinder
* has changed its frozen state.
+ *
* @param who The IBinder whose hosting process has changed state.
* @param state The latest state.
*/
@@ -427,16 +448,32 @@
* <p>You will only receive state change notifications for remote binders, as local binders by
* definition can't be frozen without you being frozen too.</p>
*
+ * @param executor The executor on which to run the callback.
+ * @param callback The callback used to deliver state change notifications.
+ *
* <p>@throws {@link UnsupportedOperationException} if the kernel binder driver does not support
* this feature.
*/
@FlaggedApi(Flags.FLAG_BINDER_FROZEN_STATE_CHANGE_CALLBACK)
- default void addFrozenStateChangeCallback(@NonNull FrozenStateChangeCallback callback)
+ default void addFrozenStateChangeCallback(
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull FrozenStateChangeCallback callback)
throws RemoteException {
throw new UnsupportedOperationException();
}
/**
+ * Same as {@link #addFrozenStateChangeCallback(Executor, FrozenStateChangeCallback)} except
+ * that callbacks are invoked on a binder thread.
+ *
+ * @hide
+ */
+ default void addFrozenStateChangeCallback(@NonNull FrozenStateChangeCallback callback)
+ throws RemoteException {
+ addFrozenStateChangeCallback(Runnable::run, callback);
+ }
+
+ /**
* Unregister a {@link FrozenStateChangeCallback}. The callback will no longer be invoked when
* the hosting process changes its frozen state.
*/
diff --git a/core/java/android/os/IHintManager.aidl b/core/java/android/os/IHintManager.aidl
index 73cdd56..3312055 100644
--- a/core/java/android/os/IHintManager.aidl
+++ b/core/java/android/os/IHintManager.aidl
@@ -17,6 +17,8 @@
package android.os;
+import android.os.CpuHeadroomParamsInternal;
+import android.os.GpuHeadroomParamsInternal;
import android.os.IHintSession;
import android.hardware.power.ChannelConfig;
import android.hardware.power.SessionConfig;
@@ -50,4 +52,8 @@
*/
@nullable ChannelConfig getSessionChannel(in IBinder token);
oneway void closeSessionChannel();
+ float[] getCpuHeadroom(in CpuHeadroomParamsInternal params);
+ long getCpuHeadroomMinIntervalMillis();
+ float getGpuHeadroom(in GpuHeadroomParamsInternal params);
+ long getGpuHeadroomMinIntervalMillis();
}
diff --git a/core/java/android/os/OWNERS b/core/java/android/os/OWNERS
index 590ddb4..e63b664 100644
--- a/core/java/android/os/OWNERS
+++ b/core/java/android/os/OWNERS
@@ -78,6 +78,9 @@
# PermissionEnforcer
per-file PermissionEnforcer.java = tweek@google.com, brufino@google.com
+# RemoteCallbackList
+per-file RemoteCallbackList.java = shayba@google.com
+
# ART
per-file ArtModuleServiceManager.java = file:platform/art:/OWNERS
@@ -125,3 +128,6 @@
# Dropbox
per-file DropBoxManager* = mwachens@google.com
+
+# Flags
+per-file flags.aconfig = file:/FF_LEADS_OWNERS
diff --git a/core/java/android/os/PerformanceHintManager.java b/core/java/android/os/PerformanceHintManager.java
index 224b10d..2b0042d 100644
--- a/core/java/android/os/PerformanceHintManager.java
+++ b/core/java/android/os/PerformanceHintManager.java
@@ -22,6 +22,7 @@
import android.annotation.Nullable;
import android.annotation.SystemService;
import android.annotation.TestApi;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import com.android.internal.util.Preconditions;
@@ -106,7 +107,9 @@
* All timings should be in {@link SystemClock#uptimeNanos()}.
*/
public static class Session implements Closeable {
- private long mNativeSessionPtr;
+ /** @hide */
+ @UnsupportedAppUsage
+ public long mNativeSessionPtr;
/** @hide */
public Session(long nativeSessionPtr) {
diff --git a/core/java/android/os/PowerManagerInternal.java b/core/java/android/os/PowerManagerInternal.java
index 29ccb85..9435f4d 100644
--- a/core/java/android/os/PowerManagerInternal.java
+++ b/core/java/android/os/PowerManagerInternal.java
@@ -98,18 +98,6 @@
}
/**
- * Used by the window manager to override the screen brightness based on the
- * current foreground activity.
- *
- * This method must only be called by the window manager.
- *
- * @param brightness The overridden brightness, or Float.NaN to disable the override.
- * @param tag Source identifier of the app window that requests the override.
- */
- public abstract void setScreenBrightnessOverrideFromWindowManager(
- float brightness, CharSequence tag);
-
- /**
* Used by the window manager to override the user activity timeout based on the
* current foreground activity. It can only be used to make the timeout shorter
* than usual, not longer.
diff --git a/core/java/android/os/RemoteCallbackList.java b/core/java/android/os/RemoteCallbackList.java
index 91c482fa..d5630fd 100644
--- a/core/java/android/os/RemoteCallbackList.java
+++ b/core/java/android/os/RemoteCallbackList.java
@@ -16,6 +16,7 @@
package android.os;
+import android.annotation.CallbackExecutor;
import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -29,6 +30,7 @@
import java.lang.annotation.RetentionPolicy;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.Executor;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
@@ -134,6 +136,7 @@
private final @FrozenCalleePolicy int mFrozenCalleePolicy;
private final int mMaxQueueSize;
+ private final Executor mExecutor;
private final class Interface implements IBinder.DeathRecipient,
IBinder.FrozenStateChangeCallback {
@@ -197,7 +200,7 @@
void maybeSubscribeToFrozenCallback() throws RemoteException {
if (mFrozenCalleePolicy != FROZEN_CALLEE_POLICY_UNSET) {
try {
- mBinder.addFrozenStateChangeCallback(this);
+ mBinder.addFrozenStateChangeCallback(mExecutor, this);
} catch (UnsupportedOperationException e) {
// The kernel does not support frozen notifications. In this case we want to
// silently fall back to FROZEN_CALLEE_POLICY_UNSET. This is done by simply
@@ -237,6 +240,7 @@
private @FrozenCalleePolicy int mFrozenCalleePolicy;
private int mMaxQueueSize = DEFAULT_MAX_QUEUE_SIZE;
private InterfaceDiedCallback mInterfaceDiedCallback;
+ private Executor mExecutor;
/**
* Creates a Builder for {@link RemoteCallbackList}.
@@ -285,6 +289,18 @@
}
/**
+ * Sets the executor to be used when invoking callbacks asynchronously.
+ *
+ * This is only used when callbacks need to be invoked asynchronously, e.g. when the process
+ * hosting a callback becomes unfrozen. Callbacks that can be invoked immediately run on the
+ * same thread that calls {@link #broadcast} synchronously.
+ */
+ public @NonNull Builder setExecutor(@NonNull @CallbackExecutor Executor executor) {
+ mExecutor = executor;
+ return this;
+ }
+
+ /**
* For notifying when the process hosting a callback interface has died.
*
* @param <E> The remote callback interface type.
@@ -308,15 +324,21 @@
* @return The built {@link RemoteCallbackList} object.
*/
public @NonNull RemoteCallbackList<E> build() {
+ Executor executor = mExecutor;
+ if (executor == null && mFrozenCalleePolicy != FROZEN_CALLEE_POLICY_UNSET) {
+ // TODO Throw an exception here once the existing API caller is updated to provide
+ // an executor.
+ executor = new HandlerExecutor(Handler.getMain());
+ }
if (mInterfaceDiedCallback != null) {
- return new RemoteCallbackList<E>(mFrozenCalleePolicy, mMaxQueueSize) {
+ return new RemoteCallbackList<E>(mFrozenCalleePolicy, mMaxQueueSize, executor) {
@Override
public void onCallbackDied(E deadInterface, Object cookie) {
mInterfaceDiedCallback.onInterfaceDied(this, deadInterface, cookie);
}
};
}
- return new RemoteCallbackList<E>(mFrozenCalleePolicy, mMaxQueueSize);
+ return new RemoteCallbackList<E>(mFrozenCalleePolicy, mMaxQueueSize, executor);
}
}
@@ -341,13 +363,23 @@
}
/**
+ * Returns the executor used when invoking callbacks asynchronously.
+ *
+ * @return The executor.
+ */
+ @FlaggedApi(Flags.FLAG_BINDER_FROZEN_STATE_CHANGE_CALLBACK)
+ public @Nullable Executor getExecutor() {
+ return mExecutor;
+ }
+
+ /**
* Creates a RemoteCallbackList with {@link #FROZEN_CALLEE_POLICY_UNSET}. This is equivalent to
* <pre>
* new RemoteCallbackList.Build(RemoteCallbackList.FROZEN_CALLEE_POLICY_UNSET).build()
* </pre>
*/
public RemoteCallbackList() {
- this(FROZEN_CALLEE_POLICY_UNSET, DEFAULT_MAX_QUEUE_SIZE);
+ this(FROZEN_CALLEE_POLICY_UNSET, DEFAULT_MAX_QUEUE_SIZE, null);
}
/**
@@ -362,10 +394,14 @@
* recipient's process is frozen. Once the limit is reached, the oldest callbacks would be
* dropped to keep the size under limit. Ignored except for
* {@link #FROZEN_CALLEE_POLICY_ENQUEUE_ALL}.
+ *
+ * @param executor The executor used when invoking callbacks asynchronously.
*/
- private RemoteCallbackList(@FrozenCalleePolicy int frozenCalleePolicy, int maxQueueSize) {
+ private RemoteCallbackList(@FrozenCalleePolicy int frozenCalleePolicy, int maxQueueSize,
+ @CallbackExecutor Executor executor) {
mFrozenCalleePolicy = frozenCalleePolicy;
mMaxQueueSize = maxQueueSize;
+ mExecutor = executor;
}
/**
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 9ab9228..5a53bc15 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -5308,7 +5308,13 @@
Manifest.permission.MANAGE_USERS,
Manifest.permission.CREATE_USERS,
Manifest.permission.QUERY_USERS}, conditional = true)
+ @CachedProperty(api = "user_manager_user_data")
public List<UserInfo> getProfiles(@UserIdInt int userId) {
+ if (android.multiuser.Flags.cacheProfilesReadOnly()) {
+ return UserManagerCache.getProfiles(
+ (Integer userIdentifier) -> mService.getProfiles(userIdentifier, false),
+ userId);
+ }
try {
return mService.getProfiles(userId, false /* enabledOnly */);
} catch (RemoteException re) {
@@ -6484,6 +6490,19 @@
}
/**
+ * This method is used to invalidate caches, when UserManagerService.mUsers
+ * {@link UserManagerService.UserData} is modified, including changes to {@link UserInfo}.
+ * In practice we determine modification by when that data is persisted, or scheduled to be
+ * presisted, to xml.
+ * @hide
+ */
+ public static final void invalidateCacheOnUserDataChanged() {
+ if (android.multiuser.Flags.cacheProfilesReadOnly()) {
+ UserManagerCache.invalidateProfiles();
+ }
+ }
+
+ /**
* Returns a serial number on this device for a given userId. User handles can be recycled
* when deleting and creating users, but serial numbers are not reused until the device is
* wiped.
diff --git a/core/java/android/os/VibrationEffect.java b/core/java/android/os/VibrationEffect.java
index 9d15104..f6bc389 100644
--- a/core/java/android/os/VibrationEffect.java
+++ b/core/java/android/os/VibrationEffect.java
@@ -41,6 +41,7 @@
import android.os.vibrator.RampSegment;
import android.os.vibrator.StepSegment;
import android.os.vibrator.VibrationEffectSegment;
+import android.os.vibrator.VibratorEnvelopeEffectInfo;
import android.os.vibrator.VibratorFrequencyProfileLegacy;
import android.util.MathUtils;
@@ -1825,52 +1826,6 @@
}
/**
- * Start building a waveform vibration.
- *
- * <p>The waveform envelope builder offers more flexibility for creating waveform effects,
- * allowing control over vibration amplitude and frequency via smooth transitions between
- * values. The waveform will start the first transition from the vibrator off state, using
- * the same frequency of the first control point. To provide a different initial vibration
- * frequency, use {@link #startWaveformEnvelope(float)}.
- *
- * <p>Note: To check whether waveform envelope effects are supported, use
- * {@link Vibrator#areEnvelopeEffectsSupported()}.
- *
- * @see VibrationEffect.WaveformEnvelopeBuilder
- */
- @FlaggedApi(Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
- @NonNull
- public static VibrationEffect.WaveformEnvelopeBuilder startWaveformEnvelope() {
- return new WaveformEnvelopeBuilder();
- }
-
- /**
- * Start building a waveform vibration with an initial frequency.
- *
- * <p>The waveform envelope builder offers more flexibility for creating waveform effects,
- * allowing control over vibration amplitude and frequency via smooth transitions between
- * values.
- *
- * <p>This is the same as {@link #startWaveformEnvelope()}, but the waveform will start
- * vibrating at given frequency, in hertz, while it transitions to the new amplitude and
- * frequency of the first control point.
- *
- * <p>Note: To check whether waveform envelope effects are supported, use
- * {@link Vibrator#areEnvelopeEffectsSupported()}.
- *
- * @param initialFrequencyHz The starting frequency of the vibration, in hertz. Must be greater
- * than zero.
- *
- * @see VibrationEffect.WaveformEnvelopeBuilder
- */
- @FlaggedApi(Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
- @NonNull
- public static VibrationEffect.WaveformEnvelopeBuilder startWaveformEnvelope(
- @FloatRange(from = 0) float initialFrequencyHz) {
- return new WaveformEnvelopeBuilder(initialFrequencyHz);
- }
-
- /**
* A builder for waveform effects described by its envelope.
*
* <p>Waveform effect envelopes are defined by one or more control points describing a target
@@ -1881,7 +1836,7 @@
* 100ms, holds that state for 200ms, and then ramps back down over 100ms:
*
* <pre>{@code
- * VibrationEffect effect = VibrationEffect.startWaveformEnvelope()
+ * VibrationEffect effect = new VibrationEffect.WaveformEnvelopeBuilder()
* .addControlPoint(1.0f, 120f, 100)
* .addControlPoint(1.0f, 120f, 200)
* .addControlPoint(0.0f, 120f, 100)
@@ -1908,27 +1863,55 @@
*
* <p>You can use the following APIs to obtain these limits:
* <ul>
- * <li>Maximum envelope control points: {@link Vibrator#getMaxEnvelopeEffectSize()}</li>
+ * <li>Maximum envelope control points: {@link VibratorEnvelopeEffectInfo#getMaxSize()}
* <li>Minimum control point duration:
- * {@link Vibrator#getMinEnvelopeEffectControlPointDurationMillis()}</li>
+ * {@link VibratorEnvelopeEffectInfo#getMinControlPointDurationMillis()}
* <li>Maximum control point duration:
- * {@link Vibrator#getMaxEnvelopeEffectControlPointDurationMillis()}</li>
- * <li>Maximum total effect duration: {@link Vibrator#getMaxEnvelopeEffectDurationMillis()}</li>
+ * {@link VibratorEnvelopeEffectInfo#getMaxControlPointDurationMillis()}
+ * <li>Maximum total effect duration: {@link VibratorEnvelopeEffectInfo#getMaxDurationMillis()}
* </ul>
- *
- * @see VibrationEffect#startWaveformEnvelope()
*/
@FlaggedApi(Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
public static final class WaveformEnvelopeBuilder {
private ArrayList<PwleSegment> mSegments = new ArrayList<>();
private float mLastAmplitude = 0f;
- private float mLastFrequencyHz = 0f;
+ private float mLastFrequencyHz = Float.NaN;
- private WaveformEnvelopeBuilder() {}
+ public WaveformEnvelopeBuilder() {}
- private WaveformEnvelopeBuilder(float initialFrequency) {
- mLastFrequencyHz = initialFrequency;
+ /**
+ * Sets the initial frequency for the waveform in Hertz.
+ *
+ * <p>The effect will start vibrating at this frequency when it transitions to the
+ * amplitude and frequency defined by the first control point.
+ *
+ * <p>The frequency must be greater than zero and within the supported range. To determine
+ * the supported range, use {@link Vibrator#getFrequencyProfile()}. Creating
+ * effects using frequencies outside this range will result in the vibration not playing.
+ *
+ * @param initialFrequencyHz The starting frequency of the vibration, in Hz. Must be
+ * greater than zero.
+ */
+ @FlaggedApi(Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
+ @SuppressWarnings("MissingGetterMatchingBuilder")// No getter to initial frequency once set.
+ @NonNull
+ public WaveformEnvelopeBuilder setInitialFrequencyHz(
+ @FloatRange(from = 0) float initialFrequencyHz) {
+
+ if (mSegments.isEmpty()) {
+ mLastFrequencyHz = initialFrequencyHz;
+ } else {
+ PwleSegment firstSegment = mSegments.getFirst();
+ mSegments.set(0, new PwleSegment(
+ firstSegment.getStartAmplitude(),
+ firstSegment.getEndAmplitude(),
+ initialFrequencyHz, // Update start frequency
+ firstSegment.getEndFrequencyHz(),
+ (int) firstSegment.getDuration()));
+ }
+
+ return this;
}
/**
@@ -1939,15 +1922,13 @@
* perceived intensity. It's determined by the actuator response curve.
*
* <p>Frequency must be greater than zero and within the supported range. To determine
- * the supported range, use {@link Vibrator#getFrequencyProfile()}. This method returns a
- * {@link android.os.vibrator.VibratorFrequencyProfile} object, which contains the
- * minimum and maximum frequencies, among other frequency-related information. Creating
+ * the supported range, use {@link Vibrator#getFrequencyProfile()}. Creating
* effects using frequencies outside this range will result in the vibration not playing.
*
* <p>Time specifies the duration (in milliseconds) for the vibrator to smoothly transition
* from the previous control point to this new one. It must be greater than zero. To
* transition as quickly as possible, use
- * {@link Vibrator#getMinEnvelopeEffectControlPointDurationMillis()}.
+ * {@link VibratorEnvelopeEffectInfo#getMinControlPointDurationMillis()}.
*
* @param amplitude The amplitude value between 0 and 1, inclusive. 0 represents the
* vibrator being off, and 1 represents the maximum achievable amplitude
@@ -1962,7 +1943,7 @@
@FloatRange(from = 0, to = 1) float amplitude,
@FloatRange(from = 0) float frequencyHz, int timeMillis) {
- if (mLastFrequencyHz == 0) {
+ if (Float.isNaN(mLastFrequencyHz)) {
mLastFrequencyHz = frequencyHz;
}
@@ -1983,6 +1964,7 @@
* calling this method again.
*
* @return The {@link VibrationEffect} resulting from the list of control points.
+ * @throws IllegalStateException if no control points were added to the builder.
*/
@FlaggedApi(Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
@NonNull
diff --git a/core/java/android/os/Vibrator.java b/core/java/android/os/Vibrator.java
index 53f8a92..8620914 100644
--- a/core/java/android/os/Vibrator.java
+++ b/core/java/android/os/Vibrator.java
@@ -35,6 +35,7 @@
import android.os.vibrator.Flags;
import android.os.vibrator.VendorVibrationSession;
import android.os.vibrator.VibrationConfig;
+import android.os.vibrator.VibratorEnvelopeEffectInfo;
import android.os.vibrator.VibratorFrequencyProfile;
import android.os.vibrator.VibratorFrequencyProfileLegacy;
import android.util.Log;
@@ -137,6 +138,9 @@
@Nullable
private volatile VibrationConfig mVibrationConfig;
+ private VibratorFrequencyProfile mVibratorFrequencyProfile;
+ private VibratorEnvelopeEffectInfo mVibratorEnvelopeEffectInfo;
+
/**
* @hide to prevent subclassing from outside of the framework
*/
@@ -351,7 +355,11 @@
return null;
}
- return new VibratorFrequencyProfile(frequencyProfile);
+ if (mVibratorFrequencyProfile == null) {
+ mVibratorFrequencyProfile = new VibratorFrequencyProfile(frequencyProfile);
+ }
+
+ return mVibratorFrequencyProfile;
}
/**
@@ -383,70 +391,28 @@
}
/**
- * Retrieves the maximum duration supported for an envelope effect, in milliseconds.
+ * Retrieves the vibrator's capabilities and limitations for envelope effects.
*
- * <p>If the device supports envelope effects (check {@link #areEnvelopeEffectsSupported}),
- * this value will be positive. Devices with envelope effects capabilities guarantees a
- * maximum duration equivalent to the product of {@link #getMaxEnvelopeEffectSize()} and
- * {@link #getMaxEnvelopeEffectControlPointDurationMillis()}. If the device does not support
- * envelope effects, this method will return 0.
+ * <p>These parameters can be used with {@link VibrationEffect.WaveformEnvelopeBuilder}
+ * to create custom envelope effects.
*
- * @return The maximum duration (in milliseconds) allowed for an envelope effect, or 0 if
- * envelope effects are not supported.
+ * @return The vibrator's envelope effect information, or null if not supported. If this
+ * vibrator is a composite of multiple physical devices then this will return a profile
+ * supported in all devices, or null if the intersection is empty or not available.
+ *
+ * @see VibrationEffect.WaveformEnvelopeBuilder
*/
@FlaggedApi(Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
- public int getMaxEnvelopeEffectDurationMillis() {
- return getInfo().getMaxEnvelopeEffectDurationMillis();
- }
+ @NonNull
+ public VibratorEnvelopeEffectInfo getEnvelopeEffectInfo() {
+ if (mVibratorEnvelopeEffectInfo == null) {
+ mVibratorEnvelopeEffectInfo = new VibratorEnvelopeEffectInfo(
+ getInfo().getMaxEnvelopeEffectSize(),
+ getInfo().getMinEnvelopeEffectControlPointDurationMillis(),
+ getInfo().getMaxEnvelopeEffectControlPointDurationMillis());
+ }
- /**
- * Retrieves the maximum number of control points supported for an envelope effect.
- *
- * <p>If the device supports envelope effects (check {@link #areEnvelopeEffectsSupported}),
- * this value will be positive. Devices with envelope effects capabilities guarantee support
- * for a minimum of 16 control points. If the device does not support envelope effects,
- * this method will return 0.
- *
- * @return the maximum number of control points allowed for an envelope effect, or 0 if
- * envelope effects are not supported.
- */
- @FlaggedApi(Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
- public int getMaxEnvelopeEffectSize() {
- return getInfo().getMaxEnvelopeEffectSize();
- }
-
- /**
- * Retrieves the minimum duration supported between two control points within an envelope
- * effect, in milliseconds.
- *
- * <p>If the device supports envelope effects (check {@link #areEnvelopeEffectsSupported}),
- * this value will be positive. Devices with envelope effects capabilities guarantee
- * support for durations down to at least 20 milliseconds. If the device does
- * not support envelope effects, this method will return 0.
- *
- * @return the minimum allowed duration between two control points in an envelope effect,
- * or 0 if envelope effects are not supported.
- */
- @FlaggedApi(Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
- public int getMinEnvelopeEffectControlPointDurationMillis() {
- return getInfo().getMinEnvelopeEffectControlPointDurationMillis();
- }
-
- /**
- * Retrieves the maximum duration supported between two control points within an envelope
- * effect, in milliseconds.
- *
- * <p>If the device supports envelope effects (check {@link #areEnvelopeEffectsSupported}),
- * this value will be positive. Devices with envelope effects capabilities guarantee support
- * for durations up to at least 1 second. If the device does not support envelope effects,
- * this method will return 0.
- *
- * @return the maximum allowed duration between two control points in an envelope effect,
- * or 0 if envelope effects are not supported.
- */
- @FlaggedApi(Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
- public int getMaxEnvelopeEffectControlPointDurationMillis() {
- return getInfo().getMaxEnvelopeEffectControlPointDurationMillis();
+ return mVibratorEnvelopeEffectInfo;
}
/**
diff --git a/core/java/android/os/VibratorInfo.java b/core/java/android/os/VibratorInfo.java
index 9dec867..84325a4 100644
--- a/core/java/android/os/VibratorInfo.java
+++ b/core/java/android/os/VibratorInfo.java
@@ -121,7 +121,7 @@
* @param qFactor The vibrator quality factor.
* @param frequencyProfileLegacy The description of the vibrator supported frequencies and max
* amplitude mappings.
- * @param frequencyProfile The description of the vibrator supported frequencies and
+ * @param frequencyProfile The description of the vibrator supported frequencies and
* output acceleration mappings.
* @param maxEnvelopeEffectSize The maximum number of control points supported for an
* envelope effect.
diff --git a/core/java/android/os/flags.aconfig b/core/java/android/os/flags.aconfig
index d9db28e..2ef8764 100644
--- a/core/java/android/os/flags.aconfig
+++ b/core/java/android/os/flags.aconfig
@@ -66,6 +66,14 @@
}
flag {
+ name: "adpf_use_load_hints"
+ namespace: "game"
+ description: "Guards use of the ADPF public load hints behind a readonly flag"
+ is_fixed_read_only: true
+ bug: "367803904"
+}
+
+flag {
name: "allow_consentless_bugreport_delegated_consent"
namespace: "crumpet"
description: "Allow privileged apps to call bugreport generation without enforcing user consent and delegate it to the calling app instead"
@@ -148,6 +156,13 @@
}
flag {
+ name: "cpu_gpu_headrooms"
+ namespace: "game"
+ description: "Feature flag for adding CPU/GPU headroom API"
+ bug: "346604998"
+}
+
+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."
@@ -179,6 +194,17 @@
}
flag {
+ name: "material_colors_10_2024"
+ namespace: "systemui"
+ description: "Adding new Material Tokens as of October 2024"
+ bug: "376195115"
+ is_exported: true
+ metadata {
+ purpose: PURPOSE_FEATURE
+ }
+}
+
+flag {
name: "message_queue_tail_tracking"
namespace: "system_performance"
description: "track tail of message queue."
@@ -253,6 +279,15 @@
flag {
namespace: "system_performance"
+ name: "enable_has_binders"
+ is_exported: true
+ description: "Add hasBinders to Public API under a flag."
+ is_fixed_read_only: true
+ bug: "330345513"
+}
+
+flag {
+ namespace: "system_performance"
name: "perfetto_sdk_tracing"
description: "Tracing using Perfetto SDK."
bug: "303199244"
diff --git a/core/java/android/os/health/SystemHealthManager.java b/core/java/android/os/health/SystemHealthManager.java
index deabfed..4db9bc3 100644
--- a/core/java/android/os/health/SystemHealthManager.java
+++ b/core/java/android/os/health/SystemHealthManager.java
@@ -17,6 +17,7 @@
package android.os.health;
import android.annotation.FlaggedApi;
+import android.annotation.FloatRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemService;
@@ -25,6 +26,11 @@
import android.os.BatteryStats;
import android.os.Build;
import android.os.Bundle;
+import android.os.CpuHeadroomParams;
+import android.os.CpuHeadroomParamsInternal;
+import android.os.GpuHeadroomParams;
+import android.os.GpuHeadroomParamsInternal;
+import android.os.IHintManager;
import android.os.IPowerStatsService;
import android.os.OutcomeReceiver;
import android.os.PowerMonitor;
@@ -68,6 +74,8 @@
private final IBatteryStats mBatteryStats;
@Nullable
private final IPowerStatsService mPowerStats;
+ @Nullable
+ private final IHintManager mHintManager;
private List<PowerMonitor> mPowerMonitorsInfo;
private final Object mPowerMonitorsLock = new Object();
private static final long TAKE_UID_SNAPSHOT_TIMEOUT_MILLIS = 10_000;
@@ -88,14 +96,111 @@
public SystemHealthManager() {
this(IBatteryStats.Stub.asInterface(ServiceManager.getService(BatteryStats.SERVICE_NAME)),
IPowerStatsService.Stub.asInterface(
- ServiceManager.getService(Context.POWER_STATS_SERVICE)));
+ ServiceManager.getService(Context.POWER_STATS_SERVICE)),
+ IHintManager.Stub.asInterface(
+ ServiceManager.getService(Context.PERFORMANCE_HINT_SERVICE)));
}
/** {@hide} */
public SystemHealthManager(@NonNull IBatteryStats batteryStats,
- @Nullable IPowerStatsService powerStats) {
+ @Nullable IPowerStatsService powerStats, @Nullable IHintManager hintManager) {
mBatteryStats = batteryStats;
mPowerStats = powerStats;
+ mHintManager = hintManager;
+ }
+
+ /**
+ * Provides an estimate of global available CPU headroom of the calling thread.
+ * <p>
+ *
+ * @param params params to customize the CPU headroom calculation, null to use default params.
+ * @return a single value a {@code Float.NaN} if it's temporarily unavailable.
+ * A valid value is ranged from [0, 100], where 0 indicates no more CPU resources can be
+ * granted.
+ * @throws UnsupportedOperationException if the API is unsupported or the request params can't
+ * be served.
+ */
+ @FlaggedApi(android.os.Flags.FLAG_CPU_GPU_HEADROOMS)
+ public @FloatRange(from = 0f, to = 100f) float getCpuHeadroom(
+ @Nullable CpuHeadroomParams params) {
+ if (mHintManager == null) {
+ throw new UnsupportedOperationException();
+ }
+ try {
+ return mHintManager.getCpuHeadroom(
+ params != null ? params.getInternal() : new CpuHeadroomParamsInternal())[0];
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+
+
+ /**
+ * Provides an estimate of global available GPU headroom of the device.
+ * <p>
+ *
+ * @param params params to customize the GPU headroom calculation, null to use default params.
+ * @return a single value headroom or a {@code Float.NaN} if it's temporarily unavailable.
+ * A valid value is ranged from [0, 100], where 0 indicates no more GPU resources can be
+ * granted.
+ * @throws UnsupportedOperationException if the API is unsupported or the request params can't
+ * be served.
+ */
+ @FlaggedApi(android.os.Flags.FLAG_CPU_GPU_HEADROOMS)
+ public @FloatRange(from = 0f, to = 100f) float getGpuHeadroom(
+ @Nullable GpuHeadroomParams params) {
+ if (mHintManager == null) {
+ throw new UnsupportedOperationException();
+ }
+ try {
+ return mHintManager.getGpuHeadroom(
+ params != null ? params.getInternal() : new GpuHeadroomParamsInternal());
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Minimum polling interval for calling {@link #getCpuHeadroom(CpuHeadroomParams)} in
+ * milliseconds.
+ * <p>
+ * The {@link #getCpuHeadroom(CpuHeadroomParams)} API may return cached result if called more
+ * frequent than the interval.
+ *
+ * @throws UnsupportedOperationException if the API is unsupported.
+ */
+ @FlaggedApi(android.os.Flags.FLAG_CPU_GPU_HEADROOMS)
+ public long getCpuHeadroomMinIntervalMillis() {
+ if (mHintManager == null) {
+ throw new UnsupportedOperationException();
+ }
+ try {
+ return mHintManager.getCpuHeadroomMinIntervalMillis();
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Minimum polling interval for calling {@link #getGpuHeadroom(GpuHeadroomParams)} in
+ * milliseconds.
+ * <p>
+ * The {@link #getGpuHeadroom(GpuHeadroomParams)} API may return cached result if called more
+ * frequent than the interval.
+ *
+ * @throws UnsupportedOperationException if the API is unsupported.
+ */
+ @FlaggedApi(android.os.Flags.FLAG_CPU_GPU_HEADROOMS)
+ public long getGpuHeadroomMinIntervalMillis() {
+ if (mHintManager == null) {
+ throw new UnsupportedOperationException();
+ }
+ try {
+ return mHintManager.getGpuHeadroomMinIntervalMillis();
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
}
/**
@@ -261,7 +366,7 @@
mPowerMonitorsInfo = result;
}
if (executor != null) {
- executor.execute(()-> onResult.accept(result));
+ executor.execute(() -> onResult.accept(result));
} else {
onResult.accept(result);
}
diff --git a/core/java/android/os/vibrator/VibratorEnvelopeEffectInfo.java b/core/java/android/os/vibrator/VibratorEnvelopeEffectInfo.java
new file mode 100644
index 0000000..afaab55
--- /dev/null
+++ b/core/java/android/os/vibrator/VibratorEnvelopeEffectInfo.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os.vibrator;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.VibrationEffect;
+
+import java.util.Objects;
+
+/**
+ * Provides information about the vibrator hardware capabilities and limitations regarding
+ * waveform envelope effects. This includes:
+ * <ul>
+ * <li>Maximum number of control points supported.
+ * <li>Minimum and maximum duration for individual segments.
+ * <li>Maximum total duration for an envelope effect.
+ * </ul>
+ *
+ * <p>This information can be used to help construct waveform envelope effects with
+ * {@link VibrationEffect.WaveformEnvelopeBuilder}. When designing these effects, it is also
+ * recommended to check the {@link VibratorFrequencyProfile} for information about the supported
+ * frequency range and the vibrator's output response.
+ *
+ * @see VibrationEffect.WaveformEnvelopeBuilder
+ * @see VibratorFrequencyProfile
+ */
+@FlaggedApi(Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
+public final class VibratorEnvelopeEffectInfo implements Parcelable {
+ private final int mMaxSize;
+ private final long mMinControlPointDurationMillis;
+ private final long mMaxControlPointDurationMillis;
+
+ VibratorEnvelopeEffectInfo(Parcel in) {
+ mMaxSize = in.readInt();
+ mMinControlPointDurationMillis = in.readLong();
+ mMaxControlPointDurationMillis = in.readLong();
+ }
+
+ /**
+ * Default constructor.
+ *
+ * @param maxSize The maximum number of control points supported for an
+ * envelope effect.
+ * @param minControlPointDurationMillis The minimum duration supported between two control
+ * points within an envelope effect.
+ * @param maxControlPointDurationMillis The maximum duration supported between two control
+ * points within an envelope effect.
+ * @hide
+ */
+ public VibratorEnvelopeEffectInfo(int maxSize,
+ long minControlPointDurationMillis,
+ long maxControlPointDurationMillis) {
+ mMaxSize = maxSize;
+ mMinControlPointDurationMillis = minControlPointDurationMillis;
+ mMaxControlPointDurationMillis = maxControlPointDurationMillis;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(mMaxSize);
+ dest.writeLong(mMinControlPointDurationMillis);
+ dest.writeLong(mMaxControlPointDurationMillis);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof VibratorEnvelopeEffectInfo)) {
+ return false;
+ }
+ VibratorEnvelopeEffectInfo other = (VibratorEnvelopeEffectInfo) o;
+ return mMaxSize == other.mMaxSize
+ && mMinControlPointDurationMillis == other.mMinControlPointDurationMillis
+ && mMaxControlPointDurationMillis == other.mMaxControlPointDurationMillis;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mMaxSize,
+ mMinControlPointDurationMillis,
+ mMaxControlPointDurationMillis);
+ }
+
+ @Override
+ public String toString() {
+ return "VibratorEnvelopeEffectInfo{"
+ + ", mMaxSize=" + mMaxSize
+ + ", mMinControlPointDurationMillis=" + mMinControlPointDurationMillis
+ + ", mMaxControlPointDurationMillis=" + mMaxControlPointDurationMillis
+ + '}';
+ }
+
+ @NonNull
+ public static final Creator<VibratorEnvelopeEffectInfo> CREATOR =
+ new Creator<VibratorEnvelopeEffectInfo>() {
+ @Override
+ public VibratorEnvelopeEffectInfo createFromParcel(Parcel in) {
+ return new VibratorEnvelopeEffectInfo(in);
+ }
+
+ @Override
+ public VibratorEnvelopeEffectInfo[] newArray(int size) {
+ return new VibratorEnvelopeEffectInfo[size];
+ }
+ };
+
+ /**
+ * Retrieves the maximum duration supported for an envelope effect, in milliseconds.
+ *
+ * <p>If the device supports envelope effects
+ * (check {@link android.os.VibratorInfo#areEnvelopeEffectsSupported}), this value will be
+ * positive. Devices with envelope effects capabilities guarantees a maximum duration
+ * equivalent to the product of {@link #getMaxSize()} and
+ * {@link #getMaxControlPointDurationMillis()}. If the device does not support
+ * envelope effects, this method will return 0.
+ *
+ * @return The maximum duration (in milliseconds) allowed for an envelope effect, or 0 if
+ * envelope effects are not supported.
+ */
+ public long getMaxDurationMillis() {
+ return mMaxSize * mMaxControlPointDurationMillis;
+ }
+
+ /**
+ * Retrieves the maximum number of control points supported for an envelope effect.
+ *
+ * <p>If the device supports envelope effects
+ * (check {@link android.os.VibratorInfo#areEnvelopeEffectsSupported}), this value will be
+ * positive. Devices with envelope effects capabilities guarantee support for a minimum of
+ * 16 control points. If the device does not support envelope effects, this method will
+ * return 0.
+ *
+ * @return the maximum number of control points allowed for an envelope effect, or 0 if
+ * envelope effects are not supported.
+ */
+ public int getMaxSize() {
+ return mMaxSize;
+ }
+
+ /**
+ * Retrieves the minimum duration supported between two control points within an envelope
+ * effect, in milliseconds.
+ *
+ * <p>If the device supports envelope effects
+ * (check {@link android.os.VibratorInfo#areEnvelopeEffectsSupported}), this value will be
+ * positive. Devices with envelope effects capabilities guarantee support for durations down
+ * to at least 20 milliseconds. If the device does not support envelope effects,
+ * this method will return 0.
+ *
+ * @return the minimum allowed duration between two control points in an envelope effect,
+ * or 0 if envelope effects are not supported.
+ */
+ public long getMinControlPointDurationMillis() {
+ return mMinControlPointDurationMillis;
+ }
+
+ /**
+ * Retrieves the maximum duration supported between two control points within an envelope
+ * effect, in milliseconds.
+ *
+ * <p>If the device supports envelope effects
+ * (check {@link android.os.VibratorInfo#areEnvelopeEffectsSupported}), this value will be
+ * positive. Devices with envelope effects capabilities guarantee support for durations up to
+ * at least 1 second. If the device does not support envelope effects, this method
+ * will return 0.
+ *
+ * @return the maximum allowed duration between two control points in an envelope effect,
+ * or 0 if envelope effects are not supported.
+ */
+ public long getMaxControlPointDurationMillis() {
+ return mMaxControlPointDurationMillis;
+ }
+}
diff --git a/core/java/android/permission/flags.aconfig b/core/java/android/permission/flags.aconfig
index 6264fbb..1093503 100644
--- a/core/java/android/permission/flags.aconfig
+++ b/core/java/android/permission/flags.aconfig
@@ -383,7 +383,7 @@
bug: "363318732"
}
-flag{
+flag {
name: "note_op_batching_enabled"
is_fixed_read_only: true
is_exported: true
@@ -399,4 +399,31 @@
namespace: "supervision"
description: "This flag is used to enable all the remaining permissions required to the supervision role"
bug: "367333883"
-}
\ No newline at end of file
+}
+
+flag {
+ name: "permission_request_short_circuit_enabled"
+ is_fixed_read_only: true
+ is_exported: true
+ namespace: "permissions"
+ description: "This flag is used to short circuit the request for permananently denied permissions"
+ bug: "378923900"
+}
+
+flag {
+ name: "check_op_overload_api_enabled"
+ is_exported: true
+ is_fixed_read_only: true
+ namespace: "permissions"
+ description: "Add new checkOp APIs that accept attributionTag"
+ bug: "240617242"
+}
+
+flag {
+ name: "device_policy_management_role_split_create_managed_profile_enabled"
+ is_fixed_read_only: true
+ is_exported: true
+ namespace: "enterprise"
+ description: "Gives the device policy management role the ability to create a managed profile using new APIs"
+ bug: "375382324"
+}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index d2a20b6..19b0c6f 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -6395,27 +6395,6 @@
public static final String SCREEN_FLASH_NOTIFICATION_COLOR =
"screen_flash_notification_color_global";
-
- /**
- * A semi-colon separated list of Bluetooth hearing devices' local ambient volume.
- * Each entry is encoded as a key=value list, separated by commas. Ex:
- *
- * "addr=XX:XX:XX:00:11,ambient=20,group_ambient=30;addr=XX:XX:XX:00:22,ambient=50"
- *
- * The following keys are supported:
- * <pre>
- * addr (String)
- * ambient (int)
- * group_ambient (int)
- * control_expanded (boolean)
- * </pre>
- *
- * Each entry must contains "addr" attribute, otherwise it'll be ignored.
- * @hide
- */
- public static final String HEARING_DEVICE_LOCAL_AMBIENT_VOLUME =
- "hearing_device_local_ambient_volume";
-
/**
* IMPORTANT: If you add a new public settings you also have to add it to
* PUBLIC_SETTINGS below. If the new setting is hidden you have to add
@@ -6560,7 +6539,6 @@
PRIVATE_SETTINGS.add(DEFAULT_DEVICE_FONT_SCALE);
PRIVATE_SETTINGS.add(MOUSE_REVERSE_VERTICAL_SCROLLING);
PRIVATE_SETTINGS.add(MOUSE_SWAP_PRIMARY_BUTTON);
- PRIVATE_SETTINGS.add(HEARING_DEVICE_LOCAL_AMBIENT_VOLUME);
}
/**
@@ -18138,6 +18116,44 @@
public static final String ONE_HANDED_KEYGUARD_SIDE = "one_handed_keyguard_side";
/**
+ * A semi-colon separated list of Bluetooth hearing devices' local ambient volume data.
+ * Each entry is encoded as a key=value list, separated by commas. Ex:
+ *
+ * "addr=XX:XX:XX:00:11,ambient=20,group_ambient=30;addr=XX:XX:XX:00:22,ambient=50"
+ *
+ * The following keys are supported:
+ * <pre>
+ * addr (String)
+ * ambient (int)
+ * group_ambient (int)
+ * control_expanded (boolean)
+ * </pre>
+ *
+ * Each entry must contains "addr" attribute, otherwise it'll be ignored.
+ * @hide
+ */
+ public static final String HEARING_DEVICE_LOCAL_AMBIENT_VOLUME =
+ "hearing_device_local_ambient_volume";
+
+ /**
+ * A semi-colon separated list of Bluetooth hearing devices' notification data.
+ * Each entry is encoded as a key=value list, separated by commas. Ex:
+ *
+ * "addr=XX:XX:XX:00:11,input_changes=1"
+ *
+ * The following keys are supported:
+ * <pre>
+ * addr (String)
+ * input_changes (boolean)
+ * </pre>
+ *
+ * Each entry must contains "addr" attribute, otherwise it'll be ignored.
+ * @hide
+ */
+ public static final String HEARING_DEVICE_LOCAL_NOTIFICATION =
+ "hearing_device_local_notification";
+
+ /**
* Global settings that shouldn't be persisted.
*
* @hide
@@ -20438,6 +20454,29 @@
* @hide
*/
public static final String AUTO_BEDTIME_MODE = "auto_bedtime_mode";
+
+ /**
+ * Indicates that all elements of the system status tray on wear should be rendered
+ * by default wear system.
+ *
+ * @hide
+ */
+ public static final int STATUS_TRAY_CONFIGURATION_DEFAULT = 0;
+
+ /**
+ * Indicates that all elements of the system status tray on wear should be hidden.
+ *
+ * @hide
+ */
+ public static final int STATUS_TRAY_CONFIGURATION_SYSTEM_HIDDEN = 1;
+
+ /**
+ * Configuration of system status tray in wear.
+ *
+ * @hide
+ */
+ public static final String WEAR_SYSTEM_STATUS_TRAY_CONFIGURATION =
+ "wear_system_status_tray_configuration";
}
}
diff --git a/core/java/android/security/authenticationpolicy/AuthenticationPolicyManager.java b/core/java/android/security/authenticationpolicy/AuthenticationPolicyManager.java
new file mode 100644
index 0000000..75abd5f
--- /dev/null
+++ b/core/java/android/security/authenticationpolicy/AuthenticationPolicyManager.java
@@ -0,0 +1,237 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.security.authenticationpolicy;
+
+import static android.Manifest.permission.MANAGE_SECURE_LOCK_DEVICE;
+import static android.security.Flags.FLAG_SECURE_LOCKDOWN;
+
+import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.annotation.SystemService;
+import android.content.Context;
+import android.os.RemoteException;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * AuthenticationPolicyManager is a centralized interface for managing authentication related
+ * policies on the device. This includes device locking capabilities to protect users in "at risk"
+ * environments.
+ *
+ * AuthenticationPolicyManager is designed to protect Android users by integrating with apps and
+ * key system components, such as the lock screen. It is not related to enterprise control surfaces
+ * and does not offer additional administrative controls.
+ *
+ * <p>
+ * To use this class, call {@link #enableSecureLockDevice} to enable secure lock on the device.
+ * This will require the caller to have the
+ * {@link android.Manifest.permission#MANAGE_SECURE_LOCK_DEVICE} permission.
+ *
+ * <p>
+ * To disable secure lock on the device, call {@link #disableSecureLockDevice}. This will require
+ * the caller to have the {@link android.Manifest.permission#MANAGE_SECURE_LOCK_DEVICE} permission.
+ *
+ * @hide
+ */
+@SystemApi
+@FlaggedApi(FLAG_SECURE_LOCKDOWN)
+@SystemService(Context.AUTHENTICATION_POLICY_SERVICE)
+public final class AuthenticationPolicyManager {
+ private static final String TAG = "AuthenticationPolicyManager";
+
+ @NonNull private final IAuthenticationPolicyService mAuthenticationPolicyService;
+ @NonNull private final Context mContext;
+
+ /**
+ * Error result code for {@link #enableSecureLockDevice} and {@link
+ * #disableSecureLockDevice}.
+ *
+ * Secure lock device request status unknown.
+ *
+ * @hide
+ */
+ @SystemApi
+ @FlaggedApi(FLAG_SECURE_LOCKDOWN)
+ public static final int ERROR_UNKNOWN = 0;
+
+ /**
+ * Success result code for {@link #enableSecureLockDevice} and {@link #disableSecureLockDevice}.
+ *
+ * Secure lock device request successful.
+ *
+ * @hide
+ */
+ @SystemApi
+ @FlaggedApi(FLAG_SECURE_LOCKDOWN)
+ public static final int SUCCESS = 1;
+
+ /**
+ * Error result code for {@link #enableSecureLockDevice} and {@link #disableSecureLockDevice}.
+ *
+ * Secure lock device is unsupported.
+ *
+ * @hide
+ */
+ @SystemApi
+ @FlaggedApi(FLAG_SECURE_LOCKDOWN)
+ public static final int ERROR_UNSUPPORTED = 2;
+
+
+ /**
+ * Error result code for {@link #enableSecureLockDevice} and {@link #disableSecureLockDevice}.
+ *
+ * Invalid secure lock device request params provided.
+ *
+ * @hide
+ */
+ @SystemApi
+ @FlaggedApi(FLAG_SECURE_LOCKDOWN)
+ public static final int ERROR_INVALID_PARAMS = 3;
+
+
+ /**
+ * Error result code for {@link #enableSecureLockDevice} and {@link #disableSecureLockDevice}.
+ *
+ * Secure lock device is unavailable because there are no biometrics enrolled on the device.
+ *
+ * @hide
+ */
+ @SystemApi
+ @FlaggedApi(FLAG_SECURE_LOCKDOWN)
+ public static final int ERROR_NO_BIOMETRICS_ENROLLED = 4;
+
+ /**
+ * Error result code for {@link #enableSecureLockDevice} and {@link #disableSecureLockDevice}.
+ *
+ * Secure lock device is unavailable because the device has no biometric hardware or the
+ * biometric sensors do not meet
+ * {@link android.hardware.biometrics.BiometricManager.Authenticators#BIOMETRIC_STRONG}
+ *
+ * @hide
+ */
+ @SystemApi
+ @FlaggedApi(FLAG_SECURE_LOCKDOWN)
+ public static final int ERROR_INSUFFICIENT_BIOMETRICS = 5;
+
+ /**
+ * Error result code for {@link #enableSecureLockDevice}.
+ *
+ * Secure lock is already enabled.
+ *
+ * @hide
+ */
+ @SystemApi
+ @FlaggedApi(FLAG_SECURE_LOCKDOWN)
+ public static final int ERROR_ALREADY_ENABLED = 6;
+
+ /**
+ * Communicates the current status of a request to enable secure lock on the device.
+ *
+ * @hide
+ */
+ @IntDef(prefix = {"ENABLE_SECURE_LOCK_DEVICE_STATUS_"}, value = {
+ ERROR_UNKNOWN,
+ SUCCESS,
+ ERROR_UNSUPPORTED,
+ ERROR_INVALID_PARAMS,
+ ERROR_NO_BIOMETRICS_ENROLLED,
+ ERROR_INSUFFICIENT_BIOMETRICS,
+ ERROR_ALREADY_ENABLED
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface EnableSecureLockDeviceRequestStatus {}
+
+ /**
+ * Communicates the current status of a request to disable secure lock on the device.
+ *
+ * @hide
+ */
+ @IntDef(prefix = {"DISABLE_SECURE_LOCK_DEVICE_STATUS_"}, value = {
+ ERROR_UNKNOWN,
+ SUCCESS,
+ ERROR_UNSUPPORTED,
+ ERROR_INVALID_PARAMS,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface DisableSecureLockDeviceRequestStatus {}
+
+ /** @hide */
+ public AuthenticationPolicyManager(@NonNull Context context,
+ @NonNull IAuthenticationPolicyService authenticationPolicyService) {
+ mContext = context;
+ mAuthenticationPolicyService = authenticationPolicyService;
+ }
+
+ /**
+ * Called by a privileged component to remotely enable secure lock on the device.
+ *
+ * Secure lock is an enhanced security state that restricts access to sensitive data (app
+ * notifications, widgets, quick settings, assistant, etc) and requires multi-factor
+ * authentication for device entry, such as
+ * {@link android.hardware.biometrics.BiometricManager.Authenticators#DEVICE_CREDENTIAL} and
+ * {@link android.hardware.biometrics.BiometricManager.Authenticators#BIOMETRIC_STRONG}.
+ *
+ * If secure lock is already enabled when this method is called, it will return
+ * {@link ERROR_ALREADY_ENABLED}.
+ *
+ * @param params EnableSecureLockDeviceParams for caller to supply params related to the secure
+ * lock device request
+ * @return @EnableSecureLockDeviceRequestStatus int indicating the result of the secure lock
+ * device request
+ *
+ * @hide
+ */
+ @EnableSecureLockDeviceRequestStatus
+ @RequiresPermission(MANAGE_SECURE_LOCK_DEVICE)
+ @SystemApi
+ @FlaggedApi(FLAG_SECURE_LOCKDOWN)
+ public int enableSecureLockDevice(@NonNull EnableSecureLockDeviceParams params) {
+ try {
+ return mAuthenticationPolicyService.enableSecureLockDevice(params);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Called by a privileged component to disable secure lock on the device.
+ *
+ * If secure lock is already disabled when this method is called, it will return
+ * {@link SUCCESS}.
+ *
+ * @param params @DisableSecureLockDeviceParams for caller to supply params related to the
+ * secure lock device request
+ * @return @DisableSecureLockDeviceRequestStatus int indicating the result of the secure lock
+ * device request
+ *
+ * @hide
+ */
+ @DisableSecureLockDeviceRequestStatus
+ @RequiresPermission(MANAGE_SECURE_LOCK_DEVICE)
+ @SystemApi
+ @FlaggedApi(FLAG_SECURE_LOCKDOWN)
+ public int disableSecureLockDevice(@NonNull DisableSecureLockDeviceParams params) {
+ try {
+ return mAuthenticationPolicyService.disableSecureLockDevice(params);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+}
diff --git a/core/java/android/text/ClientFlags.java b/core/java/android/security/authenticationpolicy/DisableSecureLockDeviceParams.aidl
similarity index 61%
copy from core/java/android/text/ClientFlags.java
copy to core/java/android/security/authenticationpolicy/DisableSecureLockDeviceParams.aidl
index ca88764..81f7726 100644
--- a/core/java/android/text/ClientFlags.java
+++ b/core/java/android/security/authenticationpolicy/DisableSecureLockDeviceParams.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,17 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
-package android.text;
+package android.security.authenticationpolicy;
/**
- * An aconfig feature flags that can be accessible from application process without
- * ContentProvider IPCs.
- *
- * When you add new flags, you have to add flag string to {@link TextFlags#TEXT_ACONFIGS_FLAGS}.
- *
- * TODO(nona): Remove this class.
* @hide
*/
-public class ClientFlags {
-}
+parcelable DisableSecureLockDeviceParams;
\ No newline at end of file
diff --git a/core/java/android/security/authenticationpolicy/DisableSecureLockDeviceParams.java b/core/java/android/security/authenticationpolicy/DisableSecureLockDeviceParams.java
new file mode 100644
index 0000000..64a3f0f
--- /dev/null
+++ b/core/java/android/security/authenticationpolicy/DisableSecureLockDeviceParams.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.authenticationpolicy;
+
+import static android.security.Flags.FLAG_SECURE_LOCKDOWN;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * Parameters related to a request to disable secure lock on the device.
+ *
+ * @hide
+ */
+@SystemApi
+@FlaggedApi(FLAG_SECURE_LOCKDOWN)
+public final class DisableSecureLockDeviceParams implements Parcelable {
+
+ /**
+ * Client message associated with the request to disable secure lock on the device. This message
+ * will be shown on the device when secure lock mode is disabled.
+ */
+ private final @NonNull String mMessage;
+
+ /**
+ * Creates DisableSecureLockDeviceParams with the given params.
+ *
+ * @param message Allows clients to pass in a message with information about the request to
+ * disable secure lock on the device. This message will be shown to the user when
+ * secure lock mode is disabled. If an empty string is provided, it will default
+ * to a system-defined string (e.g. "Secure lock mode has been disabled.")
+ */
+ public DisableSecureLockDeviceParams(@NonNull String message) {
+ mMessage = message;
+ }
+
+ private DisableSecureLockDeviceParams(@NonNull Parcel in) {
+ mMessage = Objects.requireNonNull(in.readString8());
+ }
+
+ public static final @NonNull Creator<DisableSecureLockDeviceParams> CREATOR =
+ new Creator<DisableSecureLockDeviceParams>() {
+ @Override
+ public DisableSecureLockDeviceParams createFromParcel(Parcel in) {
+ return new DisableSecureLockDeviceParams(in);
+ }
+
+ @Override
+ public DisableSecureLockDeviceParams[] newArray(int size) {
+ return new DisableSecureLockDeviceParams[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeString8(mMessage);
+ }
+}
diff --git a/core/java/android/text/ClientFlags.java b/core/java/android/security/authenticationpolicy/EnableSecureLockDeviceParams.aidl
similarity index 61%
copy from core/java/android/text/ClientFlags.java
copy to core/java/android/security/authenticationpolicy/EnableSecureLockDeviceParams.aidl
index ca88764..9e496f8 100644
--- a/core/java/android/text/ClientFlags.java
+++ b/core/java/android/security/authenticationpolicy/EnableSecureLockDeviceParams.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,17 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
-package android.text;
+package android.security.authenticationpolicy;
/**
- * An aconfig feature flags that can be accessible from application process without
- * ContentProvider IPCs.
- *
- * When you add new flags, you have to add flag string to {@link TextFlags#TEXT_ACONFIGS_FLAGS}.
- *
- * TODO(nona): Remove this class.
* @hide
*/
-public class ClientFlags {
-}
+parcelable EnableSecureLockDeviceParams;
\ No newline at end of file
diff --git a/core/java/android/security/authenticationpolicy/EnableSecureLockDeviceParams.java b/core/java/android/security/authenticationpolicy/EnableSecureLockDeviceParams.java
new file mode 100644
index 0000000..1d72772
--- /dev/null
+++ b/core/java/android/security/authenticationpolicy/EnableSecureLockDeviceParams.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.security.authenticationpolicy;
+
+import static android.security.Flags.FLAG_SECURE_LOCKDOWN;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+
+/**
+ * Parameters related to a request to enable secure lock on the device.
+ *
+ * @hide
+ */
+@SystemApi
+@FlaggedApi(FLAG_SECURE_LOCKDOWN)
+public final class EnableSecureLockDeviceParams implements Parcelable {
+
+ /**
+ * Client message associated with the request to enable secure lock on the device. This message
+ * will be shown on the device when secure lock mode is enabled.
+ */
+ private final @NonNull String mMessage;
+
+ /**
+ * Creates EnableSecureLockDeviceParams with the given params.
+ *
+ * @param message Allows clients to pass in a message with information about the request to
+ * enable secure lock on the device. This message will be shown to the user when
+ * secure lock mode is enabled. If an empty string is provided, it will default
+ * to a system-defined string (e.g. "Device is securely locked remotely.")
+ */
+ public EnableSecureLockDeviceParams(@NonNull String message) {
+ mMessage = message;
+ }
+
+ private EnableSecureLockDeviceParams(@NonNull Parcel in) {
+ mMessage = Objects.requireNonNull(in.readString8());
+ }
+
+ public static final @NonNull Creator<EnableSecureLockDeviceParams> CREATOR =
+ new Creator<EnableSecureLockDeviceParams>() {
+ @Override
+ public EnableSecureLockDeviceParams createFromParcel(Parcel in) {
+ return new EnableSecureLockDeviceParams(in);
+ }
+
+ @Override
+ public EnableSecureLockDeviceParams[] newArray(int size) {
+ return new EnableSecureLockDeviceParams[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeString8(mMessage);
+ }
+}
diff --git a/core/java/android/security/authenticationpolicy/IAuthenticationPolicyService.aidl b/core/java/android/security/authenticationpolicy/IAuthenticationPolicyService.aidl
new file mode 100644
index 0000000..5ad4534
--- /dev/null
+++ b/core/java/android/security/authenticationpolicy/IAuthenticationPolicyService.aidl
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.authenticationpolicy;
+
+import android.security.authenticationpolicy.EnableSecureLockDeviceParams;
+import android.security.authenticationpolicy.DisableSecureLockDeviceParams;
+
+/**
+ * Communication channel from AuthenticationPolicyManager to AuthenticationPolicyService.
+ * @hide
+ */
+interface IAuthenticationPolicyService {
+ @EnforcePermission("MANAGE_SECURE_LOCK_DEVICE")
+ int enableSecureLockDevice(in EnableSecureLockDeviceParams params);
+
+ @EnforcePermission("MANAGE_SECURE_LOCK_DEVICE")
+ int disableSecureLockDevice(in DisableSecureLockDeviceParams params);
+}
\ No newline at end of file
diff --git a/core/java/android/security/authenticationpolicy/OWNERS b/core/java/android/security/authenticationpolicy/OWNERS
new file mode 100644
index 0000000..4310d1a
--- /dev/null
+++ b/core/java/android/security/authenticationpolicy/OWNERS
@@ -0,0 +1 @@
+include /services/core/java/com/android/server/security/authenticationpolicy/OWNERS
\ No newline at end of file
diff --git a/core/java/android/security/flags.aconfig b/core/java/android/security/flags.aconfig
index ce90121..09004b3 100644
--- a/core/java/android/security/flags.aconfig
+++ b/core/java/android/security/flags.aconfig
@@ -115,6 +115,14 @@
}
flag {
+ name: "protect_device_config_flags"
+ namespace: "psap_ai"
+ description: "Feature flag to limit adb shell to allowlisted flags"
+ bug: "364083026"
+ is_fixed_read_only: true
+}
+
+flag {
name: "keystore_grant_api"
namespace: "hardware_backed_security"
description: "Feature flag for exposing KeyStore grant APIs"
diff --git a/core/java/android/security/forensic/ForensicEvent.java b/core/java/android/security/forensic/ForensicEvent.java
index 90906ed..3d908cc 100644
--- a/core/java/android/security/forensic/ForensicEvent.java
+++ b/core/java/android/security/forensic/ForensicEvent.java
@@ -17,13 +17,17 @@
package android.security.forensic;
import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.app.admin.ConnectEvent;
+import android.app.admin.DnsEvent;
+import android.app.admin.SecurityLog.SecurityEvent;
import android.os.Parcel;
import android.os.Parcelable;
import android.security.Flags;
-import android.util.ArrayMap;
-import java.util.Map;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
/**
* A class that represents a forensic event.
@@ -33,11 +37,24 @@
public final class ForensicEvent implements Parcelable {
private static final String TAG = "ForensicEvent";
- @NonNull
- private final String mType;
+ public static final int SECURITY_EVENT = 0;
+ public static final int NETWORK_EVENT_DNS = 1;
+ public static final int NETWORK_EVENT_CONNECT = 2;
- @NonNull
- private final Map<String, String> mKeyValuePairs;
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({
+ ForensicEvent.SECURITY_EVENT,
+ ForensicEvent.NETWORK_EVENT_DNS,
+ ForensicEvent.NETWORK_EVENT_CONNECT,
+ })
+ public @interface EventType {}
+
+ @NonNull @EventType private final int mType;
+
+ private final SecurityEvent mSecurityEvent;
+ private final DnsEvent mNetworkEventDns;
+ private final ConnectEvent mNetworkEventConnect;
public static final @NonNull Parcelable.Creator<ForensicEvent> CREATOR =
new Parcelable.Creator<>() {
@@ -50,30 +67,99 @@
}
};
- public ForensicEvent(@NonNull String type, @NonNull Map<String, String> keyValuePairs) {
- mType = type;
- mKeyValuePairs = keyValuePairs;
+ public ForensicEvent(@NonNull SecurityEvent securityEvent) {
+ mType = SECURITY_EVENT;
+ mSecurityEvent = securityEvent;
+ mNetworkEventDns = null;
+ mNetworkEventConnect = null;
+ }
+
+ public ForensicEvent(@NonNull DnsEvent dnsEvent) {
+ mType = NETWORK_EVENT_DNS;
+ mNetworkEventDns = dnsEvent;
+ mSecurityEvent = null;
+ mNetworkEventConnect = null;
+ }
+
+ public ForensicEvent(@NonNull ConnectEvent connectEvent) {
+ mType = NETWORK_EVENT_CONNECT;
+ mNetworkEventConnect = connectEvent;
+ mSecurityEvent = null;
+ mNetworkEventDns = null;
}
private ForensicEvent(@NonNull Parcel in) {
- mType = in.readString();
- mKeyValuePairs = new ArrayMap<>(in.readInt());
- in.readMap(mKeyValuePairs, getClass().getClassLoader(), String.class, String.class);
+ mType = in.readInt();
+ switch (mType) {
+ case SECURITY_EVENT:
+ mSecurityEvent = SecurityEvent.CREATOR.createFromParcel(in);
+ mNetworkEventDns = null;
+ mNetworkEventConnect = null;
+ break;
+ case NETWORK_EVENT_DNS:
+ mNetworkEventDns = DnsEvent.CREATOR.createFromParcel(in);
+ mSecurityEvent = null;
+ mNetworkEventConnect = null;
+ break;
+ case NETWORK_EVENT_CONNECT:
+ mNetworkEventConnect = ConnectEvent.CREATOR.createFromParcel(in);
+ mSecurityEvent = null;
+ mNetworkEventDns = null;
+ break;
+ default:
+ throw new IllegalArgumentException("Invalid event type: " + mType);
+ }
}
- public String getType() {
+ /** Returns the type of the forensic event. */
+ @NonNull
+ public @EventType int getType() {
return mType;
}
- public Map<String, String> getKeyValuePairs() {
- return mKeyValuePairs;
+ /** Returns the SecurityEvent object. */
+ @NonNull
+ public SecurityEvent getSecurityEvent() {
+ if (mType == SECURITY_EVENT) {
+ return mSecurityEvent;
+ }
+ throw new IllegalArgumentException("Event type is not security event: " + mType);
+ }
+
+ /** Returns the DnsEvent object. */
+ @NonNull
+ public DnsEvent getDnsEvent() {
+ if (mType == NETWORK_EVENT_DNS) {
+ return mNetworkEventDns;
+ }
+ throw new IllegalArgumentException("Event type is not network DNS event: " + mType);
+ }
+
+ /** Returns the ConnectEvent object. */
+ @NonNull
+ public ConnectEvent getConnectEvent() {
+ if (mType == NETWORK_EVENT_CONNECT) {
+ return mNetworkEventConnect;
+ }
+ throw new IllegalArgumentException("Event type is not network connect event: " + mType);
}
@Override
public void writeToParcel(@NonNull Parcel out, int flags) {
- out.writeString(mType);
- out.writeInt(mKeyValuePairs.size());
- out.writeMap(mKeyValuePairs);
+ out.writeInt(mType);
+ switch (mType) {
+ case SECURITY_EVENT:
+ out.writeParcelable(mSecurityEvent, flags);
+ break;
+ case NETWORK_EVENT_DNS:
+ out.writeParcelable(mNetworkEventDns, flags);
+ break;
+ case NETWORK_EVENT_CONNECT:
+ out.writeParcelable(mNetworkEventConnect, flags);
+ break;
+ default:
+ throw new IllegalArgumentException("Invalid event type: " + mType);
+ }
}
@FlaggedApi(Flags.FLAG_AFL_API)
@@ -86,7 +172,6 @@
public String toString() {
return "ForensicEvent{"
+ "mType=" + mType
- + ", mKeyValuePairs=" + mKeyValuePairs
+ '}';
}
}
diff --git a/core/java/android/security/forensic/ForensicManager.java b/core/java/android/security/forensic/ForensicManager.java
new file mode 100644
index 0000000..9126182
--- /dev/null
+++ b/core/java/android/security/forensic/ForensicManager.java
@@ -0,0 +1,276 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.forensic;
+
+import static android.Manifest.permission.MANAGE_FORENSIC_STATE;
+import static android.Manifest.permission.READ_FORENSIC_STATE;
+
+import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.annotation.SystemService;
+import android.content.Context;
+import android.os.RemoteException;
+import android.security.Flags;
+import android.util.Log;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
+
+/**
+ * ForensicManager manages the forensic logging on Android devices.
+ * Upon user consent, forensic logging collects various device events for
+ * off-device investigation of potential device compromise.
+ * <p>
+ * Forensic logging can either be enabled ({@link #STATE_ENABLED}
+ * or disabled ({@link #STATE_DISABLED}).
+ * <p>
+ * The Forensic logs will be transferred to
+ * {@link android.security.forensic.ForensicEventTransport}.
+ *
+ * @hide
+ */
+@SystemApi
+@FlaggedApi(Flags.FLAG_AFL_API)
+@SystemService(Context.FORENSIC_SERVICE)
+public class ForensicManager {
+ private static final String TAG = "ForensicManager";
+
+ /** @hide */
+ @Target(ElementType.TYPE_USE)
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = { "STATE_" }, value = {
+ STATE_UNKNOWN,
+ STATE_DISABLED,
+ STATE_ENABLED
+ })
+ public @interface ForensicState {}
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = { "ERROR_" }, value = {
+ ERROR_UNKNOWN,
+ ERROR_PERMISSION_DENIED,
+ ERROR_TRANSPORT_UNAVAILABLE,
+ ERROR_DATA_SOURCE_UNAVAILABLE
+ })
+ public @interface ForensicError {}
+
+ /**
+ * Indicates an unknown state
+ */
+ public static final int STATE_UNKNOWN = IForensicServiceStateCallback.State.UNKNOWN;
+
+ /**
+ * Indicates an state that the forensic is turned off.
+ */
+ public static final int STATE_DISABLED = IForensicServiceStateCallback.State.DISABLED;
+
+ /**
+ * Indicates an state that the forensic is turned on.
+ */
+ public static final int STATE_ENABLED = IForensicServiceStateCallback.State.ENABLED;
+
+ /**
+ * Indicates an unknown error
+ */
+ public static final int ERROR_UNKNOWN = IForensicServiceCommandCallback.ErrorCode.UNKNOWN;
+
+ /**
+ * Indicates an error due to insufficient access rights.
+ */
+ public static final int ERROR_PERMISSION_DENIED =
+ IForensicServiceCommandCallback.ErrorCode.PERMISSION_DENIED;
+
+ /**
+ * Indicates an error due to unavailability of the forensic event transport.
+ */
+ public static final int ERROR_TRANSPORT_UNAVAILABLE =
+ IForensicServiceCommandCallback.ErrorCode.TRANSPORT_UNAVAILABLE;
+
+ /**
+ * Indicates an error due to unavailability of the data source.
+ */
+ public static final int ERROR_DATA_SOURCE_UNAVAILABLE =
+ IForensicServiceCommandCallback.ErrorCode.DATA_SOURCE_UNAVAILABLE;
+
+
+ private final IForensicService mService;
+
+ private final ConcurrentHashMap<Consumer<Integer>, IForensicServiceStateCallback>
+ mStateCallbacks = new ConcurrentHashMap<>();
+
+ /**
+ * Constructor
+ *
+ * @param service A valid instance of IForensicService.
+ * @hide
+ */
+ public ForensicManager(IForensicService service) {
+ mService = service;
+ }
+
+ /**
+ * Add a callback to monitor the state of the ForensicService.
+ *
+ * @param executor The executor through which the callback should be invoked.
+ * @param callback The callback for state change.
+ * Once the callback is registered, the callback will be called
+ * to reflect the init state.
+ * The callback can be registered only once.
+ */
+ @RequiresPermission(READ_FORENSIC_STATE)
+ public void addStateCallback(@NonNull @CallbackExecutor Executor executor,
+ @NonNull @ForensicState Consumer<Integer> callback) {
+ Objects.requireNonNull(executor);
+ Objects.requireNonNull(callback);
+
+ if (mStateCallbacks.get(callback) != null) {
+ Log.d(TAG, "addStateCallback callback already present");
+ return;
+ }
+
+ final IForensicServiceStateCallback wrappedCallback =
+ new IForensicServiceStateCallback.Stub() {
+ @Override
+ public void onStateChange(int state) {
+ executor.execute(() -> callback.accept(state));
+ }
+ };
+ try {
+ mService.addStateCallback(wrappedCallback);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+
+ mStateCallbacks.put(callback, wrappedCallback);
+ }
+
+ /**
+ * Remove a callback to monitor the state of the ForensicService.
+ *
+ * @param callback The callback to remove.
+ */
+ @RequiresPermission(READ_FORENSIC_STATE)
+ public void removeStateCallback(@NonNull Consumer<@ForensicState Integer> callback) {
+ Objects.requireNonNull(callback);
+ if (!mStateCallbacks.containsKey(callback)) {
+ Log.d(TAG, "removeStateCallback callback not present");
+ return;
+ }
+
+ IForensicServiceStateCallback wrappedCallback = mStateCallbacks.get(callback);
+
+ try {
+ mService.removeStateCallback(wrappedCallback);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+
+ mStateCallbacks.remove(callback);
+ }
+
+ /**
+ * Enable forensic logging.
+ * If successful, ForensicService will transition to {@link #STATE_ENABLED} state.
+ * <p>
+ * When forensic logging is enabled, various device events will be collected and
+ * sent over to the registered {@link android.security.forensic.ForensicEventTransport}.
+ *
+ * @param executor The executor through which the callback should be invoked.
+ * @param callback The callback for the command result.
+ */
+ @RequiresPermission(MANAGE_FORENSIC_STATE)
+ public void enable(@NonNull @CallbackExecutor Executor executor,
+ @NonNull CommandCallback callback) {
+ Objects.requireNonNull(executor);
+ Objects.requireNonNull(callback);
+ try {
+ mService.enable(new IForensicServiceCommandCallback.Stub() {
+ @Override
+ public void onSuccess() {
+ executor.execute(callback::onSuccess);
+ }
+
+ @Override
+ public void onFailure(int error) {
+ executor.execute(() -> callback.onFailure(error));
+ }
+ });
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Disable forensic logging.
+ * If successful, ForensicService will transition to {@link #STATE_DISABLED}.
+ * <p>
+ * When forensic logging is disabled, device events will no longer be collected.
+ * Any events that have been collected but not yet sent to ForensicEventTransport
+ * will be transferred as a final batch.
+ *
+ * @param executor The executor through which the callback should be invoked.
+ * @param callback The callback for the command result.
+ */
+ @RequiresPermission(MANAGE_FORENSIC_STATE)
+ public void disable(@NonNull @CallbackExecutor Executor executor,
+ @NonNull CommandCallback callback) {
+ Objects.requireNonNull(executor);
+ Objects.requireNonNull(callback);
+ try {
+ mService.disable(new IForensicServiceCommandCallback.Stub() {
+ @Override
+ public void onSuccess() {
+ executor.execute(callback::onSuccess);
+ }
+
+ @Override
+ public void onFailure(int error) {
+ executor.execute(() -> callback.onFailure(error));
+ }
+ });
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Callback used in {@link #enable} and {@link #disable} to indicate the result of the command.
+ */
+ public interface CommandCallback {
+ /**
+ * Called when command succeeds.
+ */
+ void onSuccess();
+
+ /**
+ * Called when command fails.
+ * @param error The error number.
+ */
+ void onFailure(@ForensicError int error);
+ }
+}
diff --git a/core/java/android/security/forensic/IBackupTransport.aidl b/core/java/android/security/forensic/IForensicEventTransport.aidl
similarity index 96%
rename from core/java/android/security/forensic/IBackupTransport.aidl
rename to core/java/android/security/forensic/IForensicEventTransport.aidl
index c2cbc83..80e78eb 100644
--- a/core/java/android/security/forensic/IBackupTransport.aidl
+++ b/core/java/android/security/forensic/IForensicEventTransport.aidl
@@ -20,7 +20,7 @@
import com.android.internal.infra.AndroidFuture;
/** {@hide} */
-oneway interface IBackupTransport {
+oneway interface IForensicEventTransport {
/**
* Initialize the server side.
*/
diff --git a/core/java/android/security/forensic/IForensicService.aidl b/core/java/android/security/forensic/IForensicService.aidl
index a944b18..8039b26 100644
--- a/core/java/android/security/forensic/IForensicService.aidl
+++ b/core/java/android/security/forensic/IForensicService.aidl
@@ -24,9 +24,12 @@
* @hide
*/
interface IForensicService {
- void monitorState(IForensicServiceStateCallback callback);
- void makeVisible(IForensicServiceCommandCallback callback);
- void makeInvisible(IForensicServiceCommandCallback callback);
+ @EnforcePermission("READ_FORENSIC_STATE")
+ void addStateCallback(IForensicServiceStateCallback callback);
+ @EnforcePermission("READ_FORENSIC_STATE")
+ void removeStateCallback(IForensicServiceStateCallback callback);
+ @EnforcePermission("MANAGE_FORENSIC_STATE")
void enable(IForensicServiceCommandCallback callback);
+ @EnforcePermission("MANAGE_FORENSIC_STATE")
void disable(IForensicServiceCommandCallback callback);
}
diff --git a/core/java/android/security/forensic/IForensicServiceCommandCallback.aidl b/core/java/android/security/forensic/IForensicServiceCommandCallback.aidl
index 7fa0c7f..6d1456e 100644
--- a/core/java/android/security/forensic/IForensicServiceCommandCallback.aidl
+++ b/core/java/android/security/forensic/IForensicServiceCommandCallback.aidl
@@ -25,8 +25,8 @@
UNKNOWN = 0,
PERMISSION_DENIED = 1,
INVALID_STATE_TRANSITION = 2,
- BACKUP_TRANSPORT_UNAVAILABLE = 3,
- DATA_SOURCE_UNAVAILABLE = 3,
+ TRANSPORT_UNAVAILABLE = 3,
+ DATA_SOURCE_UNAVAILABLE = 4,
}
void onSuccess();
void onFailure(ErrorCode error);
diff --git a/core/java/android/security/forensic/IForensicServiceStateCallback.aidl b/core/java/android/security/forensic/IForensicServiceStateCallback.aidl
index 0cda350..1b68c7b 100644
--- a/core/java/android/security/forensic/IForensicServiceStateCallback.aidl
+++ b/core/java/android/security/forensic/IForensicServiceStateCallback.aidl
@@ -23,9 +23,8 @@
@Backing(type="int")
enum State{
UNKNOWN = 0,
- INVISIBLE = 1,
- VISIBLE = 2,
- ENABLED = 3,
+ DISABLED = 1,
+ ENABLED = 2,
}
void onStateChange(State state);
}
diff --git a/core/java/android/security/responsible_apis_flags.aconfig b/core/java/android/security/responsible_apis_flags.aconfig
index 66e1f38..6c92991 100644
--- a/core/java/android/security/responsible_apis_flags.aconfig
+++ b/core/java/android/security/responsible_apis_flags.aconfig
@@ -103,3 +103,10 @@
description: "Applies intentMatchingFlags while matching intents to application components"
bug: "364354494"
}
+
+flag {
+ name: "aapm_feature_disable_install_unknown_sources"
+ namespace: "responsible_apis"
+ description: "Android Advanced Protection Mode Feature: Disable Install Unknown Sources"
+ bug: "369361373"
+}
diff --git a/core/java/android/service/autofill/FillEventHistory.java b/core/java/android/service/autofill/FillEventHistory.java
index 98dda10..14a14e6 100644
--- a/core/java/android/service/autofill/FillEventHistory.java
+++ b/core/java/android/service/autofill/FillEventHistory.java
@@ -16,8 +16,10 @@
package android.service.autofill;
+import static android.service.autofill.Flags.FLAG_AUTOFILL_W_METRICS;
import static android.view.autofill.Helper.sVerbose;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -63,14 +65,21 @@
private static final String TAG = "FillEventHistory";
/**
- * Not in parcel. The ID of the autofill session that created the {@link FillResponse}.
+ * The ID of the autofill session that created the {@link FillResponse}.
+ *
+ * TODO: add this to the parcel.
*/
private final int mSessionId;
@Nullable private final Bundle mClientState;
@Nullable List<Event> mEvents;
- /** @hide */
+ /**
+ * Returns the unique identifier of this FillEventHistory.
+ *
+ * <p>This is used to differentiate individual FillEventHistory.
+ */
+ @FlaggedApi(FLAG_AUTOFILL_W_METRICS)
public int getSessionId() {
return mSessionId;
}
@@ -283,6 +292,13 @@
/** All fields matched contents of datasets. */
public static final int NO_SAVE_UI_REASON_DATASET_MATCH = 6;
+ /**
+ * Credential Manager is invoked instead of Autofill. When that happens, Save Dialog cannot
+ * be shown, and this will be populated in
+ */
+ @FlaggedApi(FLAG_AUTOFILL_W_METRICS)
+ public static final int NO_SAVE_UI_REASON_USING_CREDMAN = 7;
+
/** @hide */
@IntDef(prefix = { "NO_SAVE_UI_REASON_" }, value = {
NO_SAVE_UI_REASON_NONE,
@@ -310,11 +326,20 @@
public static final int UI_TYPE_DIALOG = 3;
/**
- * The autofill suggestion is shown os a credman bottom sheet
- * @hide
+ * The autofill suggestion is shown os a credman bottom sheet
+ *
+ * <p>Note, this was introduced as bottom sheet even though it applies to all credman UI
+ * types. Instead of exposing this directly to the public, the generic UI_TYPE_CREDMAN is
+ * introduced with the same number.
+ *
+ * @hide
*/
public static final int UI_TYPE_CREDMAN_BOTTOM_SHEET = 4;
+ /** Credential Manager suggestions are shown instead of Autofill suggestion */
+ @FlaggedApi(FLAG_AUTOFILL_W_METRICS)
+ public static final int UI_TYPE_CREDMAN = 4;
+
/** @hide */
@IntDef(prefix = { "UI_TYPE_" }, value = {
UI_TYPE_UNKNOWN,
@@ -359,6 +384,13 @@
return mEventType;
}
+ /** Gets the {@code AutofillId} that's focused at the time of action */
+ @FlaggedApi(FLAG_AUTOFILL_W_METRICS)
+ @Nullable
+ public AutofillId getFocusedId() {
+ return null;
+ }
+
/**
* Returns the id of dataset the id was on.
*
@@ -391,6 +423,17 @@
}
/**
+ * Returns which datasets were shown to the user.
+ *
+ * <p><b>Note: </b>Only set on events of type {@link #TYPE_DATASETS_SHOWN}.
+ */
+ @FlaggedApi(FLAG_AUTOFILL_W_METRICS)
+ @NonNull
+ public Set<String> getShownDatasetIds() {
+ return Collections.emptySet();
+ }
+
+ /**
* Returns which datasets were NOT selected by the user.
*
* <p><b>Note: </b>Only set on events of type {@link #TYPE_CONTEXT_COMMITTED}.
diff --git a/core/java/android/service/carrier/CarrierMessagingService.java b/core/java/android/service/carrier/CarrierMessagingService.java
index 61213e6..a825a7e 100644
--- a/core/java/android/service/carrier/CarrierMessagingService.java
+++ b/core/java/android/service/carrier/CarrierMessagingService.java
@@ -16,6 +16,7 @@
package android.service.carrier;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -26,6 +27,8 @@
import android.os.IBinder;
import android.os.RemoteException;
+import com.android.internal.telephony.flags.Flags;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.List;
@@ -97,16 +100,317 @@
public static final int SEND_STATUS_RETRY_ON_CARRIER_NETWORK = 1;
/**
- * SMS/MMS sending failed. We should not retry via the carrier network.
+ * SMS/MMS sending failed due to an unspecified issue. Sending will not be retried via the
+ * carrier network.
+ *
+ * <p>Maps to SmsManager.RESULT_RIL_GENERIC_FAILURE for SMS and SmsManager.MMS_ERROR_UNSPECIFIED
+ * for MMS.
*/
public static final int SEND_STATUS_ERROR = 2;
+ /**
+ * More precise error reasons for outbound SMS send requests. These will not be retried on the
+ * carrier network.
+ *
+ * <p>Each code maps directly to an SmsManager code (e.g. SEND_STATS_RESULT_ERROR_NULL_PDU maps
+ * to SmsManager.RESULT_ERROR_NULL_PDU).
+ */
+
+ /**
+ * Generic failure cause.
+ *
+ * @see android.telephony.SmsManager.RESULT_ERROR_GENERIC_FAILURE
+ */
+ @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE)
+ public static final int SEND_STATUS_RESULT_ERROR_GENERIC_FAILURE = 200;
+
+ /**
+ * Failed because no pdu provided.
+ *
+ * @see android.telephony.SmsManager.RESULT_ERROR_NULL_PDU
+ */
+ @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE)
+ public static final int SEND_STATUS_RESULT_ERROR_NULL_PDU = 201;
+
+ /**
+ * Failed because service is currently unavailable.
+ *
+ * @see android.telephony.SmsManager.RESULT_ERROR_NO_SERVICE
+ */
+ @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE)
+ public static final int SEND_STATUS_RESULT_ERROR_NO_SERVICE = 202;
+
+ /**
+ * Failed because we reached the sending queue limit.
+ *
+ * @see android.telephony.SmsManager.RESULT_ERROR_LIMIT_EXCEEDED
+ */
+ @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE)
+ public static final int SEND_STATUS_RESULT_ERROR_LIMIT_EXCEEDED = 203;
+
+ /**
+ * Failed because FDN is enabled.
+ *
+ * @see android.telephony.SmsManager.RESULT_ERROR_FDN_CHECK_FAILURE
+ */
+ @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE)
+ public static final int SEND_STATUS_RESULT_ERROR_FDN_CHECK_FAILURE = 204;
+
+ /**
+ * Failed because user denied the sending of this short code.
+ *
+ * @see android.telephony.SmsManager.RESULT_ERROR_SHORT_CODE_NOT_ALLOWED
+ */
+ @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE)
+ public static final int SEND_STATUS_RESULT_ERROR_SHORT_CODE_NOT_ALLOWED = 205;
+
+ /**
+ * Failed because the user has denied this app ever send premium short codes.
+ *
+ * @see android.telephony.SmsManager.RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED
+ */
+ @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE)
+ public static final int SEND_STATUS_RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED = 206;
+
+ /**
+ * Failed because of network rejection.
+ *
+ * @see android.telephony.SmsManager.RESULT_NETWORK_REJECT
+ */
+ @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE)
+ public static final int SEND_STATUS_RESULT_NETWORK_REJECT = 207;
+
+ /**
+ * Failed because of invalid arguments.
+ *
+ * @see android.telephony.SmsManager.RESULT_INVALID_ARGUMENTS
+ */
+ @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE)
+ public static final int SEND_STATUS_RESULT_INVALID_ARGUMENTS = 208;
+
+ /**
+ * Failed because of an invalid state.
+ *
+ * @see android.telephony.SmsManager.RESULT_INVALID_STATE
+ */
+ @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE)
+ public static final int SEND_STATUS_RESULT_INVALID_STATE = 209;
+
+ /**
+ * Failed because the sms format is not valid.
+ *
+ * @see android.telephony.SmsManager.RESULT_INVALID_SMS_FORMAT
+ */
+ @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE)
+ public static final int SEND_STATUS_RESULT_INVALID_SMS_FORMAT = 210;
+
+ /**
+ * Failed because of a network error.
+ *
+ * @see android.telephony.SmsManager.RESULT_NETWORK_ERROR
+ */
+ @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE)
+ public static final int SEND_STATUS_RESULT_NETWORK_ERROR = 211;
+
+ /**
+ * Failed because of an encoding error.
+ *
+ * @see android.telephony.SmsManager.RESULT_ENCODING_ERROR
+ */
+ @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE)
+ public static final int SEND_STATUS_RESULT_ENCODING_ERROR = 212;
+
+ /**
+ * Failed because of an invalid smsc address
+ *
+ * @see android.telephony.SmsManager.RESULT_INVALID_SMSC_ADDRESS
+ */
+ @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE)
+ public static final int SEND_STATUS_RESULT_INVALID_SMSC_ADDRESS = 213;
+
+ /**
+ * Failed because the operation is not allowed.
+ *
+ * @see android.telephony.SmsManager.RESULT_OPERATION_NOT_ALLOWED
+ */
+ @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE)
+ public static final int SEND_STATUS_RESULT_OPERATION_NOT_ALLOWED = 214;
+
+ /**
+ * Failed because the operation was cancelled.
+ *
+ * @see android.telephony.SmsManager.RESULT_CANCELLED
+ */
+ @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE)
+ public static final int SEND_STATUS_RESULT_CANCELLED = 215;
+
+ /**
+ * Failed because the request is not supported.
+ *
+ * @see android.telephony.SmsManager.RESULT_REQUEST_NOT_SUPPORTED
+ */
+ @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE)
+ public static final int SEND_STATUS_RESULT_REQUEST_NOT_SUPPORTED = 216;
+
+ /**
+ * Failed sending during an emergency call.
+ *
+ * @see android.telephony.SmsManager.RESULT_SMS_BLOCKED_DURING_EMERGENCY
+ */
+ @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE)
+ public static final int SEND_STATUS_RESULT_SMS_BLOCKED_DURING_EMERGENCY = 217;
+
+ /**
+ * Failed to send an sms retry.
+ *
+ * @see android.telephony.SmsManager.RESULT_SMS_SEND_RETRY_FAILED
+ */
+ @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE)
+ public static final int SEND_STATUS_RESULT_SMS_SEND_RETRY_FAILED = 218;
+
+ /**
+ * More precise error reasons for outbound MMS send requests. These will not be retried on the
+ * carrier network.
+ *
+ * <p>Each code maps directly to an SmsManager code (e.g.
+ * SEND_STATUS_MMS_ERROR_UNABLE_CONNECT_MMS maps to SmsManager.MMS_ERROR_UNABLE_CONNECT_MMS).
+ */
+
+ /**
+ * Unspecific MMS error occurred during send.
+ *
+ * @see android.telephony.SmsManager.MMS_ERROR_UNSPECIFIED
+ */
+ @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE)
+ public static final int SEND_STATUS_MMS_ERROR_UNSPECIFIED = 400;
+
+ /**
+ * ApnException occurred during MMS network setup.
+ *
+ * @see android.telephony.SmsManager.MMS_ERROR_INVALID_APN
+ */
+ @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE)
+ public static final int SEND_STATUS_MMS_ERROR_INVALID_APN = 401;
+
+ /**
+ * An error occurred during the MMS connection setup.
+ *
+ * @see android.telephony.SmsManager.MMS_ERROR_UNABLE_CONNECT_MMS
+ */
+ @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE)
+ public static final int SEND_STATUS_MMS_ERROR_UNABLE_CONNECT_MMS = 402;
+
+ /**
+ * An error occurred during the HTTP client setup.
+ *
+ * @see android.telephony.SmsManager.MMS_ERROR_HTTP_FAILURE
+ */
+ @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE)
+ public static final int SEND_STATUS_MMS_ERROR_HTTP_FAILURE = 403;
+
+ /**
+ * An I/O error occurred reading the PDU.
+ *
+ * @see android.telephony.SmsManager.MMS_ERROR_IO_ERROR
+ */
+ @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE)
+ public static final int SEND_STATUS_MMS_ERROR_IO_ERROR = 404;
+
+ /**
+ * An error occurred while retrying sending the MMS.
+ *
+ * @see android.telephony.SmsManager.MMS_ERROR_RETRY
+ */
+ @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE)
+ public static final int SEND_STATUS_MMS_ERROR_RETRY = 405;
+
+ /**
+ * The carrier-dependent configuration values could not be loaded.
+ *
+ * @see android.telephony.SmsManager.MMS_ERROR_CONFIGURATION_ERROR
+ */
+ @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE)
+ public static final int SEND_STATUS_MMS_ERROR_CONFIGURATION_ERROR = 406;
+
+ /**
+ * There is neither Wi-Fi nor mobile data network.
+ *
+ * @see android.telephony.SmsManager.MMS_ERROR_NO_DATA_NETWORK
+ */
+ @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE)
+ public static final int SEND_STATUS_MMS_ERROR_NO_DATA_NETWORK = 407;
+
+ /**
+ * The subscription id for the send is invalid.
+ *
+ * @see android.telephony.SmsManager.MMS_ERROR_INVALID_SUBSCRIPTION_ID
+ */
+ @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE)
+ public static final int SEND_STATUS_MMS_ERROR_INVALID_SUBSCRIPTION_ID = 408;
+
+ /**
+ * The subscription id for the send is inactive.
+ *
+ * @see android.telephony.SmsManager.MMS_ERROR_INACTIVE_SUBSCRIPTION
+ */
+ @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE)
+ public static final int SEND_STATUS_MMS_ERROR_INACTIVE_SUBSCRIPTION = 409;
+
+ /**
+ * Data is disabled for the MMS APN.
+ *
+ * @see android.telephony.SmsManager.MMS_ERROR_DATA_DISABLED
+ */
+ @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE)
+ public static final int SEND_STATUS_MMS_ERROR_DATA_DISABLED = 410;
+
+ /**
+ * MMS is disabled by a carrier.
+ *
+ * @see android.telephony.SmsManager.MMS_ERROR_MMS_DISABLED_BY_CARRIER
+ */
+ @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE)
+ public static final int SEND_STATUS_MMS_ERROR_MMS_DISABLED_BY_CARRIER = 411;
+
/** @hide */
- @IntDef(prefix = { "SEND_STATUS_" }, value = {
- SEND_STATUS_OK,
- SEND_STATUS_RETRY_ON_CARRIER_NETWORK,
- SEND_STATUS_ERROR
- })
+ @IntDef(
+ prefix = {"SEND_STATUS_"},
+ value = {
+ SEND_STATUS_OK,
+ SEND_STATUS_RETRY_ON_CARRIER_NETWORK,
+ SEND_STATUS_ERROR,
+ SEND_STATUS_RESULT_ERROR_GENERIC_FAILURE,
+ SEND_STATUS_RESULT_ERROR_NULL_PDU,
+ SEND_STATUS_RESULT_ERROR_NO_SERVICE,
+ SEND_STATUS_RESULT_ERROR_LIMIT_EXCEEDED,
+ SEND_STATUS_RESULT_ERROR_FDN_CHECK_FAILURE,
+ SEND_STATUS_RESULT_ERROR_SHORT_CODE_NOT_ALLOWED,
+ SEND_STATUS_RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED,
+ SEND_STATUS_RESULT_NETWORK_REJECT,
+ SEND_STATUS_RESULT_INVALID_ARGUMENTS,
+ SEND_STATUS_RESULT_INVALID_STATE,
+ SEND_STATUS_RESULT_INVALID_SMS_FORMAT,
+ SEND_STATUS_RESULT_NETWORK_ERROR,
+ SEND_STATUS_RESULT_ENCODING_ERROR,
+ SEND_STATUS_RESULT_INVALID_SMSC_ADDRESS,
+ SEND_STATUS_RESULT_OPERATION_NOT_ALLOWED,
+ SEND_STATUS_RESULT_CANCELLED,
+ SEND_STATUS_RESULT_REQUEST_NOT_SUPPORTED,
+ SEND_STATUS_RESULT_SMS_BLOCKED_DURING_EMERGENCY,
+ SEND_STATUS_RESULT_SMS_SEND_RETRY_FAILED,
+ SEND_STATUS_MMS_ERROR_UNSPECIFIED,
+ SEND_STATUS_MMS_ERROR_INVALID_APN,
+ SEND_STATUS_MMS_ERROR_UNABLE_CONNECT_MMS,
+ SEND_STATUS_MMS_ERROR_HTTP_FAILURE,
+ SEND_STATUS_MMS_ERROR_IO_ERROR,
+ SEND_STATUS_MMS_ERROR_RETRY,
+ SEND_STATUS_MMS_ERROR_CONFIGURATION_ERROR,
+ SEND_STATUS_MMS_ERROR_NO_DATA_NETWORK,
+ SEND_STATUS_MMS_ERROR_INVALID_SUBSCRIPTION_ID,
+ SEND_STATUS_MMS_ERROR_INACTIVE_SUBSCRIPTION,
+ SEND_STATUS_MMS_ERROR_DATA_DISABLED,
+ SEND_STATUS_MMS_ERROR_MMS_DISABLED_BY_CARRIER
+ })
@Retention(RetentionPolicy.SOURCE)
public @interface SendResult {}
@@ -121,16 +425,138 @@
public static final int DOWNLOAD_STATUS_RETRY_ON_CARRIER_NETWORK = 1;
/**
- * MMS downloading failed. We should not retry via the carrier network.
+ * MMS downloading failed due to an unspecified issue. Downloading will not be retried via the
+ * carrier network.
+ *
+ * <p>Maps to SmsManager.MMR_ERROR_UNSPECIFIED.
*/
public static final int DOWNLOAD_STATUS_ERROR = 2;
+ /**
+ * More precise error reasons for inbound MMS download requests. These will not be retried on
+ * the carrier network.
+ *
+ * <p>Each code maps directly to an SmsManager code (e.g.
+ * DOWNLOAD_STATUS_MMS_ERROR_UNABLE_CONNECT_MMS maps to
+ * SmsManager.MMS_ERROR_UNABLE_CONNECT_MMS).
+ */
+
+ /**
+ * Unspecific MMS error occurred during download.
+ *
+ * @see android.telephony.SmsManager.MMS_ERROR_UNSPECIFIED
+ */
+ @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE)
+ public static final int DOWNLOAD_STATUS_MMS_ERROR_UNSPECIFIED = 600;
+
+ /**
+ * ApnException occurred during MMS network setup.
+ *
+ * @see android.telephony.SmsManager.MMS_ERROR_INVALID_APN
+ */
+ @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE)
+ public static final int DOWNLOAD_STATUS_MMS_ERROR_INVALID_APN = 601;
+
+ /**
+ * An error occurred during the MMS connection setup.
+ *
+ * @see android.telephony.SmsManager.MMS_ERROR_UNABLE_CONNECT_MMS
+ */
+ @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE)
+ public static final int DOWNLOAD_STATUS_MMS_ERROR_UNABLE_CONNECT_MMS = 602;
+
+ /**
+ * An error occurred during the HTTP client setup.
+ *
+ * @see android.telephony.SmsManager.MMS_ERROR_HTTP_FAILURE
+ */
+ @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE)
+ public static final int DOWNLOAD_STATUS_MMS_ERROR_HTTP_FAILURE = 603;
+
+ /**
+ * An I/O error occurred reading the PDU.
+ *
+ * @see android.telephony.SmsManager.MMS_ERROR_IO_ERROR
+ */
+ @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE)
+ public static final int DOWNLOAD_STATUS_MMS_ERROR_IO_ERROR = 604;
+
+ /**
+ * An error occurred while retrying downloading the MMS.
+ *
+ * @see android.telephony.SmsManager.MMS_ERROR_RETRY
+ */
+ @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE)
+ public static final int DOWNLOAD_STATUS_MMS_ERROR_RETRY = 605;
+
+ /**
+ * The carrier-dependent configuration values could not be loaded.
+ *
+ * @see android.telephony.SmsManager.MMS_ERROR_CONFIGURATION_ERROR
+ */
+ @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE)
+ public static final int DOWNLOAD_STATUS_MMS_ERROR_CONFIGURATION_ERROR = 606;
+
+ /**
+ * There is neither Wi-Fi nor mobile data network.
+ *
+ * @see android.telephony.SmsManager.MMS_ERROR_NO_DATA_NETWORK
+ */
+ @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE)
+ public static final int DOWNLOAD_STATUS_MMS_ERROR_NO_DATA_NETWORK = 607;
+
+ /**
+ * The subscription id for the download is invalid.
+ *
+ * @see android.telephony.SmsManager.MMS_ERROR_INVALID_SUBSCRIPTION_ID
+ */
+ @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE)
+ public static final int DOWNLOAD_STATUS_MMS_ERROR_INVALID_SUBSCRIPTION_ID = 608;
+
+ /**
+ * The subscription id for the download is inactive.
+ *
+ * @see android.telephony.SmsManager.MMS_ERROR_INACTIVE_SUBSCRIPTION
+ */
+ @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE)
+ public static final int DOWNLOAD_STATUS_MMS_ERROR_INACTIVE_SUBSCRIPTION = 609;
+
+ /**
+ * Data is disabled for the MMS APN.
+ *
+ * @see android.telephony.SmsManager.MMS_ERROR_DATA_DISABLED
+ */
+ @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE)
+ public static final int DOWNLOAD_STATUS_MMS_ERROR_DATA_DISABLED = 610;
+
+ /**
+ * MMS is disabled by a carrier.
+ *
+ * @see android.telephony.SmsManager.MMS_ERROR_MMS_DISABLED_BY_CARRIER
+ */
+ @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE)
+ public static final int DOWNLOAD_STATUS_MMS_ERROR_MMS_DISABLED_BY_CARRIER = 611;
+
/** @hide */
- @IntDef(prefix = { "DOWNLOAD_STATUS_" }, value = {
- DOWNLOAD_STATUS_OK,
- DOWNLOAD_STATUS_RETRY_ON_CARRIER_NETWORK,
- DOWNLOAD_STATUS_ERROR
- })
+ @IntDef(
+ prefix = {"DOWNLOAD_STATUS_"},
+ value = {
+ DOWNLOAD_STATUS_OK,
+ DOWNLOAD_STATUS_RETRY_ON_CARRIER_NETWORK,
+ DOWNLOAD_STATUS_ERROR,
+ DOWNLOAD_STATUS_MMS_ERROR_UNSPECIFIED,
+ DOWNLOAD_STATUS_MMS_ERROR_INVALID_APN,
+ DOWNLOAD_STATUS_MMS_ERROR_UNABLE_CONNECT_MMS,
+ DOWNLOAD_STATUS_MMS_ERROR_HTTP_FAILURE,
+ DOWNLOAD_STATUS_MMS_ERROR_IO_ERROR,
+ DOWNLOAD_STATUS_MMS_ERROR_RETRY,
+ DOWNLOAD_STATUS_MMS_ERROR_CONFIGURATION_ERROR,
+ DOWNLOAD_STATUS_MMS_ERROR_NO_DATA_NETWORK,
+ DOWNLOAD_STATUS_MMS_ERROR_INVALID_SUBSCRIPTION_ID,
+ DOWNLOAD_STATUS_MMS_ERROR_INACTIVE_SUBSCRIPTION,
+ DOWNLOAD_STATUS_MMS_ERROR_DATA_DISABLED,
+ DOWNLOAD_STATUS_MMS_ERROR_MMS_DISABLED_BY_CARRIER
+ })
@Retention(RetentionPolicy.SOURCE)
public @interface DownloadResult {}
diff --git a/core/java/android/service/notification/INotificationListener.aidl b/core/java/android/service/notification/INotificationListener.aidl
index b384b66..5471048 100644
--- a/core/java/android/service/notification/INotificationListener.aidl
+++ b/core/java/android/service/notification/INotificationListener.aidl
@@ -34,10 +34,14 @@
void onListenerConnected(in NotificationRankingUpdate update);
void onNotificationPosted(in IStatusBarNotificationHolder notificationHolder,
in NotificationRankingUpdate update);
+ void onNotificationPostedFull(in StatusBarNotification sbn,
+ in NotificationRankingUpdate update);
void onStatusBarIconsBehaviorChanged(boolean hideSilentStatusIcons);
// stats only for assistant
void onNotificationRemoved(in IStatusBarNotificationHolder notificationHolder,
in NotificationRankingUpdate update, in NotificationStats stats, int reason);
+ void onNotificationRemovedFull(in StatusBarNotification sbn,
+ in NotificationRankingUpdate update, in NotificationStats stats, int reason);
void onNotificationRankingUpdate(in NotificationRankingUpdate update);
void onListenerHintsChanged(int hints);
void onInterruptionFilterChanged(int interruptionFilter);
@@ -48,7 +52,9 @@
// assistants only
void onNotificationEnqueuedWithChannel(in IStatusBarNotificationHolder notificationHolder, in NotificationChannel channel, in NotificationRankingUpdate update);
+ void onNotificationEnqueuedWithChannelFull(in StatusBarNotification sbn, in NotificationChannel channel, in NotificationRankingUpdate update);
void onNotificationSnoozedUntilContext(in IStatusBarNotificationHolder notificationHolder, String snoozeCriterionId);
+ void onNotificationSnoozedUntilContextFull(in StatusBarNotification sbn, String snoozeCriterionId);
void onNotificationsSeen(in List<String> keys);
void onPanelRevealed(int items);
void onPanelHidden();
diff --git a/core/java/android/service/notification/NotificationAssistantService.java b/core/java/android/service/notification/NotificationAssistantService.java
index 091b25a..0a9276c 100644
--- a/core/java/android/service/notification/NotificationAssistantService.java
+++ b/core/java/android/service/notification/NotificationAssistantService.java
@@ -423,7 +423,12 @@
+ "Error receiving StatusBarNotification");
return;
}
+ onNotificationEnqueuedWithChannelFull(sbn, channel, update);
+ }
+ @Override
+ public void onNotificationEnqueuedWithChannelFull(StatusBarNotification sbn,
+ NotificationChannel channel, NotificationRankingUpdate update) {
applyUpdateLocked(update);
SomeArgs args = SomeArgs.obtain();
args.arg1 = sbn;
@@ -447,7 +452,12 @@
Log.w(TAG, "onNotificationSnoozed: Error receiving StatusBarNotification");
return;
}
+ onNotificationSnoozedUntilContextFull(sbn, snoozeCriterionId);
+ }
+ @Override
+ public void onNotificationSnoozedUntilContextFull(
+ StatusBarNotification sbn, String snoozeCriterionId) {
SomeArgs args = SomeArgs.obtain();
args.arg1 = sbn;
args.arg2 = snoozeCriterionId;
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index a8ab211..5d0ec73 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -1490,7 +1490,12 @@
Log.w(TAG, "onNotificationPosted: Error receiving StatusBarNotification");
return;
}
+ onNotificationPostedFull(sbn, update);
+ }
+ @Override
+ public void onNotificationPostedFull(StatusBarNotification sbn,
+ NotificationRankingUpdate update) {
try {
// convert icon metadata to legacy format for older clients
createLegacyIconExtras(sbn.getNotification());
@@ -1518,7 +1523,6 @@
mRankingMap).sendToTarget();
}
}
-
}
@Override
@@ -1531,6 +1535,12 @@
Log.w(TAG, "onNotificationRemoved: Error receiving StatusBarNotification", e);
return;
}
+ onNotificationRemovedFull(sbn, update, stats, reason);
+ }
+
+ @Override
+ public void onNotificationRemovedFull(StatusBarNotification sbn,
+ NotificationRankingUpdate update, NotificationStats stats, int reason) {
if (sbn == null) {
Log.w(TAG, "onNotificationRemoved: Error receiving StatusBarNotification");
return;
@@ -1592,6 +1602,14 @@
}
@Override
+ public void onNotificationEnqueuedWithChannelFull(
+ StatusBarNotification sbn, NotificationChannel channel,
+ NotificationRankingUpdate update)
+ throws RemoteException {
+ // no-op in the listener
+ }
+
+ @Override
public void onNotificationsSeen(List<String> keys)
throws RemoteException {
// no-op in the listener
@@ -1621,6 +1639,13 @@
}
@Override
+ public void onNotificationSnoozedUntilContextFull(
+ StatusBarNotification sbn, String snoozeCriterionId)
+ throws RemoteException {
+ // no-op in the listener
+ }
+
+ @Override
public void onNotificationExpansionChanged(
String key, boolean isUserAction, boolean isExpanded) {
// no-op in the listener
@@ -1688,8 +1713,6 @@
Bundle feedback) {
// no-op in the listener
}
-
-
}
/**
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index 24328eb..1388778 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -2532,7 +2532,7 @@
/** Returns whether the rule id corresponds to an implicit rule. */
public static boolean isImplicitRuleId(@NonNull String ruleId) {
- return ruleId.startsWith(IMPLICIT_RULE_ID_PREFIX);
+ return ruleId != null && ruleId.startsWith(IMPLICIT_RULE_ID_PREFIX);
}
private static int[] tryParseHourAndMinute(String value) {
diff --git a/core/java/android/service/quickaccesswallet/IQuickAccessWalletService.aidl b/core/java/android/service/quickaccesswallet/IQuickAccessWalletService.aidl
index 0dca78d..03e27b8 100644
--- a/core/java/android/service/quickaccesswallet/IQuickAccessWalletService.aidl
+++ b/core/java/android/service/quickaccesswallet/IQuickAccessWalletService.aidl
@@ -43,4 +43,6 @@
oneway void unregisterWalletServiceEventListener(in WalletServiceEventListenerRequest request);
// Request to get a PendingIntent to launch an activity from which the user can manage their cards.
oneway void onTargetActivityIntentRequested(in IQuickAccessWalletServiceCallbacks callbacks);
+ // Request to get a PendingIntent to launch an activity, triggered when the user performs a gesture.
+ oneway void onGestureTargetActivityIntentRequested(in IQuickAccessWalletServiceCallbacks callbacks);
}
\ No newline at end of file
diff --git a/core/java/android/service/quickaccesswallet/IQuickAccessWalletServiceCallbacks.aidl b/core/java/android/service/quickaccesswallet/IQuickAccessWalletServiceCallbacks.aidl
index 1b69ca1..61d7fd1 100644
--- a/core/java/android/service/quickaccesswallet/IQuickAccessWalletServiceCallbacks.aidl
+++ b/core/java/android/service/quickaccesswallet/IQuickAccessWalletServiceCallbacks.aidl
@@ -37,4 +37,6 @@
oneway void onWalletServiceEvent(in WalletServiceEvent event);
// Called in response to onTargetActivityIntentRequested. May only be called once per request.
oneway void onTargetActivityPendingIntentReceived(in PendingIntent pendingIntent);
+ //Called in response to onGesturePendingIntent
+ oneway void onGestureTargetActivityPendingIntentReceived(in PendingIntent pendingIntent);
}
\ No newline at end of file
diff --git a/core/java/android/service/quickaccesswallet/QuickAccessWalletClient.java b/core/java/android/service/quickaccesswallet/QuickAccessWalletClient.java
index faa5b2f..b5251db 100644
--- a/core/java/android/service/quickaccesswallet/QuickAccessWalletClient.java
+++ b/core/java/android/service/quickaccesswallet/QuickAccessWalletClient.java
@@ -17,6 +17,7 @@
package android.service.quickaccesswallet;
import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.TestApi;
@@ -181,6 +182,23 @@
}
/**
+ * Gets the {@link PendingIntent} provided by QuickAccessWalletService to be sent when the user
+ * launches Wallet via gesture.
+ */
+ @FlaggedApi(Flags.FLAG_LAUNCH_WALLET_OPTION_ON_POWER_DOUBLE_TAP)
+ void getGestureTargetActivityPendingIntent(
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull GesturePendingIntentCallback gesturePendingIntentCallback);
+
+ /** Callback interface for getGesturePendingIntent. */
+ @FlaggedApi(Flags.FLAG_LAUNCH_WALLET_OPTION_ON_POWER_DOUBLE_TAP)
+ interface GesturePendingIntentCallback {
+ /** Callback method for getGesturePendingIntent */
+ @FlaggedApi(Flags.FLAG_LAUNCH_WALLET_OPTION_ON_POWER_DOUBLE_TAP)
+ void onGesturePendingIntentRetrieved(@Nullable PendingIntent pendingIntent);
+ }
+
+ /**
* The manifest entry for the QuickAccessWalletService may also publish information about the
* activity that hosts the Wallet view. This is typically the home screen of the Wallet
* application.
diff --git a/core/java/android/service/quickaccesswallet/QuickAccessWalletClientImpl.java b/core/java/android/service/quickaccesswallet/QuickAccessWalletClientImpl.java
index a59f026..97a4bef 100644
--- a/core/java/android/service/quickaccesswallet/QuickAccessWalletClientImpl.java
+++ b/core/java/android/service/quickaccesswallet/QuickAccessWalletClientImpl.java
@@ -267,6 +267,34 @@
}
@Override
+ public void getGestureTargetActivityPendingIntent(
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull GesturePendingIntentCallback gesturePendingIntentCallback) {
+ BaseCallbacks callbacks =
+ new BaseCallbacks() {
+ @Override
+ public void onGestureTargetActivityPendingIntentReceived(
+ PendingIntent pendingIntent) {
+ if (!Flags.launchWalletOptionOnPowerDoubleTap()) {
+ return;
+ }
+ executor.execute(
+ () ->
+ gesturePendingIntentCallback
+ .onGesturePendingIntentRetrieved(pendingIntent));
+ }
+ };
+
+ executeApiCall(
+ new ApiCaller("getGestureTargetActivityPendingIntent") {
+ @Override
+ void performApiCall(IQuickAccessWalletService service) throws RemoteException {
+ service.onGestureTargetActivityIntentRequested(callbacks);
+ }
+ });
+ }
+
+ @Override
@Nullable
public Intent createWalletSettingsIntent() {
if (mServiceInfo == null) {
@@ -506,5 +534,9 @@
public void onTargetActivityPendingIntentReceived(PendingIntent pendingIntent) {
throw new IllegalStateException();
}
+
+ public void onGestureTargetActivityPendingIntentReceived(PendingIntent pendingIntent) {
+ throw new IllegalStateException();
+ }
}
}
diff --git a/core/java/android/service/quickaccesswallet/QuickAccessWalletService.java b/core/java/android/service/quickaccesswallet/QuickAccessWalletService.java
index 36fa21c..90136ae 100644
--- a/core/java/android/service/quickaccesswallet/QuickAccessWalletService.java
+++ b/core/java/android/service/quickaccesswallet/QuickAccessWalletService.java
@@ -16,6 +16,9 @@
package android.service.quickaccesswallet;
+import static android.service.quickaccesswallet.Flags.launchWalletOptionOnPowerDoubleTap;
+
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SdkConstant;
@@ -247,6 +250,19 @@
callbacks));
}
+ @FlaggedApi(Flags.FLAG_LAUNCH_WALLET_OPTION_ON_POWER_DOUBLE_TAP)
+ @Override
+ public void onGestureTargetActivityIntentRequested(
+ @NonNull IQuickAccessWalletServiceCallbacks callbacks) {
+ if (launchWalletOptionOnPowerDoubleTap()) {
+ mHandler.post(
+ () ->
+ QuickAccessWalletService.this
+ .onGestureTargetActivityIntentRequestedInternal(
+ callbacks));
+ }
+ }
+
public void registerWalletServiceEventListener(
@NonNull WalletServiceEventListenerRequest request,
@NonNull IQuickAccessWalletServiceCallbacks callback) {
@@ -275,6 +291,20 @@
}
}
+ private void onGestureTargetActivityIntentRequestedInternal(
+ IQuickAccessWalletServiceCallbacks callbacks) {
+ if (!Flags.launchWalletOptionOnPowerDoubleTap()) {
+ return;
+ }
+
+ try {
+ callbacks.onGestureTargetActivityPendingIntentReceived(
+ getGestureTargetActivityPendingIntent());
+ } catch (RemoteException e) {
+ Log.w(TAG, "Error", e);
+ }
+ }
+
@Override
@Nullable
public IBinder onBind(@NonNull Intent intent) {
@@ -349,6 +379,18 @@
return null;
}
+ /**
+ * Specify a {@link PendingIntent} to be launched on user gesture.
+ *
+ * <p>The pending intent will be sent when the user performs a gesture to open Wallet.
+ * The pending intent should launch an activity.
+ */
+ @Nullable
+ @FlaggedApi(Flags.FLAG_LAUNCH_WALLET_OPTION_ON_POWER_DOUBLE_TAP)
+ public PendingIntent getGestureTargetActivityPendingIntent() {
+ return null;
+ }
+
private void sendWalletServiceEventInternal(WalletServiceEvent serviceEvent) {
if (mEventListener == null) {
Log.i(TAG, "No dismiss listener registered");
diff --git a/core/java/android/service/quickaccesswallet/flags.aconfig b/core/java/android/service/quickaccesswallet/flags.aconfig
index 07311d5..75a9309 100644
--- a/core/java/android/service/quickaccesswallet/flags.aconfig
+++ b/core/java/android/service/quickaccesswallet/flags.aconfig
@@ -3,7 +3,7 @@
flag {
name: "launch_wallet_option_on_power_double_tap"
- namespace: "wallet_integrations"
+ namespace: "wallet_integration"
description: "Option to launch the Wallet app on double-tap of the power button"
bug: "378469025"
}
\ No newline at end of file
diff --git a/core/java/android/telephony/PhoneStateListener.java b/core/java/android/telephony/PhoneStateListener.java
index 1df3b43..c16a510 100644
--- a/core/java/android/telephony/PhoneStateListener.java
+++ b/core/java/android/telephony/PhoneStateListener.java
@@ -1712,6 +1712,15 @@
@NonNull NtnSignalStrength ntnSignalStrength) {
// not supported on the deprecated interface - Use TelephonyCallback instead
}
+
+ public final void onSecurityAlgorithmsChanged(SecurityAlgorithmUpdate update) {
+ // not supported on the deprecated interface - Use TelephonyCallback instead
+ }
+
+ public final void onCellularIdentifierDisclosedChanged(
+ CellularIdentifierDisclosure disclosure) {
+ // not supported on the deprecated interface - Use TelephonyCallback instead
+ }
}
private void log(String s) {
diff --git a/core/java/android/telephony/SubscriptionPlan.java b/core/java/android/telephony/SubscriptionPlan.java
index 7b48a16..4c59a85 100644
--- a/core/java/android/telephony/SubscriptionPlan.java
+++ b/core/java/android/telephony/SubscriptionPlan.java
@@ -18,6 +18,7 @@
import android.annotation.BytesLong;
import android.annotation.CurrentTimeMillisLong;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -28,6 +29,7 @@
import android.util.Range;
import android.util.RecurrenceRule;
+import com.android.internal.telephony.flags.Flags;
import com.android.internal.util.Preconditions;
import java.lang.annotation.Retention;
@@ -83,6 +85,33 @@
/** Value indicating a timestamp is unknown. */
public static final long TIME_UNKNOWN = -1;
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = { "SUBSCRIPTION_STATUS_" }, value = {
+ SUBSCRIPTION_STATUS_UNKNOWN,
+ SUBSCRIPTION_STATUS_ACTIVE,
+ SUBSCRIPTION_STATUS_INACTIVE,
+ SUBSCRIPTION_STATUS_TRIAL,
+ SUBSCRIPTION_STATUS_SUSPENDED
+ })
+ public @interface SubscriptionStatus {}
+
+ /** Subscription status is unknown. */
+ @FlaggedApi(Flags.FLAG_SUBSCRIPTION_PLAN_ALLOW_STATUS_AND_END_DATE)
+ public static final int SUBSCRIPTION_STATUS_UNKNOWN = 0;
+ /** Subscription is active. */
+ @FlaggedApi(Flags.FLAG_SUBSCRIPTION_PLAN_ALLOW_STATUS_AND_END_DATE)
+ public static final int SUBSCRIPTION_STATUS_ACTIVE = 1;
+ /** Subscription is inactive. */
+ @FlaggedApi(Flags.FLAG_SUBSCRIPTION_PLAN_ALLOW_STATUS_AND_END_DATE)
+ public static final int SUBSCRIPTION_STATUS_INACTIVE = 2;
+ /** Subscription is in a trial period. */
+ @FlaggedApi(Flags.FLAG_SUBSCRIPTION_PLAN_ALLOW_STATUS_AND_END_DATE)
+ public static final int SUBSCRIPTION_STATUS_TRIAL = 3;
+ /** Subscription is suspended. */
+ @FlaggedApi(Flags.FLAG_SUBSCRIPTION_PLAN_ALLOW_STATUS_AND_END_DATE)
+ public static final int SUBSCRIPTION_STATUS_SUSPENDED = 4;
+
private final RecurrenceRule cycleRule;
private CharSequence title;
private CharSequence summary;
@@ -91,6 +120,7 @@
private long dataUsageBytes = BYTES_UNKNOWN;
private long dataUsageTime = TIME_UNKNOWN;
private @NetworkType int[] networkTypes;
+ private int mSubscriptionStatus = SUBSCRIPTION_STATUS_UNKNOWN;
private SubscriptionPlan(RecurrenceRule cycleRule) {
this.cycleRule = Preconditions.checkNotNull(cycleRule);
@@ -107,6 +137,7 @@
dataUsageBytes = source.readLong();
dataUsageTime = source.readLong();
networkTypes = source.createIntArray();
+ mSubscriptionStatus = source.readInt();
}
@Override
@@ -124,6 +155,7 @@
dest.writeLong(dataUsageBytes);
dest.writeLong(dataUsageTime);
dest.writeIntArray(networkTypes);
+ dest.writeInt(mSubscriptionStatus);
}
@Override
@@ -137,13 +169,14 @@
.append(" dataUsageBytes=").append(dataUsageBytes)
.append(" dataUsageTime=").append(dataUsageTime)
.append(" networkTypes=").append(Arrays.toString(networkTypes))
+ .append(" subscriptionStatus=").append(mSubscriptionStatus)
.append("}").toString();
}
@Override
public int hashCode() {
return Objects.hash(cycleRule, title, summary, dataLimitBytes, dataLimitBehavior,
- dataUsageBytes, dataUsageTime, Arrays.hashCode(networkTypes));
+ dataUsageBytes, dataUsageTime, Arrays.hashCode(networkTypes), mSubscriptionStatus);
}
@Override
@@ -157,7 +190,8 @@
&& dataLimitBehavior == other.dataLimitBehavior
&& dataUsageBytes == other.dataUsageBytes
&& dataUsageTime == other.dataUsageTime
- && Arrays.equals(networkTypes, other.networkTypes);
+ && Arrays.equals(networkTypes, other.networkTypes)
+ && mSubscriptionStatus == other.mSubscriptionStatus;
}
return false;
}
@@ -179,6 +213,13 @@
return cycleRule;
}
+ /** Return the end date of this plan, or null if no end date exists. */
+ @FlaggedApi(Flags.FLAG_SUBSCRIPTION_PLAN_ALLOW_STATUS_AND_END_DATE)
+ public @Nullable ZonedDateTime getPlanEndDate() {
+ // ZonedDateTime is immutable, so no need to create a defensive copy.
+ return cycleRule.end;
+ }
+
/** Return the short title of this plan. */
public @Nullable CharSequence getTitle() {
return title;
@@ -238,6 +279,16 @@
}
/**
+ * Returns the status of the subscription plan.
+ *
+ * @return The subscription status, or {@link #SUBSCRIPTION_STATUS_UNKNOWN} if not available.
+ */
+ @FlaggedApi(Flags.FLAG_SUBSCRIPTION_PLAN_ALLOW_STATUS_AND_END_DATE)
+ public @SubscriptionStatus int getSubscriptionStatus() {
+ return mSubscriptionStatus;
+ }
+
+ /**
* Builder for a {@link SubscriptionPlan}.
*/
public static class Builder {
@@ -382,5 +433,21 @@
TelephonyManager.getAllNetworkTypes().length);
return this;
}
+
+ /**
+ * Set the subscription status.
+ *
+ * @param subscriptionStatus the current subscription status
+ */
+ @FlaggedApi(Flags.FLAG_SUBSCRIPTION_PLAN_ALLOW_STATUS_AND_END_DATE)
+ public @NonNull Builder setSubscriptionStatus(@SubscriptionStatus int subscriptionStatus) {
+ if (subscriptionStatus < SUBSCRIPTION_STATUS_UNKNOWN
+ || subscriptionStatus > SUBSCRIPTION_STATUS_SUSPENDED) {
+ throw new IllegalArgumentException(
+ "Subscription status must be defined with a valid value");
+ }
+ plan.mSubscriptionStatus = subscriptionStatus;
+ return this;
+ }
}
}
diff --git a/core/java/android/telephony/TelephonyCallback.java b/core/java/android/telephony/TelephonyCallback.java
index 0d1dc46..2c585e6 100644
--- a/core/java/android/telephony/TelephonyCallback.java
+++ b/core/java/android/telephony/TelephonyCallback.java
@@ -705,6 +705,28 @@
public static final int EVENT_CARRIER_ROAMING_NTN_SIGNAL_STRENGTH_CHANGED = 45;
/**
+ * Event for changes to mobile network ciphering algorithms.
+ * See {@link SecurityAlgorithmsListener#onSecurityAlgorithmsChanged}
+ *
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_CELLULAR_IDENTIFIER_DISCLOSURE_INDICATIONS)
+ @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @SystemApi
+ public static final int EVENT_SECURITY_ALGORITHMS_CHANGED = 46;
+
+ /**
+ * Event for updates to sensitive device identifier disclosures (IMSI, IMEI, unciphered SUCI).
+ * See {@link CellularIdentifierDisclosedListener#onCellularIdentifierDisclosedChanged}
+ *
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_CELLULAR_IDENTIFIER_DISCLOSURE_INDICATIONS)
+ @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @SystemApi
+ public static final int EVENT_CELLULAR_IDENTIFIER_DISCLOSED_CHANGED = 47;
+
+ /**
* @hide
*/
@IntDef(prefix = {"EVENT_"}, value = {
@@ -752,7 +774,9 @@
EVENT_CARRIER_ROAMING_NTN_MODE_CHANGED,
EVENT_CARRIER_ROAMING_NTN_ELIGIBLE_STATE_CHANGED,
EVENT_CARRIER_ROAMING_NTN_AVAILABLE_SERVICES_CHANGED,
- EVENT_CARRIER_ROAMING_NTN_SIGNAL_STRENGTH_CHANGED
+ EVENT_CARRIER_ROAMING_NTN_SIGNAL_STRENGTH_CHANGED,
+ EVENT_SECURITY_ALGORITHMS_CHANGED,
+ EVENT_CELLULAR_IDENTIFIER_DISCLOSED_CHANGED
})
@Retention(RetentionPolicy.SOURCE)
public @interface TelephonyEvent {
@@ -1827,6 +1851,41 @@
}
/**
+ * Interface for CellularIdentifierDisclosedListener
+ * @hide
+ */
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_CELLULAR_IDENTIFIER_DISCLOSURE_INDICATIONS)
+ public interface CellularIdentifierDisclosedListener {
+ /**
+ * Callback invoked when a device identifier (IMSI, IMEI, or unciphered SUCI)
+ * is disclosed over the network before a security context is established
+ * ("pre-authentication").
+ *
+ * @param disclosure details of the identifier disclosure
+ * See {@link CellularIdentifierDisclosure} for more details
+ */
+ void onCellularIdentifierDisclosedChanged(@NonNull CellularIdentifierDisclosure disclosure);
+ }
+
+ /**
+ * Interface for SecurityAlgorithmsListener
+ * @hide
+ */
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_SECURITY_ALGORITHMS_UPDATE_INDICATIONS)
+ public interface SecurityAlgorithmsListener {
+ /**
+ * Callback invoked when the most recently reported security algorithms has changed,
+ * per a specified connection event.
+ *
+ * @param securityAlgorithmUpdate details of the security algorithm update
+ * See {@link SecurityAlgorithmUpdate} for more details
+ */
+ void onSecurityAlgorithmsChanged(@NonNull SecurityAlgorithmUpdate securityAlgorithmUpdate);
+ }
+
+ /**
* The callback methods need to be called on the handler thread where
* this object was created. If the binder did that for us it'd be nice.
* <p>
@@ -2302,5 +2361,27 @@
() -> listener.onCarrierRoamingNtnSignalStrengthChanged(ntnSignalStrength)));
}
+
+ public void onSecurityAlgorithmsChanged(SecurityAlgorithmUpdate update) {
+ if (!Flags.securityAlgorithmsUpdateIndications()) return;
+
+ SecurityAlgorithmsListener listener =
+ (SecurityAlgorithmsListener) mTelephonyCallbackWeakRef.get();
+ if (listener == null) return;
+
+ Binder.withCleanCallingIdentity(() -> mExecutor.execute(
+ () -> listener.onSecurityAlgorithmsChanged(update)));
+ }
+
+ public void onCellularIdentifierDisclosedChanged(CellularIdentifierDisclosure disclosure) {
+ if (!Flags.cellularIdentifierDisclosureIndications()) return;
+
+ CellularIdentifierDisclosedListener listener =
+ (CellularIdentifierDisclosedListener) mTelephonyCallbackWeakRef.get();
+ if (listener == null) return;
+
+ Binder.withCleanCallingIdentity(() -> mExecutor.execute(
+ () -> listener.onCellularIdentifierDisclosedChanged(disclosure)));
+ }
}
}
diff --git a/core/java/android/telephony/TelephonyRegistryManager.java b/core/java/android/telephony/TelephonyRegistryManager.java
index 90b0bb3..4ec429d 100644
--- a/core/java/android/telephony/TelephonyRegistryManager.java
+++ b/core/java/android/telephony/TelephonyRegistryManager.java
@@ -1154,6 +1154,40 @@
}
}
+ /**
+ * Notify external listeners that the radio security algorithms have changed.
+ * @param slotIndex for the phone object that got updated
+ * @param subId for which the security algorithm changed
+ * @param update details of the security algorithm update
+ * @hide
+ */
+ public void notifySecurityAlgorithmsChanged(
+ int slotIndex, int subId, SecurityAlgorithmUpdate update) {
+ try {
+ sRegistry.notifySecurityAlgorithmsChanged(slotIndex, subId, update);
+ } catch (RemoteException ex) {
+ // system server crash
+ throw ex.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Notify external listeners of a new cellular identifier disclosure change.
+ * @param slotIndex for the phone object that the disclosure applies to
+ * @param subId for which the disclosure applies to
+ * @param disclosure details of the identifier disclosure
+ * @hide
+ */
+ public void notifyCellularIdentifierDisclosedChanged(
+ int slotIndex, int subId, CellularIdentifierDisclosure disclosure) {
+ try {
+ sRegistry.notifyCellularIdentifierDisclosedChanged(slotIndex, subId, disclosure);
+ } catch (RemoteException ex) {
+ // system server crash
+ throw ex.rethrowFromSystemServer();
+ }
+ }
+
/**
* Processes potential event changes from the provided {@link TelephonyCallback}.
*
@@ -1313,6 +1347,15 @@
eventList.add(TelephonyCallback.EVENT_CARRIER_ROAMING_NTN_AVAILABLE_SERVICES_CHANGED);
eventList.add(TelephonyCallback.EVENT_CARRIER_ROAMING_NTN_SIGNAL_STRENGTH_CHANGED);
}
+
+ if (telephonyCallback instanceof TelephonyCallback.CellularIdentifierDisclosedListener) {
+ eventList.add(TelephonyCallback.EVENT_CELLULAR_IDENTIFIER_DISCLOSED_CHANGED);
+ }
+
+ if (telephonyCallback instanceof TelephonyCallback.SecurityAlgorithmsListener) {
+ eventList.add(TelephonyCallback.EVENT_SECURITY_ALGORITHMS_CHANGED);
+ }
+
return eventList;
}
diff --git a/core/java/android/text/TextFlags.java b/core/java/android/text/TextFlags.java
deleted file mode 100644
index f69a333..0000000
--- a/core/java/android/text/TextFlags.java
+++ /dev/null
@@ -1,81 +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.text;
-
-import android.annotation.NonNull;
-import android.app.AppGlobals;
-
-/**
- * Flags in the "text" namespace.
- *
- * TODO(nona): Remove this class.
- * @hide
- */
-public final class TextFlags {
-
- /**
- * The name space of the "text" feature.
- *
- * This needs to move to DeviceConfig constant.
- */
- public static final String NAMESPACE = "text";
-
- /**
- * Whether we use the new design of context menu.
- */
- public static final String ENABLE_NEW_CONTEXT_MENU =
- "TextEditing__enable_new_context_menu";
-
- /**
- * The key name used in app core settings for {@link #ENABLE_NEW_CONTEXT_MENU}.
- */
- public static final String KEY_ENABLE_NEW_CONTEXT_MENU = "text__enable_new_context_menu";
-
- /**
- * Default value for the flag {@link #ENABLE_NEW_CONTEXT_MENU}.
- */
- public static final boolean ENABLE_NEW_CONTEXT_MENU_DEFAULT = true;
-
- /**
- * List of text flags to be transferred to the application process.
- */
- public static final String[] TEXT_ACONFIGS_FLAGS = {
- };
-
- /**
- * 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 = {
- };
-
- /**
- * Get a key for the feature flag.
- */
- public static String getKeyForFlag(@NonNull String flag) {
- return "text__" + flag;
- }
-
- /**
- * Return true if the feature flag is enabled.
- */
- public static boolean isFeatureEnabled(@NonNull String flag) {
- return AppGlobals.getIntCoreSetting(
- getKeyForFlag(flag), 0 /* aconfig is false by default */) != 0;
- }
-}
diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java
index 032f592..cb72b97 100644
--- a/core/java/android/text/TextUtils.java
+++ b/core/java/android/text/TextUtils.java
@@ -50,6 +50,7 @@
import android.text.style.LineBreakConfigSpan;
import android.text.style.LineHeightSpan;
import android.text.style.LocaleSpan;
+import android.text.style.NoWritingToolsSpan;
import android.text.style.ParagraphStyle;
import android.text.style.QuoteSpan;
import android.text.style.RelativeSizeSpan;
@@ -817,7 +818,9 @@
/** @hide */
public static final int LINE_BREAK_CONFIG_SPAN = 30;
/** @hide */
- public static final int LAST_SPAN = LINE_BREAK_CONFIG_SPAN;
+ public static final int NO_WRITING_TOOLS_SPAN = 31;
+ /** @hide */
+ public static final int LAST_SPAN = NO_WRITING_TOOLS_SPAN;
/**
* Flatten a CharSequence and whatever styles can be copied across processes
@@ -1025,6 +1028,10 @@
span = LineBreakConfigSpan.CREATOR.createFromParcel(p);
break;
+ case NO_WRITING_TOOLS_SPAN:
+ span = NoWritingToolsSpan.CREATOR.createFromParcel(p);
+ break;
+
default:
throw new RuntimeException("bogus span encoding " + kind);
}
diff --git a/core/java/android/text/style/NoWritingToolsSpan.java b/core/java/android/text/style/NoWritingToolsSpan.java
new file mode 100644
index 0000000..90f85aa
--- /dev/null
+++ b/core/java/android/text/style/NoWritingToolsSpan.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.text.style;
+
+import static android.view.inputmethod.Flags.FLAG_WRITING_TOOLS;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.text.ParcelableSpan;
+import android.text.TextUtils;
+
+/**
+ * A span that signals to IMEs that writing tools should not modify the text.
+ *
+ * <p>For example, a text field may contain a mix of user input text and quoted text. The app
+ * can apply {@code NoWritingToolsSpan} to the quoted text so that the IME knows that writing
+ * tools should only rewrite the user input text, and not modify the quoted text.
+ */
+@FlaggedApi(FLAG_WRITING_TOOLS)
+public final class NoWritingToolsSpan implements ParcelableSpan {
+
+ /**
+ * Creates a {@link NoWritingToolsSpan}.
+ */
+ public NoWritingToolsSpan() {
+ }
+
+ @Override
+ public int getSpanTypeId() {
+ return getSpanTypeIdInternal();
+ }
+
+ /** @hide */
+ @Override
+ public int getSpanTypeIdInternal() {
+ return TextUtils.NO_WRITING_TOOLS_SPAN;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ writeToParcelInternal(dest, flags);
+ }
+
+ /** @hide */
+ @Override
+ public void writeToParcelInternal(@NonNull Parcel dest, int flags) {
+ }
+
+ @Override
+ public String toString() {
+ return "NoWritingToolsSpan{}";
+ }
+
+ @NonNull
+ public static final Creator<NoWritingToolsSpan> CREATOR = new Creator<>() {
+
+ @Override
+ public NoWritingToolsSpan createFromParcel(Parcel source) {
+ return new NoWritingToolsSpan();
+ }
+
+ @Override
+ public NoWritingToolsSpan[] newArray(int size) {
+ return new NoWritingToolsSpan[size];
+ }
+ };
+}
diff --git a/core/java/android/view/Choreographer.java b/core/java/android/view/Choreographer.java
index 5a71282..8cb96ae 100644
--- a/core/java/android/view/Choreographer.java
+++ b/core/java/android/view/Choreographer.java
@@ -930,8 +930,8 @@
// of the next vsync event.
int totalFrameDelays = mBufferStuffingData.numberFrameDelays
+ mBufferStuffingData.numberWaitsForNextVsync + 1;
- long vsyncsSinceLastCallback =
- (frameTimeNanos - mLastNoOffsetFrameTimeNanos) / mLastFrameIntervalNanos;
+ long vsyncsSinceLastCallback = mLastFrameIntervalNanos > 0
+ ? (frameTimeNanos - mLastNoOffsetFrameTimeNanos) / mLastFrameIntervalNanos : 0;
// Detected idle state due to a longer inactive period since the last vsync callback
// than the total expected number of vsync frame delays. End buffer stuffing recovery.
diff --git a/core/java/android/view/ScaleGestureDetector.java b/core/java/android/view/ScaleGestureDetector.java
index 5a28d5f..80ae3c3 100644
--- a/core/java/android/view/ScaleGestureDetector.java
+++ b/core/java/android/view/ScaleGestureDetector.java
@@ -437,16 +437,16 @@
}
}
- /**
- * Return whether the quick scale gesture, in which the user performs a double tap followed by a
- * swipe, should perform scaling. {@see #setQuickScaleEnabled(boolean)}.
- */
+ /**
+ * Return whether the quick scale gesture, in which the user performs a double tap followed by a
+ * swipe, should perform scaling. {@see #setQuickScaleEnabled(boolean)}.
+ */
public boolean isQuickScaleEnabled() {
return mQuickScaleEnabled;
}
/**
- * Sets whether the associates {@link OnScaleGestureListener} should receive
+ * Sets whether the associated {@link OnScaleGestureListener} should receive
* onScale callbacks when the user uses a stylus and presses the button.
* Note that this is enabled by default if the app targets API 23 and newer.
*
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 68efa79..d56768d 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -4566,14 +4566,31 @@
return this;
}
- /** @hide */
+ /**
+ * Sets the Luts for the layer.
+ *
+ * <p> The function also allows to clear previously applied lut(s). To do this,
+ * set the displayluts to be either {@code nullptr} or
+ * an empty {@link android.hardware.DisplayLuts} instance.
+ *
+ * @param sc The SurfaceControl to update
+ *
+ * @param displayLuts The selected Lut(s)
+ *
+ * @return this
+ * @see DisplayLuts
+ */
+ @FlaggedApi(android.hardware.flags.Flags.FLAG_LUTS_API)
public @NonNull Transaction setLuts(@NonNull SurfaceControl sc,
- @NonNull DisplayLuts displayLuts) {
+ @Nullable DisplayLuts displayLuts) {
checkPreconditions(sc);
-
- nativeSetLuts(mNativeObject, sc.mNativeObject, displayLuts.getLutBuffers(),
- displayLuts.getOffsets(), displayLuts.getLutDimensions(),
- displayLuts.getLutSizes(), displayLuts.getLutSamplingKeys());
+ if (displayLuts != null && displayLuts.valid()) {
+ nativeSetLuts(mNativeObject, sc.mNativeObject, displayLuts.getLutBuffers(),
+ displayLuts.getOffsets(), displayLuts.getLutDimensions(),
+ displayLuts.getLutSizes(), displayLuts.getLutSamplingKeys());
+ } else {
+ nativeSetLuts(mNativeObject, sc.mNativeObject, null, null, null, null, null);
+ }
return this;
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index e50662a..19d3dc4 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -2516,6 +2516,11 @@
public void notifyInsetsAnimationRunningStateChanged(boolean running) {
if (sToolkitSetFrameRateReadOnlyFlagValue) {
mInsetsAnimationRunning = running;
+ if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
+ Trace.instant(Trace.TRACE_TAG_VIEW,
+ TextUtils.formatSimple("notifyInsetsAnimationRunningStateChanged(%s)",
+ Boolean.toString(running)));
+ }
}
}
diff --git a/core/java/android/view/ViewStructure.java b/core/java/android/view/ViewStructure.java
index 1af9387..1be7f48 100644
--- a/core/java/android/view/ViewStructure.java
+++ b/core/java/android/view/ViewStructure.java
@@ -17,6 +17,7 @@
package android.view;
import static android.service.autofill.Flags.FLAG_AUTOFILL_CREDMAN_DEV_INTEGRATION;
+import static android.service.autofill.Flags.FLAG_AUTOFILL_W_METRICS;
import android.annotation.FlaggedApi;
import android.annotation.NonNull;
@@ -79,27 +80,24 @@
* Key used for writing the type of the view that generated the virtual structure of its
* children.
*
- * For example, if the virtual structure is generated by a webview, the value would be
+ * <p>For example, if the virtual structure is generated by a webview, the value would be
* "WebView". If the virtual structure is generated by a compose view, then the value would be
* "ComposeView". The value is of type String.
*
- * This value is added to mainly help with debugging purpose.
- *
- * @hide
+ * <p>This value is added to mainly help with debugging purpose.
*/
+ @FlaggedApi(FLAG_AUTOFILL_W_METRICS)
public static final String EXTRA_VIRTUAL_STRUCTURE_TYPE =
"android.view.ViewStructure.extra.VIRTUAL_STRUCTURE_TYPE";
-
/**
* Key used for specifying the version of the view that generated the virtual structure for
* itself and its children
*
- * For example, if the virtual structure is generated by a webview of version "104.0.5112.69",
- * then the value should be "104.0.5112.69"
- *
- * @hide
+ * <p>For example, if the virtual structure is generated by a webview of version
+ * "104.0.5112.69", then the value should be "104.0.5112.69"
*/
+ @FlaggedApi(FLAG_AUTOFILL_W_METRICS)
public static final String EXTRA_VIRTUAL_STRUCTURE_VERSION_NUMBER =
"android.view.ViewStructure.extra.VIRTUAL_STRUCTURE_VERSION_NUMBER";
diff --git a/core/java/android/view/autofill/AutofillId.java b/core/java/android/view/autofill/AutofillId.java
index 6b60858..bd27778 100644
--- a/core/java/android/view/autofill/AutofillId.java
+++ b/core/java/android/view/autofill/AutofillId.java
@@ -15,6 +15,9 @@
*/
package android.view.autofill;
+import static android.service.autofill.Flags.FLAG_AUTOFILL_W_METRICS;
+
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.TestApi;
@@ -111,12 +114,43 @@
return new AutofillId(flags, id.mViewId, virtualChildId, NO_SESSION);
}
- /** @hide */
+ /**
+ * Returns the assigned unique identifier of this AutofillID.
+ *
+ * See @link{android.view.View#getAutofillId()} for more information on
+ * how this is generated for native Views.
+ */
+ @FlaggedApi(FLAG_AUTOFILL_W_METRICS)
public int getViewId() {
return mViewId;
}
/**
+ * Gets the virtual id. This is set if the view is a virtual view, most commonly set if the View
+ * is of {@link android.webkit.WebView}.
+ */
+ @FlaggedApi(FLAG_AUTOFILL_W_METRICS)
+ public int getAutofillVirtualId() {
+ return mVirtualIntId;
+ }
+
+ /** Checks whether this AutofillId represents a virtual view. */
+ @FlaggedApi(FLAG_AUTOFILL_W_METRICS)
+ public boolean isVirtual() {
+ return !isNonVirtual();
+ }
+
+ /**
+ * Checks if this node is generate as part of a {@link android.app.assist.AssistStructure}. This
+ * will usually return true if it should be used by an autofill service provider, and false
+ * otherwise.
+ */
+ @FlaggedApi(FLAG_AUTOFILL_W_METRICS)
+ public boolean isInAutofillSession() {
+ return hasSession();
+ }
+
+ /**
* Gets the virtual child id.
*
* <p>Should only be used on subsystems where such id is represented by an {@code int}
@@ -181,7 +215,12 @@
return (mFlags & FLAG_HAS_SESSION) != 0;
}
- /** @hide */
+ /**
+ * Used to get the Session identifier associated with this AutofillId.
+ *
+ * @return a non-zero integer if {@link #isInAutofillSession()} returns true
+ */
+ @FlaggedApi(FLAG_AUTOFILL_W_METRICS)
public int getSessionId() {
return mSessionId;
}
diff --git a/core/java/android/view/flags/scroll_feedback_flags.aconfig b/core/java/android/view/flags/scroll_feedback_flags.aconfig
index b180e58..ebda4d4 100644
--- a/core/java/android/view/flags/scroll_feedback_flags.aconfig
+++ b/core/java/android/view/flags/scroll_feedback_flags.aconfig
@@ -28,5 +28,5 @@
namespace: "wear_frameworks"
name: "dynamic_view_rotary_haptics_configuration"
description: "Whether ScrollFeedbackProvider dynamically disables View-based rotary haptics."
- bug: "377998870 "
+ bug: "377998870"
}
diff --git a/core/java/android/view/inputmethod/EditorInfo.java b/core/java/android/view/inputmethod/EditorInfo.java
index a560339..afe195c 100644
--- a/core/java/android/view/inputmethod/EditorInfo.java
+++ b/core/java/android/view/inputmethod/EditorInfo.java
@@ -523,7 +523,6 @@
@Nullable
public LocaleList hintLocales = null;
-
/**
* List of acceptable MIME types for
* {@link InputConnection#commitContent(InputContentInfo, int, Bundle)}.
@@ -758,6 +757,30 @@
return mIsStylusHandwritingEnabled;
}
+ private boolean mWritingToolsEnabled = true;
+
+ /**
+ * Returns {@code true} when an {@code Editor} has writing tools enabled.
+ * {@code true} by default for all editors. Toolkits can optionally disable them where not
+ * relevant e.g. passwords, number input, etc.
+ * @see #setWritingToolsEnabled(boolean)
+ */
+ @FlaggedApi(Flags.FLAG_WRITING_TOOLS)
+ public boolean isWritingToolsEnabled() {
+ return mWritingToolsEnabled;
+ }
+
+ /**
+ * Set {@code false} if {@code Editor} opts-out of writing tools, that enable IMEs to replace
+ * text with generative AI text.
+ * @param enabled set {@code true} to enabled or {@code false to disable} support.
+ * @see #isWritingToolsEnabled()
+ */
+ @FlaggedApi(Flags.FLAG_WRITING_TOOLS)
+ public void setWritingToolsEnabled(boolean enabled) {
+ mWritingToolsEnabled = enabled;
+ }
+
/**
* If not {@code null}, this editor needs to talk to IMEs that run for the specified user, no
* matter what user ID the calling process has.
@@ -1276,6 +1299,7 @@
+ InputMethodDebug.handwritingGestureTypeFlagsToString(
mSupportedHandwritingGesturePreviewTypes));
pw.println(prefix + "isStylusHandwritingEnabled=" + mIsStylusHandwritingEnabled);
+ pw.println(prefix + "writingToolsEnabled=" + mWritingToolsEnabled);
pw.println(prefix + "contentMimeTypes=" + Arrays.toString(contentMimeTypes));
if (targetInputMethodUser != null) {
pw.println(prefix + "targetInputMethodUserId=" + targetInputMethodUser.getIdentifier());
@@ -1356,6 +1380,7 @@
}
dest.writeStringArray(contentMimeTypes);
UserHandle.writeToParcel(targetInputMethodUser, dest);
+ dest.writeBoolean(mWritingToolsEnabled);
}
/**
@@ -1396,6 +1421,7 @@
res.hintLocales = hintLocales.isEmpty() ? null : hintLocales;
res.contentMimeTypes = source.readStringArray();
res.targetInputMethodUser = UserHandle.readFromParcel(source);
+ res.mWritingToolsEnabled = source.readBoolean();
return res;
}
diff --git a/core/java/android/view/inputmethod/flags.aconfig b/core/java/android/view/inputmethod/flags.aconfig
index e619ab0..deaf957 100644
--- a/core/java/android/view/inputmethod/flags.aconfig
+++ b/core/java/android/view/inputmethod/flags.aconfig
@@ -175,3 +175,12 @@
bug: "342672560"
is_fixed_read_only: true
}
+
+flag {
+ name: "adaptive_handwriting_bounds"
+ is_exported: true
+ namespace: "input_method"
+ description: "Feature flag for adaptively increasing handwriting bounds."
+ bug: "350047836"
+ is_fixed_read_only: true
+}
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index d7750bd..cb70466 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -106,6 +106,7 @@
import android.os.ParcelableParcel;
import android.os.Process;
import android.os.SystemClock;
+import android.os.Trace;
import android.os.UserHandle;
import android.provider.Settings;
import android.text.BoringLayout;
@@ -9229,174 +9230,179 @@
@Override
protected void onDraw(Canvas canvas) {
- restartMarqueeIfNeeded();
+ Trace.traceBegin(Trace.TRACE_TAG_VIEW, "TextView.onDraw");
+ try {
+ restartMarqueeIfNeeded();
- // Draw the background for this view
- super.onDraw(canvas);
+ // Draw the background for this view
+ super.onDraw(canvas);
- final int compoundPaddingLeft = getCompoundPaddingLeft();
- final int compoundPaddingTop = getCompoundPaddingTop();
- final int compoundPaddingRight = getCompoundPaddingRight();
- final int compoundPaddingBottom = getCompoundPaddingBottom();
- final int scrollX = mScrollX;
- final int scrollY = mScrollY;
- final int right = mRight;
- final int left = mLeft;
- final int bottom = mBottom;
- final int top = mTop;
- final boolean isLayoutRtl = isLayoutRtl();
- final int offset = getHorizontalOffsetForDrawables();
- final int leftOffset = isLayoutRtl ? 0 : offset;
- final int rightOffset = isLayoutRtl ? offset : 0;
+ final int compoundPaddingLeft = getCompoundPaddingLeft();
+ final int compoundPaddingTop = getCompoundPaddingTop();
+ final int compoundPaddingRight = getCompoundPaddingRight();
+ final int compoundPaddingBottom = getCompoundPaddingBottom();
+ final int scrollX = mScrollX;
+ final int scrollY = mScrollY;
+ final int right = mRight;
+ final int left = mLeft;
+ final int bottom = mBottom;
+ final int top = mTop;
+ final boolean isLayoutRtl = isLayoutRtl();
+ final int offset = getHorizontalOffsetForDrawables();
+ final int leftOffset = isLayoutRtl ? 0 : offset;
+ final int rightOffset = isLayoutRtl ? offset : 0;
- final Drawables dr = mDrawables;
- if (dr != null) {
- /*
- * Compound, not extended, because the icon is not clipped
- * if the text height is smaller.
- */
+ final Drawables dr = mDrawables;
+ if (dr != null) {
+ /*
+ * Compound, not extended, because the icon is not clipped
+ * if the text height is smaller.
+ */
- int vspace = bottom - top - compoundPaddingBottom - compoundPaddingTop;
- int hspace = right - left - compoundPaddingRight - compoundPaddingLeft;
+ int vspace = bottom - top - compoundPaddingBottom - compoundPaddingTop;
+ int hspace = right - left - compoundPaddingRight - compoundPaddingLeft;
- // IMPORTANT: The coordinates computed are also used in invalidateDrawable()
- // Make sure to update invalidateDrawable() when changing this code.
- if (dr.mShowing[Drawables.LEFT] != null) {
- canvas.save();
- canvas.translate(scrollX + mPaddingLeft + leftOffset,
- scrollY + compoundPaddingTop + (vspace - dr.mDrawableHeightLeft) / 2);
- dr.mShowing[Drawables.LEFT].draw(canvas);
- canvas.restore();
+ // IMPORTANT: The coordinates computed are also used in invalidateDrawable()
+ // Make sure to update invalidateDrawable() when changing this code.
+ if (dr.mShowing[Drawables.LEFT] != null) {
+ canvas.save();
+ canvas.translate(scrollX + mPaddingLeft + leftOffset,
+ scrollY + compoundPaddingTop + (vspace - dr.mDrawableHeightLeft) / 2);
+ dr.mShowing[Drawables.LEFT].draw(canvas);
+ canvas.restore();
+ }
+
+ // IMPORTANT: The coordinates computed are also used in invalidateDrawable()
+ // Make sure to update invalidateDrawable() when changing this code.
+ if (dr.mShowing[Drawables.RIGHT] != null) {
+ canvas.save();
+ canvas.translate(scrollX + right - left - mPaddingRight
+ - dr.mDrawableSizeRight - rightOffset,
+ scrollY + compoundPaddingTop + (vspace - dr.mDrawableHeightRight) / 2);
+ dr.mShowing[Drawables.RIGHT].draw(canvas);
+ canvas.restore();
+ }
+
+ // IMPORTANT: The coordinates computed are also used in invalidateDrawable()
+ // Make sure to update invalidateDrawable() when changing this code.
+ if (dr.mShowing[Drawables.TOP] != null) {
+ canvas.save();
+ canvas.translate(scrollX + compoundPaddingLeft
+ + (hspace - dr.mDrawableWidthTop) / 2, scrollY + mPaddingTop);
+ dr.mShowing[Drawables.TOP].draw(canvas);
+ canvas.restore();
+ }
+
+ // IMPORTANT: The coordinates computed are also used in invalidateDrawable()
+ // Make sure to update invalidateDrawable() when changing this code.
+ if (dr.mShowing[Drawables.BOTTOM] != null) {
+ canvas.save();
+ canvas.translate(scrollX + compoundPaddingLeft
+ + (hspace - dr.mDrawableWidthBottom) / 2,
+ scrollY + bottom - top - mPaddingBottom - dr.mDrawableSizeBottom);
+ dr.mShowing[Drawables.BOTTOM].draw(canvas);
+ canvas.restore();
+ }
}
- // IMPORTANT: The coordinates computed are also used in invalidateDrawable()
- // Make sure to update invalidateDrawable() when changing this code.
- if (dr.mShowing[Drawables.RIGHT] != null) {
- canvas.save();
- canvas.translate(scrollX + right - left - mPaddingRight
- - dr.mDrawableSizeRight - rightOffset,
- scrollY + compoundPaddingTop + (vspace - dr.mDrawableHeightRight) / 2);
- dr.mShowing[Drawables.RIGHT].draw(canvas);
- canvas.restore();
+ int color = mCurTextColor;
+
+ if (mLayout == null) {
+ assumeLayout();
}
- // IMPORTANT: The coordinates computed are also used in invalidateDrawable()
- // Make sure to update invalidateDrawable() when changing this code.
- if (dr.mShowing[Drawables.TOP] != null) {
- canvas.save();
- canvas.translate(scrollX + compoundPaddingLeft
- + (hspace - dr.mDrawableWidthTop) / 2, scrollY + mPaddingTop);
- dr.mShowing[Drawables.TOP].draw(canvas);
- canvas.restore();
+ Layout layout = mLayout;
+
+ if (mHint != null && !mHideHint && mText.length() == 0) {
+ if (mHintTextColor != null) {
+ color = mCurHintTextColor;
+ }
+
+ layout = mHintLayout;
}
- // IMPORTANT: The coordinates computed are also used in invalidateDrawable()
- // Make sure to update invalidateDrawable() when changing this code.
- if (dr.mShowing[Drawables.BOTTOM] != null) {
- canvas.save();
- canvas.translate(scrollX + compoundPaddingLeft
- + (hspace - dr.mDrawableWidthBottom) / 2,
- scrollY + bottom - top - mPaddingBottom - dr.mDrawableSizeBottom);
- dr.mShowing[Drawables.BOTTOM].draw(canvas);
- canvas.restore();
- }
- }
+ mTextPaint.setColor(color);
+ mTextPaint.drawableState = getDrawableState();
- int color = mCurTextColor;
+ canvas.save();
+ /* Would be faster if we didn't have to do this. Can we chop the
+ (displayable) text so that we don't need to do this ever?
+ */
- if (mLayout == null) {
- assumeLayout();
- }
+ int extendedPaddingTop = getExtendedPaddingTop();
+ int extendedPaddingBottom = getExtendedPaddingBottom();
- Layout layout = mLayout;
+ final int vspace = mBottom - mTop - compoundPaddingBottom - compoundPaddingTop;
+ final int maxScrollY = mLayout.getHeight() - vspace;
- if (mHint != null && !mHideHint && mText.length() == 0) {
- if (mHintTextColor != null) {
- color = mCurHintTextColor;
+ float clipLeft = compoundPaddingLeft + scrollX;
+ float clipTop = (scrollY == 0) ? 0 : extendedPaddingTop + scrollY;
+ float clipRight = right - left - getCompoundPaddingRight() + scrollX;
+ float clipBottom = bottom - top + scrollY
+ - ((scrollY == maxScrollY) ? 0 : extendedPaddingBottom);
+
+ if (mShadowRadius != 0) {
+ clipLeft += Math.min(0, mShadowDx - mShadowRadius);
+ clipRight += Math.max(0, mShadowDx + mShadowRadius);
+
+ clipTop += Math.min(0, mShadowDy - mShadowRadius);
+ clipBottom += Math.max(0, mShadowDy + mShadowRadius);
}
- layout = mHintLayout;
- }
+ canvas.clipRect(clipLeft, clipTop, clipRight, clipBottom);
- mTextPaint.setColor(color);
- mTextPaint.drawableState = getDrawableState();
+ int voffsetText = 0;
+ int voffsetCursor = 0;
- canvas.save();
- /* Would be faster if we didn't have to do this. Can we chop the
- (displayable) text so that we don't need to do this ever?
- */
+ // translate in by our padding
+ /* shortcircuit calling getVerticaOffset() */
+ if ((mGravity & Gravity.VERTICAL_GRAVITY_MASK) != Gravity.TOP) {
+ voffsetText = getVerticalOffset(false);
+ voffsetCursor = getVerticalOffset(true);
+ }
+ canvas.translate(compoundPaddingLeft, extendedPaddingTop + voffsetText);
- int extendedPaddingTop = getExtendedPaddingTop();
- int extendedPaddingBottom = getExtendedPaddingBottom();
+ final int layoutDirection = getLayoutDirection();
+ final int absoluteGravity = Gravity.getAbsoluteGravity(mGravity, layoutDirection);
+ if (isMarqueeFadeEnabled()) {
+ if (!mSingleLine && getLineCount() == 1 && canMarquee()
+ && (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) != Gravity.LEFT) {
+ final int width = mRight - mLeft;
+ final int padding = getCompoundPaddingLeft() + getCompoundPaddingRight();
+ final float dx = mLayout.getLineRight(0) - (width - padding);
+ canvas.translate(layout.getParagraphDirection(0) * dx, 0.0f);
+ }
- final int vspace = mBottom - mTop - compoundPaddingBottom - compoundPaddingTop;
- final int maxScrollY = mLayout.getHeight() - vspace;
+ if (mMarquee != null && mMarquee.isRunning()) {
+ final float dx = -mMarquee.getScroll();
+ canvas.translate(layout.getParagraphDirection(0) * dx, 0.0f);
+ }
+ }
- float clipLeft = compoundPaddingLeft + scrollX;
- float clipTop = (scrollY == 0) ? 0 : extendedPaddingTop + scrollY;
- float clipRight = right - left - getCompoundPaddingRight() + scrollX;
- float clipBottom = bottom - top + scrollY
- - ((scrollY == maxScrollY) ? 0 : extendedPaddingBottom);
+ final int cursorOffsetVertical = voffsetCursor - voffsetText;
- if (mShadowRadius != 0) {
- clipLeft += Math.min(0, mShadowDx - mShadowRadius);
- clipRight += Math.max(0, mShadowDx + mShadowRadius);
+ maybeUpdateHighlightPaths();
+ // If there is a gesture preview highlight, then the selection or cursor is not drawn.
+ Path highlight = hasGesturePreviewHighlight() ? null : getUpdatedHighlightPath();
+ if (mEditor != null) {
+ mEditor.onDraw(canvas, layout, mHighlightPaths, mHighlightPaints, highlight,
+ mHighlightPaint, cursorOffsetVertical);
+ } else {
+ layout.draw(canvas, mHighlightPaths, mHighlightPaints, highlight, mHighlightPaint,
+ cursorOffsetVertical);
+ }
- clipTop += Math.min(0, mShadowDy - mShadowRadius);
- clipBottom += Math.max(0, mShadowDy + mShadowRadius);
- }
-
- canvas.clipRect(clipLeft, clipTop, clipRight, clipBottom);
-
- int voffsetText = 0;
- int voffsetCursor = 0;
-
- // translate in by our padding
- /* shortcircuit calling getVerticaOffset() */
- if ((mGravity & Gravity.VERTICAL_GRAVITY_MASK) != Gravity.TOP) {
- voffsetText = getVerticalOffset(false);
- voffsetCursor = getVerticalOffset(true);
- }
- canvas.translate(compoundPaddingLeft, extendedPaddingTop + voffsetText);
-
- final int layoutDirection = getLayoutDirection();
- final int absoluteGravity = Gravity.getAbsoluteGravity(mGravity, layoutDirection);
- if (isMarqueeFadeEnabled()) {
- if (!mSingleLine && getLineCount() == 1 && canMarquee()
- && (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) != Gravity.LEFT) {
- final int width = mRight - mLeft;
- final int padding = getCompoundPaddingLeft() + getCompoundPaddingRight();
- final float dx = mLayout.getLineRight(0) - (width - padding);
+ if (mMarquee != null && mMarquee.shouldDrawGhost()) {
+ final float dx = mMarquee.getGhostOffset();
canvas.translate(layout.getParagraphDirection(0) * dx, 0.0f);
+ layout.draw(canvas, mHighlightPaths, mHighlightPaints, highlight, mHighlightPaint,
+ cursorOffsetVertical);
}
- if (mMarquee != null && mMarquee.isRunning()) {
- final float dx = -mMarquee.getScroll();
- canvas.translate(layout.getParagraphDirection(0) * dx, 0.0f);
- }
+ canvas.restore();
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
-
- final int cursorOffsetVertical = voffsetCursor - voffsetText;
-
- maybeUpdateHighlightPaths();
- // If there is a gesture preview highlight, then the selection or cursor is not drawn.
- Path highlight = hasGesturePreviewHighlight() ? null : getUpdatedHighlightPath();
- if (mEditor != null) {
- mEditor.onDraw(canvas, layout, mHighlightPaths, mHighlightPaints, highlight,
- mHighlightPaint, cursorOffsetVertical);
- } else {
- layout.draw(canvas, mHighlightPaths, mHighlightPaints, highlight, mHighlightPaint,
- cursorOffsetVertical);
- }
-
- if (mMarquee != null && mMarquee.shouldDrawGhost()) {
- final float dx = mMarquee.getGhostOffset();
- canvas.translate(layout.getParagraphDirection(0) * dx, 0.0f);
- layout.draw(canvas, mHighlightPaths, mHighlightPaints, highlight, mHighlightPaint,
- cursorOffsetVertical);
- }
-
- canvas.restore();
}
@Override
@@ -11254,192 +11260,201 @@
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- int widthMode = MeasureSpec.getMode(widthMeasureSpec);
- int heightMode = MeasureSpec.getMode(heightMeasureSpec);
- int widthSize = MeasureSpec.getSize(widthMeasureSpec);
- int heightSize = MeasureSpec.getSize(heightMeasureSpec);
+ Trace.traceBegin(Trace.TRACE_TAG_VIEW, "TextView.onMeasure");
+ try {
+ int widthMode = MeasureSpec.getMode(widthMeasureSpec);
+ int heightMode = MeasureSpec.getMode(heightMeasureSpec);
+ int widthSize = MeasureSpec.getSize(widthMeasureSpec);
+ int heightSize = MeasureSpec.getSize(heightMeasureSpec);
- int width;
- int height;
+ int width;
+ int height;
- BoringLayout.Metrics boring = UNKNOWN_BORING;
- BoringLayout.Metrics hintBoring = UNKNOWN_BORING;
+ BoringLayout.Metrics boring = UNKNOWN_BORING;
+ BoringLayout.Metrics hintBoring = UNKNOWN_BORING;
- if (mTextDir == null) {
- mTextDir = getTextDirectionHeuristic();
- }
-
- int des = -1;
- boolean fromexisting = false;
- final float widthLimit = (widthMode == MeasureSpec.AT_MOST)
- ? (float) widthSize : Float.MAX_VALUE;
-
- if (widthMode == MeasureSpec.EXACTLY) {
- // Parent has told us how big to be. So be it.
- width = widthSize;
- } else {
- if (mLayout != null && mEllipsize == null) {
- des = desired(mLayout, mUseBoundsForWidth);
+ if (mTextDir == null) {
+ mTextDir = getTextDirectionHeuristic();
}
- if (des < 0) {
- boring = BoringLayout.isBoring(mTransformed, mTextPaint, mTextDir,
- isFallbackLineSpacingForBoringLayout(), getResolvedMinimumFontMetrics(),
- mBoring);
- if (boring != null) {
- mBoring = boring;
- }
+ int des = -1;
+ boolean fromexisting = false;
+ final float widthLimit = (widthMode == MeasureSpec.AT_MOST)
+ ? (float) widthSize : Float.MAX_VALUE;
+
+ if (widthMode == MeasureSpec.EXACTLY) {
+ // Parent has told us how big to be. So be it.
+ width = widthSize;
} else {
- fromexisting = true;
- }
+ if (mLayout != null && mEllipsize == null) {
+ des = desired(mLayout, mUseBoundsForWidth);
+ }
- if (boring == null || boring == UNKNOWN_BORING) {
if (des < 0) {
- des = (int) Math.ceil(Layout.getDesiredWidthWithLimit(mTransformed, 0,
- mTransformed.length(), mTextPaint, mTextDir, widthLimit,
- mUseBoundsForWidth));
- }
- width = des;
- } else {
- if (mUseBoundsForWidth) {
- RectF bbox = boring.getDrawingBoundingBox();
- float rightMax = Math.max(bbox.right, boring.width);
- float leftMin = Math.min(bbox.left, 0);
- width = Math.max(boring.width, (int) Math.ceil(rightMax - leftMin));
- } else {
- width = boring.width;
- }
- }
-
- final Drawables dr = mDrawables;
- if (dr != null) {
- width = Math.max(width, dr.mDrawableWidthTop);
- width = Math.max(width, dr.mDrawableWidthBottom);
- }
-
- if (mHint != null) {
- int hintDes = -1;
- int hintWidth;
-
- if (mHintLayout != null && mEllipsize == null) {
- hintDes = desired(mHintLayout, mUseBoundsForWidth);
- }
-
- if (hintDes < 0) {
- hintBoring = BoringLayout.isBoring(mHint, mTextPaint, mTextDir,
+ boring = BoringLayout.isBoring(mTransformed, mTextPaint, mTextDir,
isFallbackLineSpacingForBoringLayout(), getResolvedMinimumFontMetrics(),
- mHintBoring);
- if (hintBoring != null) {
- mHintBoring = hintBoring;
+ mBoring);
+ if (boring != null) {
+ mBoring = boring;
}
+ } else {
+ fromexisting = true;
}
- if (hintBoring == null || hintBoring == UNKNOWN_BORING) {
- if (hintDes < 0) {
- hintDes = (int) Math.ceil(Layout.getDesiredWidthWithLimit(mHint, 0,
- mHint.length(), mTextPaint, mTextDir, widthLimit,
+ if (boring == null || boring == UNKNOWN_BORING) {
+ if (des < 0) {
+ des = (int) Math.ceil(Layout.getDesiredWidthWithLimit(mTransformed, 0,
+ mTransformed.length(), mTextPaint, mTextDir, widthLimit,
mUseBoundsForWidth));
}
- hintWidth = hintDes;
+ width = des;
} else {
- hintWidth = hintBoring.width;
+ if (mUseBoundsForWidth) {
+ RectF bbox = boring.getDrawingBoundingBox();
+ float rightMax = Math.max(bbox.right, boring.width);
+ float leftMin = Math.min(bbox.left, 0);
+ width = Math.max(boring.width, (int) Math.ceil(rightMax - leftMin));
+ } else {
+ width = boring.width;
+ }
}
- if (hintWidth > width) {
- width = hintWidth;
+ final Drawables dr = mDrawables;
+ if (dr != null) {
+ width = Math.max(width, dr.mDrawableWidthTop);
+ width = Math.max(width, dr.mDrawableWidthBottom);
}
- }
- width += getCompoundPaddingLeft() + getCompoundPaddingRight();
+ if (mHint != null) {
+ int hintDes = -1;
+ int hintWidth;
- if (mMaxWidthMode == EMS) {
- width = Math.min(width, mMaxWidth * getLineHeight());
- } else {
- width = Math.min(width, mMaxWidth);
- }
+ if (mHintLayout != null && mEllipsize == null) {
+ hintDes = desired(mHintLayout, mUseBoundsForWidth);
+ }
- if (mMinWidthMode == EMS) {
- width = Math.max(width, mMinWidth * getLineHeight());
- } else {
- width = Math.max(width, mMinWidth);
- }
+ if (hintDes < 0) {
+ hintBoring = BoringLayout.isBoring(mHint, mTextPaint, mTextDir,
+ isFallbackLineSpacingForBoringLayout(),
+ getResolvedMinimumFontMetrics(),
+ mHintBoring);
+ if (hintBoring != null) {
+ mHintBoring = hintBoring;
+ }
+ }
- // Check against our minimum width
- width = Math.max(width, getSuggestedMinimumWidth());
+ if (hintBoring == null || hintBoring == UNKNOWN_BORING) {
+ if (hintDes < 0) {
+ hintDes = (int) Math.ceil(Layout.getDesiredWidthWithLimit(mHint, 0,
+ mHint.length(), mTextPaint, mTextDir, widthLimit,
+ mUseBoundsForWidth));
+ }
+ hintWidth = hintDes;
+ } else {
+ hintWidth = hintBoring.width;
+ }
- if (widthMode == MeasureSpec.AT_MOST) {
- width = Math.min(widthSize, width);
- }
- }
+ if (hintWidth > width) {
+ width = hintWidth;
+ }
+ }
- int want = width - getCompoundPaddingLeft() - getCompoundPaddingRight();
- int unpaddedWidth = want;
+ width += getCompoundPaddingLeft() + getCompoundPaddingRight();
- if (mHorizontallyScrolling) want = VERY_WIDE;
-
- int hintWant = want;
- int hintWidth = (mHintLayout == null) ? hintWant : mHintLayout.getWidth();
-
- if (mLayout == null) {
- makeNewLayout(want, hintWant, boring, hintBoring,
- width - getCompoundPaddingLeft() - getCompoundPaddingRight(), false);
- } else {
- final boolean layoutChanged = (mLayout.getWidth() != want) || (hintWidth != hintWant)
- || (mLayout.getEllipsizedWidth()
- != width - getCompoundPaddingLeft() - getCompoundPaddingRight());
-
- final boolean widthChanged = (mHint == null) && (mEllipsize == null)
- && (want > mLayout.getWidth())
- && (mLayout instanceof BoringLayout
- || (fromexisting && des >= 0 && des <= want));
-
- final boolean maximumChanged = (mMaxMode != mOldMaxMode) || (mMaximum != mOldMaximum);
-
- if (layoutChanged || maximumChanged) {
- if (!maximumChanged && widthChanged) {
- mLayout.increaseWidthTo(want);
+ if (mMaxWidthMode == EMS) {
+ width = Math.min(width, mMaxWidth * getLineHeight());
} else {
- makeNewLayout(want, hintWant, boring, hintBoring,
- width - getCompoundPaddingLeft() - getCompoundPaddingRight(), false);
+ width = Math.min(width, mMaxWidth);
}
+
+ if (mMinWidthMode == EMS) {
+ width = Math.max(width, mMinWidth * getLineHeight());
+ } else {
+ width = Math.max(width, mMinWidth);
+ }
+
+ // Check against our minimum width
+ width = Math.max(width, getSuggestedMinimumWidth());
+
+ if (widthMode == MeasureSpec.AT_MOST) {
+ width = Math.min(widthSize, width);
+ }
+ }
+
+ int want = width - getCompoundPaddingLeft() - getCompoundPaddingRight();
+ int unpaddedWidth = want;
+
+ if (mHorizontallyScrolling) want = VERY_WIDE;
+
+ int hintWant = want;
+ int hintWidth = (mHintLayout == null) ? hintWant : mHintLayout.getWidth();
+
+ if (mLayout == null) {
+ makeNewLayout(want, hintWant, boring, hintBoring,
+ width - getCompoundPaddingLeft() - getCompoundPaddingRight(), false);
} else {
- // Nothing has changed
+ final boolean layoutChanged =
+ (mLayout.getWidth() != want) || (hintWidth != hintWant)
+ || (mLayout.getEllipsizedWidth()
+ != width - getCompoundPaddingLeft() - getCompoundPaddingRight());
+
+ final boolean widthChanged = (mHint == null) && (mEllipsize == null)
+ && (want > mLayout.getWidth())
+ && (mLayout instanceof BoringLayout
+ || (fromexisting && des >= 0 && des <= want));
+
+ final boolean maximumChanged =
+ (mMaxMode != mOldMaxMode) || (mMaximum != mOldMaximum);
+
+ if (layoutChanged || maximumChanged) {
+ if (!maximumChanged && widthChanged) {
+ mLayout.increaseWidthTo(want);
+ } else {
+ makeNewLayout(want, hintWant, boring, hintBoring,
+ width - getCompoundPaddingLeft() - getCompoundPaddingRight(),
+ false);
+ }
+ } else {
+ // Nothing has changed
+ }
}
- }
- if (heightMode == MeasureSpec.EXACTLY) {
- // Parent has told us how big to be. So be it.
- height = heightSize;
- mDesiredHeightAtMeasure = -1;
- } else {
- int desired = getDesiredHeight();
+ if (heightMode == MeasureSpec.EXACTLY) {
+ // Parent has told us how big to be. So be it.
+ height = heightSize;
+ mDesiredHeightAtMeasure = -1;
+ } else {
+ int desired = getDesiredHeight();
- height = desired;
- mDesiredHeightAtMeasure = desired;
+ height = desired;
+ mDesiredHeightAtMeasure = desired;
- if (heightMode == MeasureSpec.AT_MOST) {
- height = Math.min(desired, heightSize);
+ if (heightMode == MeasureSpec.AT_MOST) {
+ height = Math.min(desired, heightSize);
+ }
}
- }
- int unpaddedHeight = height - getCompoundPaddingTop() - getCompoundPaddingBottom();
- if (mMaxMode == LINES && mLayout.getLineCount() > mMaximum) {
- unpaddedHeight = Math.min(unpaddedHeight, mLayout.getLineTop(mMaximum));
- }
+ int unpaddedHeight = height - getCompoundPaddingTop() - getCompoundPaddingBottom();
+ if (mMaxMode == LINES && mLayout.getLineCount() > mMaximum) {
+ unpaddedHeight = Math.min(unpaddedHeight, mLayout.getLineTop(mMaximum));
+ }
- /*
- * We didn't let makeNewLayout() register to bring the cursor into view,
- * so do it here if there is any possibility that it is needed.
- */
- if (mMovement != null
- || mLayout.getWidth() > unpaddedWidth
- || mLayout.getHeight() > unpaddedHeight) {
- registerForPreDraw();
- } else {
- scrollTo(0, 0);
- }
+ /*
+ * We didn't let makeNewLayout() register to bring the cursor into view,
+ * so do it here if there is any possibility that it is needed.
+ */
+ if (mMovement != null
+ || mLayout.getWidth() > unpaddedWidth
+ || mLayout.getHeight() > unpaddedHeight) {
+ registerForPreDraw();
+ } else {
+ scrollTo(0, 0);
+ }
- setMeasuredDimension(width, height);
+ setMeasuredDimension(width, height);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_VIEW);
+ }
}
/**
diff --git a/core/java/android/window/TransitionInfo.java b/core/java/android/window/TransitionInfo.java
index 0f2dd10..2c21417 100644
--- a/core/java/android/window/TransitionInfo.java
+++ b/core/java/android/window/TransitionInfo.java
@@ -49,6 +49,7 @@
import android.graphics.Point;
import android.graphics.Rect;
import android.hardware.HardwareBuffer;
+import android.os.BinderProxy;
import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
@@ -1089,8 +1090,13 @@
@Override
public String toString() {
final StringBuilder sb = new StringBuilder();
- sb.append('{'); sb.append(mContainer);
- sb.append(" m="); sb.append(modeToString(mMode));
+ sb.append('{');
+ if (mContainer != null && !(mContainer.asBinder() instanceof BinderProxy)) {
+ // Only log the token if it is not a binder proxy and has additional container info
+ sb.append(mContainer);
+ sb.append(" ");
+ }
+ sb.append("m="); sb.append(modeToString(mMode));
sb.append(" f="); sb.append(flagsToString(mFlags));
if (mParent != null) {
sb.append(" p="); sb.append(mParent);
diff --git a/core/java/android/window/WindowContainerTransaction.java b/core/java/android/window/WindowContainerTransaction.java
index 3fe63ab..a88a172 100644
--- a/core/java/android/window/WindowContainerTransaction.java
+++ b/core/java/android/window/WindowContainerTransaction.java
@@ -1120,8 +1120,8 @@
@NonNull
public String toString() {
return "WindowContainerTransaction {"
- + " changes = " + mChanges
- + " hops = " + mHierarchyOps
+ + " changes= " + mChanges
+ + " hops= " + mHierarchyOps
+ " errorCallbackToken=" + mErrorCallbackToken
+ " taskFragmentOrganizer=" + mTaskFragmentOrganizer
+ " }";
diff --git a/core/java/android/window/flags/lse_desktop_experience.aconfig b/core/java/android/window/flags/lse_desktop_experience.aconfig
index f474b34..c7d5a9f 100644
--- a/core/java/android/window/flags/lse_desktop_experience.aconfig
+++ b/core/java/android/window/flags/lse_desktop_experience.aconfig
@@ -253,6 +253,9 @@
namespace: "lse_desktop_experience"
description: "Enables enter desktop windowing transition & motion polish changes"
bug: "369763947"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
}
flag {
@@ -260,6 +263,9 @@
namespace: "lse_desktop_experience"
description: "Enables exit desktop windowing transition & motion polish changes"
bug: "353650462"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
}
flag {
@@ -343,6 +349,9 @@
namespace: "lse_desktop_experience"
description: "Enables custom transitions for alt-tab app launches in Desktop Mode."
bug: "370735595"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
}
flag {
@@ -350,6 +359,9 @@
namespace: "lse_desktop_experience"
description: "Enables custom transitions for app launches in Desktop Mode."
bug: "375992828"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
}
flag {
@@ -402,4 +414,11 @@
namespace: "lse_desktop_experience"
description: "Enables HSUM on desktop mode."
bug: "366397912"
+}
+
+flag {
+ name: "enable_multiple_desktops"
+ namespace: "lse_desktop_experience"
+ description: "Enable multiple desktop sessions for desktop windowing."
+ bug: "379158791"
}
\ No newline at end of file
diff --git a/core/java/android/window/flags/window_surfaces.aconfig b/core/java/android/window/flags/window_surfaces.aconfig
index 96b9dc7..bb47707 100644
--- a/core/java/android/window/flags/window_surfaces.aconfig
+++ b/core/java/android/window/flags/window_surfaces.aconfig
@@ -29,14 +29,6 @@
flag {
namespace: "window_surfaces"
- name: "secure_window_state"
- description: "Move SC secure flag to WindowState level"
- is_fixed_read_only: true
- bug: "308662081"
-}
-
-flag {
- namespace: "window_surfaces"
name: "trusted_presentation_listener_for_window"
is_exported: true
description: "Enable trustedPresentationListener on windows public API"
diff --git a/core/java/android/window/flags/windowing_frontend.aconfig b/core/java/android/window/flags/windowing_frontend.aconfig
index 4f924a82..ff69610 100644
--- a/core/java/android/window/flags/windowing_frontend.aconfig
+++ b/core/java/android/window/flags/windowing_frontend.aconfig
@@ -418,6 +418,17 @@
}
flag {
+ name: "record_task_snapshots_before_shutdown"
+ namespace: "windowing_frontend"
+ description: "Record task snapshots before shutdown"
+ bug: "376821232"
+ is_fixed_read_only: true
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "predictive_back_three_button_nav"
namespace: "systemui"
description: "Enable Predictive Back Animation for 3-button-nav"
diff --git a/core/java/com/android/internal/app/IAppOpsService.aidl b/core/java/com/android/internal/app/IAppOpsService.aidl
index 3ec7064..2cfc680 100644
--- a/core/java/com/android/internal/app/IAppOpsService.aidl
+++ b/core/java/com/android/internal/app/IAppOpsService.aidl
@@ -152,7 +152,7 @@
in AttributionSourceState attributionSourceStateState, boolean skipProxyOperation);
int checkOperationRawForDevice(int code, int uid, String packageName,
@nullable String attributionTag, int virtualDeviceId);
- int checkOperationForDevice(int code, int uid, String packageName, int virtualDeviceId);
+ int checkOperationForDevice(int code, int uid, String packageName, @nullable String attributionTag, int virtualDeviceId);
SyncNotedAppOp noteOperationForDevice(int code, int uid, String packageName,
@nullable String attributionTag, int virtualDeviceId,
boolean shouldCollectAsyncNotedOp, String message, boolean shouldCollectMessage);
diff --git a/core/java/com/android/internal/backup/IBackupTransport.aidl b/core/java/com/android/internal/backup/IBackupTransport.aidl
index 21c7baa..469ab48 100644
--- a/core/java/com/android/internal/backup/IBackupTransport.aidl
+++ b/core/java/com/android/internal/backup/IBackupTransport.aidl
@@ -410,4 +410,15 @@
* however backups initiated by the framework will call this method to retrieve one.
*/
void getBackupManagerMonitor(in AndroidFuture<IBackupManagerMonitor> resultFuture);
+
+ /**
+ * Ask the transport whether packages that are about to be backed up or restored should not be
+ * put into a restricted mode by the framework and started normally instead. The
+ * {@code resultFuture} should be completed with a subset of the packages passed in, indicating
+ * which packages should NOT be put into restricted mode for the given operation type.
+ *
+ * @param operationType 0 for backup, 1 for restore.
+ */
+ void getPackagesThatShouldNotUseRestrictedMode(in List<String> packageNames, int operationType,
+ in AndroidFuture<List<String>> resultFuture);
}
diff --git a/core/java/android/text/ClientFlags.java b/core/java/com/android/internal/policy/IDeviceLockedStateListener.aidl
similarity index 60%
copy from core/java/android/text/ClientFlags.java
copy to core/java/com/android/internal/policy/IDeviceLockedStateListener.aidl
index ca88764..cc626f6 100644
--- a/core/java/android/text/ClientFlags.java
+++ b/core/java/com/android/internal/policy/IDeviceLockedStateListener.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,16 +14,8 @@
* limitations under the License.
*/
-package android.text;
+package com.android.internal.policy;
-/**
- * An aconfig feature flags that can be accessible from application process without
- * ContentProvider IPCs.
- *
- * When you add new flags, you have to add flag string to {@link TextFlags#TEXT_ACONFIGS_FLAGS}.
- *
- * TODO(nona): Remove this class.
- * @hide
- */
-public class ClientFlags {
-}
+oneway interface IDeviceLockedStateListener {
+ void onDeviceLockedStateChanged(boolean isDeviceLocked);
+}
\ No newline at end of file
diff --git a/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java b/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java
index a1c987f..eb682df 100644
--- a/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java
+++ b/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java
@@ -676,15 +676,30 @@
return internMap.get(string);
}
+ protected boolean validateGroups(ILogger logger, String[] groups) {
+ for (int i = 0; i < groups.length; i++) {
+ String group = groups[i];
+ IProtoLogGroup g = mLogGroups.get(group);
+ if (g == null) {
+ logger.log("No IProtoLogGroup named " + group);
+ return false;
+ }
+ }
+ return true;
+ }
+
private int setTextLogging(boolean value, ILogger logger, String... groups) {
+ if (!validateGroups(logger, groups)) {
+ return -1;
+ }
+
for (int i = 0; i < groups.length; i++) {
String group = groups[i];
IProtoLogGroup g = mLogGroups.get(group);
if (g != null) {
g.setLogToLogcat(value);
} else {
- logger.log("No IProtoLogGroup named " + group);
- return -1;
+ throw new RuntimeException("No IProtoLogGroup named " + group);
}
}
diff --git a/core/java/com/android/internal/protolog/ProcessedPerfettoProtoLogImpl.java b/core/java/com/android/internal/protolog/ProcessedPerfettoProtoLogImpl.java
index 70d148a..967a5ed 100644
--- a/core/java/com/android/internal/protolog/ProcessedPerfettoProtoLogImpl.java
+++ b/core/java/com/android/internal/protolog/ProcessedPerfettoProtoLogImpl.java
@@ -113,6 +113,10 @@
*/
@Override
public int startLoggingToLogcat(String[] groups, @NonNull ILogger logger) {
+ if (!validateGroups(logger, groups)) {
+ return -1;
+ }
+
mViewerConfigReader.loadViewerConfig(groups, logger);
return super.startLoggingToLogcat(groups, logger);
}
@@ -125,8 +129,19 @@
*/
@Override
public int stopLoggingToLogcat(String[] groups, @NonNull ILogger logger) {
+ if (!validateGroups(logger, groups)) {
+ return -1;
+ }
+
+ var status = super.stopLoggingToLogcat(groups, logger);
+
+ if (status != 0) {
+ throw new RuntimeException("Failed to stop logging to logcat");
+ }
+
+ // If we successfully disabled logging, unload the viewer config.
mViewerConfigReader.unloadViewerConfig(groups, logger);
- return super.stopLoggingToLogcat(groups, logger);
+ return status;
}
@Deprecated
diff --git a/core/java/com/android/internal/ravenwood/RavenwoodEnvironment.java b/core/java/com/android/internal/ravenwood/RavenwoodEnvironment.java
index 3303d87..8df3f2a 100644
--- a/core/java/com/android/internal/ravenwood/RavenwoodEnvironment.java
+++ b/core/java/com/android/internal/ravenwood/RavenwoodEnvironment.java
@@ -88,15 +88,19 @@
/** @hide */
public static class CompatIdsForTest {
// Enabled by default
+ /** Used for testing */
@ChangeId
public static final long TEST_COMPAT_ID_1 = 368131859L;
+ /** Used for testing */
@Disabled
@ChangeId public static final long TEST_COMPAT_ID_2 = 368131701L;
+ /** Used for testing */
@EnabledAfter(targetSdkVersion = S)
@ChangeId public static final long TEST_COMPAT_ID_3 = 368131659L;
+ /** Used for testing */
@EnabledAfter(targetSdkVersion = UPSIDE_DOWN_CAKE)
@ChangeId public static final long TEST_COMPAT_ID_4 = 368132057L;
}
diff --git a/core/java/com/android/internal/telephony/IPhoneStateListener.aidl b/core/java/com/android/internal/telephony/IPhoneStateListener.aidl
index 0e85e04..bf8a565 100644
--- a/core/java/com/android/internal/telephony/IPhoneStateListener.aidl
+++ b/core/java/com/android/internal/telephony/IPhoneStateListener.aidl
@@ -20,6 +20,7 @@
import android.telephony.CallState;
import android.telephony.CellIdentity;
import android.telephony.CellInfo;
+import android.telephony.CellularIdentifierDisclosure;
import android.telephony.DataConnectionRealTimeInfo;
import android.telephony.LinkCapacityEstimate;
import android.telephony.TelephonyDisplayInfo;
@@ -28,6 +29,7 @@
import android.telephony.PreciseCallState;
import android.telephony.PreciseDataConnectionState;
import android.telephony.satellite.NtnSignalStrength;
+import android.telephony.SecurityAlgorithmUpdate;
import android.telephony.ServiceState;
import android.telephony.SignalStrength;
import android.telephony.emergency.EmergencyNumber;
@@ -87,4 +89,6 @@
void onCarrierRoamingNtnEligibleStateChanged(in boolean eligible);
void onCarrierRoamingNtnAvailableServicesChanged(in int[] availableServices);
void onCarrierRoamingNtnSignalStrengthChanged(in NtnSignalStrength ntnSignalStrength);
+ void onSecurityAlgorithmsChanged(in SecurityAlgorithmUpdate update);
+ void onCellularIdentifierDisclosedChanged(in CellularIdentifierDisclosure disclosure);
}
diff --git a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
index 0f268d5d..a296fbd1 100644
--- a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
+++ b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
@@ -23,6 +23,7 @@
import android.telephony.CallQuality;
import android.telephony.CellIdentity;
import android.telephony.CellInfo;
+import android.telephony.CellularIdentifierDisclosure;
import android.telephony.LinkCapacityEstimate;
import android.telephony.TelephonyDisplayInfo;
import android.telephony.ims.ImsReasonInfo;
@@ -30,6 +31,7 @@
import android.telephony.PhysicalChannelConfig;
import android.telephony.PreciseDataConnectionState;
import android.telephony.satellite.NtnSignalStrength;
+import android.telephony.SecurityAlgorithmUpdate;
import android.telephony.ServiceState;
import android.telephony.SignalStrength;
import android.telephony.emergency.EmergencyNumber;
@@ -132,4 +134,7 @@
void removeSatelliteStateChangeListener(ISatelliteStateChangeListener listener, String pkg);
void notifySatelliteStateChanged(boolean isEnabled);
+ void notifySecurityAlgorithmsChanged(int phoneId, int subId, in SecurityAlgorithmUpdate update);
+ void notifyCellularIdentifierDisclosedChanged(
+ int phoneId, int subId, in CellularIdentifierDisclosure disclosure);
}
diff --git a/core/java/com/android/internal/widget/NotificationProgressBar.java b/core/java/com/android/internal/widget/NotificationProgressBar.java
index f2b36c3..7a21275 100644
--- a/core/java/com/android/internal/widget/NotificationProgressBar.java
+++ b/core/java/com/android/internal/widget/NotificationProgressBar.java
@@ -59,6 +59,8 @@
public final class NotificationProgressBar extends ProgressBar {
private static final String TAG = "NotificationProgressBar";
+ private NotificationProgressDrawable mNotificationProgressDrawable;
+
private NotificationProgressModel mProgressModel;
@Nullable
@@ -94,6 +96,12 @@
defStyleAttr,
defStyleRes);
+ try {
+ mNotificationProgressDrawable = getNotificationProgressDrawable();
+ } catch (IllegalStateException ex) {
+ Log.e(TAG, "Can't get NotificationProgressDrawable", ex);
+ }
+
// Supports setting the tracker in xml, but ProgressStyle notifications set/override it
// via {@code setProgressTrackerIcon}.
final Drawable tracker = a.getDrawable(R.styleable.NotificationProgressBar_tracker);
@@ -131,11 +139,8 @@
progressMax,
mProgressModel.isStyledByProgress());
- try {
- final NotificationProgressDrawable drawable = getNotificationProgressDrawable();
- drawable.setParts(mProgressDrawableParts);
- } catch (IllegalStateException ex) {
- Log.e(TAG, "Can't set parts because can't get NotificationProgressDrawable", ex);
+ if (mNotificationProgressDrawable != null) {
+ mNotificationProgressDrawable.setParts(mProgressDrawableParts);
}
setMax(progressMax);
@@ -195,10 +200,6 @@
}
private void setTracker(@Nullable Drawable tracker) {
- if (isIndeterminate() && tracker != null) {
- return;
- }
-
final boolean needUpdate = mTracker != null && tracker != mTracker;
if (needUpdate) {
mTracker.setCallback(null);
@@ -222,6 +223,9 @@
}
mTracker = tracker;
+ if (mNotificationProgressDrawable != null) {
+ mNotificationProgressDrawable.setHasTrackerIcon(mTracker != null);
+ }
configureTrackerBounds();
@@ -275,16 +279,6 @@
}
@Override
- @RemotableViewMethod
- public synchronized void setIndeterminate(boolean indeterminate) {
- super.setIndeterminate(indeterminate);
-
- if (isIndeterminate()) {
- setTracker(null);
- }
- }
-
- @Override
protected boolean verifyDrawable(@NonNull Drawable who) {
return who == mTracker || super.verifyDrawable(who);
}
@@ -421,6 +415,8 @@
@Override
protected synchronized void onDraw(Canvas canvas) {
super.onDraw(canvas);
+
+ if (isIndeterminate()) return;
drawTracker(canvas);
}
diff --git a/core/java/com/android/internal/widget/NotificationProgressDrawable.java b/core/java/com/android/internal/widget/NotificationProgressDrawable.java
index fb6937c..e95225e 100644
--- a/core/java/com/android/internal/widget/NotificationProgressDrawable.java
+++ b/core/java/com/android/internal/widget/NotificationProgressDrawable.java
@@ -23,7 +23,6 @@
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
-import android.graphics.DashPathEffect;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.Rect;
@@ -62,21 +61,15 @@
private boolean mMutated;
private final ArrayList<Part> mParts = new ArrayList<>();
+ private boolean mHasTrackerIcon;
private final RectF mSegRectF = new RectF();
private final Rect mPointRect = new Rect();
private final RectF mPointRectF = new RectF();
- private final Paint mStrokePaint = new Paint();
- private final Paint mDashedStrokePaint = new Paint();
private final Paint mFillPaint = new Paint();
{
- mStrokePaint.setStyle(Paint.Style.STROKE);
- mStrokePaint.setStrokeCap(Paint.Cap.ROUND);
-
- mDashedStrokePaint.setStyle(Paint.Style.STROKE);
-
mFillPaint.setStyle(Paint.Style.FILL);
}
@@ -87,49 +80,15 @@
}
/**
- * <p>Set the stroke width and default color for the drawable.</p>
- * <p>Note: changing this property will affect all instances of a drawable loaded from a
- * resource. It is recommended to invoke
- * {@link #mutate()} before changing this property.</p>
- *
- * @param width The width in pixels of the stroke
- * @param color The color of the stroke
- * @see #mutate()
- * @see #setStroke(int, int, float, float)
- */
- public void setStroke(int width, @ColorInt int color) {
- setStroke(width, color, 0, 0);
- }
-
- /**
- * <p>Set the stroke width and default color for the drawable. This method can also be used
- * to dash the stroke for the dashed segments.</p>
- * <p>Note: changing this property will affect all instances of a drawable loaded from a
- * resource. It is recommended to invoke {@link #mutate()} before changing this property.</p>
- *
- * @param width The width in pixels of the stroke
- * @param color The color of the stroke
- * @param dashWidth The length in pixels of the dashes, set to 0 to disable dashes
- * @param dashGap The gap in pixels between dashes
- * @see #mutate()
- * @see #setStroke(int, int)
- */
- public void setStroke(int width, @ColorInt int color, float dashWidth, float dashGap) {
- mState.setStroke(width, color, dashWidth, dashGap);
- setStrokeInternal(width, dashWidth, dashGap);
- }
-
- /**
- * <p>Set the stroke default color for the drawable.</p>
+ * <p>Set the segment default color for the drawable.</p>
* <p>Note: changing this property will affect all instances of a drawable loaded from a
* resource. It is recommended to invoke {@link #mutate()} before changing this property.</p>
*
* @param color The color of the stroke
* @see #mutate()
- * @see #setStroke(int, int, float, float)
*/
- public void setStrokeDefaultColor(@ColorInt int color) {
- mState.setStrokeColor(color);
+ public void setSegmentDefaultColor(@ColorInt int color) {
+ mState.setSegmentColor(color);
}
/**
@@ -144,15 +103,12 @@
mState.setPointRectColor(color);
}
- private void setStrokeInternal(int width, float dashWidth, float dashGap) {
- mStrokePaint.setStrokeWidth(width);
-
- mDashedStrokePaint.setStrokeWidth(width);
- DashPathEffect e = null;
- if (dashWidth > 0) {
- e = new DashPathEffect(new float[] { dashWidth, dashGap }, 0);
- }
- mDashedStrokePaint.setPathEffect(e);
+ /**
+ * Set the segments and points that constitute the drawable.
+ */
+ public void setParts(List<Part> parts) {
+ mParts.clear();
+ mParts.addAll(parts);
invalidateSelf();
}
@@ -160,16 +116,18 @@
/**
* Set the segments and points that constitute the drawable.
*/
- public void setParts(List<Part> parts) {
- mParts.clear();
- mParts.addAll(parts);
+ public void setParts(@NonNull Part... parts) {
+ setParts(Arrays.asList(parts));
}
/**
- * Set the segments and points that constitute the drawable.
+ * Set whether a tracker is drawn on top of this NotificationProgressDrawable.
*/
- public void setParts(@NonNull Part... parts) {
- setParts(Arrays.asList(parts));
+ public void setHasTrackerIcon(boolean hasTrackerIcon) {
+ if (mHasTrackerIcon != hasTrackerIcon) {
+ mHasTrackerIcon = hasTrackerIcon;
+ invalidateSelf();
+ }
}
@Override
@@ -181,6 +139,7 @@
float x = (float) getBounds().left;
final float centerY = (float) getBounds().centerY();
final float totalWidth = (float) getBounds().width();
+ float segPointGap = mState.mSegPointGap;
final int numParts = mParts.size();
for (int iPart = 0; iPart < numParts; iPart++) {
@@ -188,15 +147,19 @@
final Part prevPart = iPart == 0 ? null : mParts.get(iPart - 1);
final Part nextPart = iPart + 1 == numParts ? null : mParts.get(iPart + 1);
if (part instanceof Segment segment) {
+ // Update the segment-point gap to 2X upon seeing the first faded segment.
+ // (Assuming that all segments before are solid, and all segments after are faded.)
+ if (segment.mFaded) {
+ segPointGap = mState.mSegPointGap * 2;
+ }
final float segWidth = segment.mFraction * totalWidth;
// Advance the start position to account for a point immediately prior.
- final float startOffset = getSegStartOffset(prevPart, pointRadius,
- mState.mSegPointGap, x);
+ final float startOffset = getSegStartOffset(prevPart, pointRadius, segPointGap, x);
final float start = x + startOffset;
// Retract the end position to account for the padding and a point immediately
// after.
- final float endOffset = getSegEndOffset(nextPart, pointRadius, mState.mSegPointGap,
- mState.mSegSegGap, x + segWidth, totalWidth);
+ final float endOffset = getSegEndOffset(segment, nextPart, pointRadius, segPointGap,
+ mState.mSegSegGap, x + segWidth, totalWidth, mHasTrackerIcon);
final float end = x + segWidth - endOffset;
// Advance the current position to account for the segment's fraction of the total
@@ -206,35 +169,15 @@
// No space left to draw the segment
if (start > end) continue;
- if (segment.mDashed) {
- // No caps when the segment is dashed.
+ final float radiusY = segment.mFaded ? mState.mFadedSegmentHeight / 2F
+ : mState.mSegmentHeight / 2F;
+ final float cornerRadius = mState.mSegmentCornerRadius;
- mDashedStrokePaint.setColor(segment.mColor != Color.TRANSPARENT ? segment.mColor
- : mState.mFadedStrokeColor);
- canvas.drawLine(start, centerY, end, centerY, mDashedStrokePaint);
- } else if (end - start < mState.mStrokeWidth) {
- // Not enough segment length to draw the caps
+ mFillPaint.setColor(segment.mColor != Color.TRANSPARENT ? segment.mColor
+ : (segment.mFaded ? mState.mFadedSegmentColor : mState.mSegmentColor));
- final float rad = (end - start) / 2F;
- final float capWidth = mStrokePaint.getStrokeWidth() / 2F;
-
- mFillPaint.setColor(segment.mColor != Color.TRANSPARENT ? segment.mColor
- : mState.mStrokeColor);
-
- mSegRectF.set(start, centerY - capWidth, end, centerY + capWidth);
- canvas.drawRoundRect(mSegRectF, rad, rad, mFillPaint);
- } else {
- // Leave space for the rounded line cap which extends beyond start/end.
- final float capWidth = mStrokePaint.getStrokeWidth() / 2F;
-
- // Transparent is not allowed (and also is the default in the data), so use that
- // as a sentinel to be replaced by default
- mStrokePaint.setColor(segment.mColor != Color.TRANSPARENT ? segment.mColor
- : mState.mStrokeColor);
-
- canvas.drawLine(start + capWidth, centerY, end - capWidth, centerY,
- mStrokePaint);
- }
+ mSegRectF.set(start, centerY - radiusY, end, centerY + radiusY);
+ canvas.drawRoundRect(mSegRectF, cornerRadius, cornerRadius, mFillPaint);
} else if (part instanceof Point point) {
final float pointWidth = 2 * pointRadius;
float start = x - pointRadius;
@@ -275,10 +218,17 @@
return pointOffset + pointRadius + segPointGap;
}
- private static float getSegEndOffset(Part nextPart, float pointRadius, float segPointGap,
- float segSegGap, float endX, float totalWidth) {
+ private static float getSegEndOffset(Segment seg, Part nextPart, float pointRadius,
+ float segPointGap,
+ float segSegGap, float endX, float totalWidth, boolean hasTrackerIcon) {
if (nextPart == null) return 0F;
- if (!(nextPart instanceof Point)) return segSegGap;
+ if (nextPart instanceof Segment nextSeg) {
+ if (!seg.mFaded && nextSeg.mFaded) {
+ // @see Segment#mFaded
+ return hasTrackerIcon ? 0F : segSegGap * 4F;
+ }
+ return segSegGap;
+ }
final float pointWidth = 2 * pointRadius;
final float pointOffset = (endX + pointRadius > totalWidth && totalWidth > pointWidth)
@@ -439,21 +389,17 @@
// Extract the theme attributes, if any.
state.mThemeAttrsSegments = a.extractThemeAttrs();
- final int width = a.getDimensionPixelSize(
- R.styleable.NotificationProgressDrawableSegments_width, state.mStrokeWidth);
- final float dashWidth = a.getDimension(
- R.styleable.NotificationProgressDrawableSegments_dashWidth, state.mStrokeDashWidth);
-
+ state.mSegmentHeight = a.getDimension(
+ R.styleable.NotificationProgressDrawableSegments_height, state.mSegmentHeight);
+ state.mFadedSegmentHeight = a.getDimension(
+ R.styleable.NotificationProgressDrawableSegments_fadedHeight,
+ state.mFadedSegmentHeight);
+ state.mSegmentCornerRadius = a.getDimension(
+ R.styleable.NotificationProgressDrawableSegments_cornerRadius,
+ state.mSegmentCornerRadius);
final int color = a.getColor(R.styleable.NotificationProgressDrawableSegments_color,
- state.mStrokeColor);
-
- if (dashWidth != 0.0f) {
- final float dashGap = a.getDimension(
- R.styleable.NotificationProgressDrawableSegments_dashGap, state.mStrokeDashGap);
- setStroke(width, color, dashWidth, dashGap);
- } else {
- setStroke(width, color);
- }
+ state.mSegmentColor);
+ setSegmentDefaultColor(color);
}
private void updatePointsFromTypedArray(TypedArray a) {
@@ -532,11 +478,24 @@
/**
* A segment is a part of the progress bar with non-zero length. For example, it can
* represent a portion in a navigation journey with certain traffic condition.
+ *
*/
public static final class Segment implements Part {
private final float mFraction;
@ColorInt private final int mColor;
- private final boolean mDashed;
+ /** Whether the segment is faded or not.
+ * <p>
+ * <pre>
+ * When mFaded is set to true, a combination of the following is done to the segment:
+ * 1. The drawing color is mColor with opacity updated to 15%.
+ * 2. The segment-point gap is 2X the segment-point gap for non-faded segments.
+ * 3. The gap between faded and non-faded segments is:
+ * 4X the segment-segment gap, when there is no tracker icon
+ * 0, when there is tracker icon
+ * </pre>
+ * </p>
+ */
+ private final boolean mFaded;
public Segment(float fraction) {
this(fraction, Color.TRANSPARENT);
@@ -546,10 +505,10 @@
this(fraction, color, false);
}
- public Segment(float fraction, @ColorInt int color, boolean dashed) {
+ public Segment(float fraction, @ColorInt int color, boolean faded) {
mFraction = fraction;
mColor = color;
- mDashed = dashed;
+ mFaded = faded;
}
public float getFraction() {
@@ -560,14 +519,14 @@
return this.mColor;
}
- public boolean getDashed() {
- return this.mDashed;
+ public boolean getFaded() {
+ return this.mFaded;
}
@Override
public String toString() {
- return "Segment(fraction=" + this.mFraction + ", color=" + this.mColor + ", dashed="
- + this.mDashed + ')';
+ return "Segment(fraction=" + this.mFraction + ", color=" + this.mColor + ", faded="
+ + this.mFaded + ')';
}
// Needed for unit tests
@@ -580,12 +539,12 @@
Segment that = (Segment) other;
if (Float.compare(this.mFraction, that.mFraction) != 0) return false;
if (this.mColor != that.mColor) return false;
- return this.mDashed == that.mDashed;
+ return this.mFaded == that.mFaded;
}
@Override
public int hashCode() {
- return Objects.hash(mFraction, mColor, mDashed);
+ return Objects.hash(mFraction, mColor, mFaded);
}
}
@@ -675,11 +634,11 @@
int mChangingConfigurations;
float mSegSegGap = 0.0f;
float mSegPointGap = 0.0f;
- int mStrokeWidth = 0;
- int mStrokeColor;
- int mFadedStrokeColor;
- float mStrokeDashWidth = 0.0f;
- float mStrokeDashGap = 0.0f;
+ float mSegmentHeight;
+ float mFadedSegmentHeight;
+ float mSegmentCornerRadius;
+ int mSegmentColor;
+ int mFadedSegmentColor;
float mPointRadius;
float mPointRectInset;
float mPointRectCornerRadius;
@@ -699,11 +658,11 @@
mChangingConfigurations = orig.mChangingConfigurations;
mSegSegGap = orig.mSegSegGap;
mSegPointGap = orig.mSegPointGap;
- mStrokeWidth = orig.mStrokeWidth;
- mStrokeColor = orig.mStrokeColor;
- mFadedStrokeColor = orig.mFadedStrokeColor;
- mStrokeDashWidth = orig.mStrokeDashWidth;
- mStrokeDashGap = orig.mStrokeDashGap;
+ mSegmentHeight = orig.mSegmentHeight;
+ mFadedSegmentHeight = orig.mFadedSegmentHeight;
+ mSegmentCornerRadius = orig.mSegmentCornerRadius;
+ mSegmentColor = orig.mSegmentColor;
+ mFadedSegmentColor = orig.mFadedSegmentColor;
mPointRadius = orig.mPointRadius;
mPointRectInset = orig.mPointRectInset;
mPointRectCornerRadius = orig.mPointRectCornerRadius;
@@ -721,17 +680,17 @@
}
private void applyDensityScaling(int sourceDensity, int targetDensity) {
- if (mStrokeWidth > 0) {
- mStrokeWidth = scaleFromDensity(
- mStrokeWidth, sourceDensity, targetDensity, true);
+ if (mSegmentHeight > 0) {
+ mSegmentHeight = scaleFromDensity(
+ mSegmentHeight, sourceDensity, targetDensity);
}
- if (mStrokeDashWidth > 0) {
- mStrokeDashWidth = scaleFromDensity(
- mStrokeDashWidth, sourceDensity, targetDensity);
+ if (mFadedSegmentHeight > 0) {
+ mFadedSegmentHeight = scaleFromDensity(
+ mFadedSegmentHeight, sourceDensity, targetDensity);
}
- if (mStrokeDashGap > 0) {
- mStrokeDashGap = scaleFromDensity(
- mStrokeDashGap, sourceDensity, targetDensity);
+ if (mSegmentCornerRadius > 0) {
+ mSegmentCornerRadius = scaleFromDensity(
+ mSegmentCornerRadius, sourceDensity, targetDensity);
}
if (mPointRadius > 0) {
mPointRadius = scaleFromDensity(
@@ -788,17 +747,9 @@
}
}
- public void setStroke(int width, int color, float dashWidth, float dashGap) {
- mStrokeWidth = width;
- mStrokeDashWidth = dashWidth;
- mStrokeDashGap = dashGap;
-
- setStrokeColor(color);
- }
-
- public void setStrokeColor(int color) {
- mStrokeColor = color;
- mFadedStrokeColor = getFadedColor(color);
+ public void setSegmentColor(int color) {
+ mSegmentColor = color;
+ mFadedSegmentColor = getFadedColor(color);
}
public void setPointRectColor(int color) {
@@ -808,11 +759,14 @@
}
/**
- * Get a color with an opacity that's 50% of the input color.
+ * Get a color with an opacity that's 25% of the input color.
*/
@ColorInt
static int getFadedColor(@ColorInt int color) {
- return Color.argb(Color.alpha(color) / 2, Color.red(color), Color.green(color),
+ return Color.argb(
+ (int) (Color.alpha(color) * 0.25f + 0.5f),
+ Color.red(color),
+ Color.green(color),
Color.blue(color));
}
@@ -836,15 +790,6 @@
}
private void updateLocalState() {
- final State state = mState;
-
- mStrokePaint.setStrokeWidth(state.mStrokeWidth);
- mDashedStrokePaint.setStrokeWidth(state.mStrokeWidth);
-
- if (state.mStrokeDashWidth != 0.0f) {
- final DashPathEffect e = new DashPathEffect(
- new float[] { state.mStrokeDashWidth, state.mStrokeDashGap }, 0);
- mDashedStrokePaint.setPathEffect(e);
- }
+ // NO-OP
}
}
diff --git a/core/java/com/android/internal/widget/NotificationRowIconView.java b/core/java/com/android/internal/widget/NotificationRowIconView.java
index 5fc61b0..c96e979 100644
--- a/core/java/com/android/internal/widget/NotificationRowIconView.java
+++ b/core/java/com/android/internal/widget/NotificationRowIconView.java
@@ -19,12 +19,7 @@
import android.annotation.Nullable;
import android.app.Flags;
import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.BitmapShader;
-import android.graphics.Canvas;
-import android.graphics.Paint;
import android.graphics.Rect;
-import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.util.AttributeSet;
@@ -41,7 +36,6 @@
public class NotificationRowIconView extends CachingIconView {
private NotificationIconProvider mIconProvider;
- private boolean mApplyCircularCrop = false;
private Drawable mAppIcon = null;
// Padding, background and colors set on the view prior to being overridden when showing the app
@@ -221,84 +215,6 @@
}
}
- @Nullable
- @Override
- Drawable loadSizeRestrictedIcon(@Nullable Icon icon) {
- final Drawable original = super.loadSizeRestrictedIcon(icon);
- final Drawable result;
- if (mApplyCircularCrop) {
- result = makeCircularDrawable(original);
- } else {
- result = original;
- }
-
- return result;
- }
-
- /**
- * Enables circle crop that makes given image circular
- */
- @RemotableViewMethod(asyncImpl = "setApplyCircularCropAsync")
- public void setApplyCircularCrop(boolean applyCircularCrop) {
- mApplyCircularCrop = applyCircularCrop;
- }
-
- /**
- * Async version of {@link NotificationRowIconView#setApplyCircularCrop}
- */
- public Runnable setApplyCircularCropAsync(boolean applyCircularCrop) {
- mApplyCircularCrop = applyCircularCrop;
- return () -> {
- };
- }
-
- @Nullable
- private Drawable makeCircularDrawable(@Nullable Drawable original) {
- if (original == null) {
- return original;
- }
-
- final Bitmap source = drawableToBitmap(original);
-
- int size = Math.min(source.getWidth(), source.getHeight());
-
- Bitmap squared = Bitmap.createScaledBitmap(source, size, size, /* filter= */ false);
- Bitmap result = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
-
- final Canvas canvas = new Canvas(result);
- final Paint paint = new Paint();
- paint.setShader(
- new BitmapShader(squared, BitmapShader.TileMode.CLAMP,
- BitmapShader.TileMode.CLAMP));
- paint.setAntiAlias(true);
- float radius = size / 2f;
- canvas.drawCircle(radius, radius, radius, paint);
- return new BitmapDrawable(getResources(), result);
- }
-
- private static Bitmap drawableToBitmap(Drawable drawable) {
- if (drawable instanceof BitmapDrawable bitmapDrawable) {
- final Bitmap bitmap = bitmapDrawable.getBitmap();
- if (bitmap.getConfig() == Bitmap.Config.HARDWARE) {
- return bitmap.copy(Bitmap.Config.ARGB_8888, false);
- } else {
- return bitmap;
- }
- }
-
- int width = drawable.getIntrinsicWidth();
- width = width > 0 ? width : 1;
- int height = drawable.getIntrinsicHeight();
- height = height > 0 ? height : 1;
-
- Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
- Canvas canvas = new Canvas(bitmap);
- drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
- drawable.draw(canvas);
-
- return bitmap;
- }
-
/**
* A provider that allows this view to verify whether it should use the app icon instead of the
* icon provided to it via setImageIcon, as well as actually fetching the app icon. It should
diff --git a/core/jni/android_hardware_OverlayProperties.cpp b/core/jni/android_hardware_OverlayProperties.cpp
index bb4084e..f64dec8 100644
--- a/core/jni/android_hardware_OverlayProperties.cpp
+++ b/core/jni/android_hardware_OverlayProperties.cpp
@@ -106,7 +106,7 @@
jlong nativeObject) {
gui::OverlayProperties* overlayProperties =
reinterpret_cast<gui::OverlayProperties*>(nativeObject);
- if (overlayProperties->lutProperties.has_value()) {
+ if (!overlayProperties || !overlayProperties->lutProperties) {
return NULL;
}
auto& lutProperties = overlayProperties->lutProperties.value();
diff --git a/core/jni/android_media_AudioRecord.cpp b/core/jni/android_media_AudioRecord.cpp
index f9d00ed..5a18392 100644
--- a/core/jni/android_media_AudioRecord.cpp
+++ b/core/jni/android_media_AudioRecord.cpp
@@ -584,14 +584,23 @@
return lpRecorder->setInputDevice(device_id) == NO_ERROR;
}
-static jint android_media_AudioRecord_getRoutedDeviceId(
- JNIEnv *env, jobject thiz) {
-
+static jintArray android_media_AudioRecord_getRoutedDeviceIds(JNIEnv *env, jobject thiz) {
sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
- if (lpRecorder == 0) {
- return 0;
+ if (lpRecorder == NULL) {
+ return NULL;
}
- return (jint)lpRecorder->getRoutedDeviceId();
+ DeviceIdVector deviceIds = lpRecorder->getRoutedDeviceIds();
+ jintArray result;
+ result = env->NewIntArray(deviceIds.size());
+ if (result == NULL) {
+ return NULL;
+ }
+ jint *values = env->GetIntArrayElements(result, 0);
+ for (unsigned int i = 0; i < deviceIds.size(); i++) {
+ values[i++] = static_cast<jint>(deviceIds[i]);
+ }
+ env->ReleaseIntArrayElements(result, values, 0);
+ return result;
}
// Enable and Disable Callback methods are synchronized on the Java side
@@ -821,8 +830,7 @@
// name, signature, funcPtr
{"native_start", "(II)I", (void *)android_media_AudioRecord_start},
{"native_stop", "()V", (void *)android_media_AudioRecord_stop},
- {"native_setup",
- "(Ljava/lang/Object;Ljava/lang/Object;[IIIII[ILandroid/os/Parcel;JII)I",
+ {"native_setup", "(Ljava/lang/Object;Ljava/lang/Object;[IIIII[ILandroid/os/Parcel;JII)I",
(void *)android_media_AudioRecord_setup},
{"native_finalize", "()V", (void *)android_media_AudioRecord_finalize},
{"native_release", "()V", (void *)android_media_AudioRecord_release},
@@ -846,7 +854,7 @@
{"native_getMetrics", "()Landroid/os/PersistableBundle;",
(void *)android_media_AudioRecord_native_getMetrics},
{"native_setInputDevice", "(I)Z", (void *)android_media_AudioRecord_setInputDevice},
- {"native_getRoutedDeviceId", "()I", (void *)android_media_AudioRecord_getRoutedDeviceId},
+ {"native_getRoutedDeviceIds", "()[I", (void *)android_media_AudioRecord_getRoutedDeviceIds},
{"native_enableDeviceCallback", "()V",
(void *)android_media_AudioRecord_enableDeviceCallback},
{"native_disableDeviceCallback", "()V",
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index 8eaa7aa..3d9a19e 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -109,6 +109,7 @@
// Valid only if an AudioDevicePort
jfieldID mType;
jfieldID mAddress;
+ jfieldID mSpeakerLayoutChannelMask;
// other fields unused by JNI
} gAudioPortFields;
@@ -1084,6 +1085,8 @@
strncpy(nAudioPortConfig->ext.device.address,
nDeviceAddress, AUDIO_DEVICE_MAX_ADDRESS_LEN - 1);
env->ReleaseStringUTFChars(jDeviceAddress, nDeviceAddress);
+ nAudioPortConfig->ext.device.speaker_layout_channel_mask = outChannelMaskToNative(
+ env->GetIntField(jAudioDevicePort, gAudioPortFields.mSpeakerLayoutChannelMask));
env->DeleteLocalRef(jDeviceAddress);
env->DeleteLocalRef(jAudioDevicePort);
return jStatus;
@@ -1541,10 +1544,12 @@
.encapsulation_metadata_types));
ALOGV("convertAudioPortFromNative is a device %08x", nAudioPort->ext.device.type);
ScopedLocalRef<jstring> jAddress(env, env->NewStringUTF(nAudioPort->ext.device.address));
+ int speakerLayoutChannelMask = outChannelMaskFromNative(
+ nAudioPort->active_config.ext.device.speaker_layout_channel_mask);
jAudioPort->reset(env->NewObject(gAudioDevicePortClass, gAudioDevicePortCstor,
jHandle.get(), jDeviceName.get(), jAudioProfiles.get(),
jGains.get(), nAudioPort->ext.device.type, jAddress.get(),
- jEncapsulationModes.get(),
+ speakerLayoutChannelMask, jEncapsulationModes.get(),
jEncapsulationMetadataTypes.get(),
jAudioDescriptors.get()));
} else if (nAudioPort->type == AUDIO_PORT_TYPE_MIX) {
@@ -3705,14 +3710,15 @@
gAudioDevicePortCstor =
GetMethodIDOrDie(env, audioDevicePortClass, "<init>",
"(Landroid/media/AudioHandle;Ljava/lang/String;Ljava/util/List;"
- "[Landroid/media/AudioGain;ILjava/lang/String;[I[I"
+ "[Landroid/media/AudioGain;ILjava/lang/String;I[I[I"
"Ljava/util/List;)V");
// When access AudioPort as AudioDevicePort
gAudioPortFields.mType = GetFieldIDOrDie(env, audioDevicePortClass, "mType", "I");
gAudioPortFields.mAddress = GetFieldIDOrDie(env, audioDevicePortClass, "mAddress",
"Ljava/lang/String;");
-
+ gAudioPortFields.mSpeakerLayoutChannelMask =
+ GetFieldIDOrDie(env, audioDevicePortClass, "mSpeakerLayoutChannelMask", "I");
jclass audioMixPortClass = FindClassOrDie(env, "android/media/AudioMixPort");
gAudioMixPortClass = MakeGlobalRefOrDie(env, audioMixPortClass);
gAudioMixPortCstor =
diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp
index 1557f9e..5d4d1ce 100644
--- a/core/jni/android_media_AudioTrack.cpp
+++ b/core/jni/android_media_AudioTrack.cpp
@@ -1190,15 +1190,23 @@
}
return lpTrack->setOutputDevice(device_id) == NO_ERROR;
}
-
-static jint android_media_AudioTrack_getRoutedDeviceId(
- JNIEnv *env, jobject thiz) {
-
+static jintArray android_media_AudioTrack_getRoutedDeviceIds(JNIEnv *env, jobject thiz) {
sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
if (lpTrack == NULL) {
- return 0;
+ return NULL;
}
- return (jint)lpTrack->getRoutedDeviceId();
+ DeviceIdVector deviceIds = lpTrack->getRoutedDeviceIds();
+ jintArray result;
+ result = env->NewIntArray(deviceIds.size());
+ if (result == NULL) {
+ return NULL;
+ }
+ jint *values = env->GetIntArrayElements(result, 0);
+ for (unsigned int i = 0; i < deviceIds.size(); i++) {
+ values[i++] = static_cast<jint>(deviceIds[i]);
+ }
+ env->ReleaseIntArrayElements(result, values, 0);
+ return result;
}
static void android_media_AudioTrack_enableDeviceCallback(
@@ -1503,7 +1511,7 @@
(void *)android_media_AudioTrack_setAuxEffectSendLevel},
{"native_attachAuxEffect", "(I)I", (void *)android_media_AudioTrack_attachAuxEffect},
{"native_setOutputDevice", "(I)Z", (void *)android_media_AudioTrack_setOutputDevice},
- {"native_getRoutedDeviceId", "()I", (void *)android_media_AudioTrack_getRoutedDeviceId},
+ {"native_getRoutedDeviceIds", "()[I", (void *)android_media_AudioTrack_getRoutedDeviceIds},
{"native_enableDeviceCallback", "()V",
(void *)android_media_AudioTrack_enableDeviceCallback},
{"native_disableDeviceCallback", "()V",
diff --git a/core/jni/android_media_DeviceCallback.cpp b/core/jni/android_media_DeviceCallback.cpp
index a1a0351..ccdf633 100644
--- a/core/jni/android_media_DeviceCallback.cpp
+++ b/core/jni/android_media_DeviceCallback.cpp
@@ -61,21 +61,20 @@
}
void JNIDeviceCallback::onAudioDeviceUpdate(audio_io_handle_t audioIo,
- audio_port_handle_t deviceId)
-{
+ const DeviceIdVector& deviceIds) {
JNIEnv *env = AndroidRuntime::getJNIEnv();
if (env == NULL) {
return;
}
- ALOGV("%s audioIo %d deviceId %d", __FUNCTION__, audioIo, deviceId);
- env->CallStaticVoidMethod(mClass,
- mPostEventFromNative,
- mObject,
- AUDIO_NATIVE_EVENT_ROUTING_CHANGE, deviceId, 0, NULL);
+ ALOGV("%s audioIo %d deviceIds %s", __FUNCTION__, audioIo, toString(deviceIds).c_str());
+ // Java should query the new device ids once it gets the event.
+ // TODO(b/378505346): Pass the deviceIds to Java to avoid race conditions.
+ env->CallStaticVoidMethod(mClass, mPostEventFromNative, mObject,
+ AUDIO_NATIVE_EVENT_ROUTING_CHANGE, 0 /*arg1*/, 0 /*arg2*/,
+ NULL /*obj*/);
if (env->ExceptionCheck()) {
ALOGW("An exception occurred while notifying an event.");
env->ExceptionClear();
}
}
-
diff --git a/core/jni/android_media_DeviceCallback.h b/core/jni/android_media_DeviceCallback.h
index 7ae788e..0c9ccc8 100644
--- a/core/jni/android_media_DeviceCallback.h
+++ b/core/jni/android_media_DeviceCallback.h
@@ -31,8 +31,7 @@
JNIDeviceCallback(JNIEnv* env, jobject thiz, jobject weak_thiz, jmethodID postEventFromNative);
~JNIDeviceCallback();
- virtual void onAudioDeviceUpdate(audio_io_handle_t audioIo,
- audio_port_handle_t deviceId);
+ virtual void onAudioDeviceUpdate(audio_io_handle_t audioIo, const DeviceIdVector& deviceIds);
private:
void sendEvent(int event);
diff --git a/core/jni/android_os_PerformanceHintManager.cpp b/core/jni/android_os_PerformanceHintManager.cpp
index aebe7ea..0f78c9e 100644
--- a/core/jni/android_os_PerformanceHintManager.cpp
+++ b/core/jni/android_os_PerformanceHintManager.cpp
@@ -88,9 +88,10 @@
"Failed to find required symbol "
"APerformanceHint_getPreferredUpdateRateNanos!");
- gAPH_createSessionFn = (APH_createSession)dlsym(handle_, "APerformanceHint_createSession");
+ gAPH_createSessionFn =
+ (APH_createSession)dlsym(handle_, "APerformanceHint_createSessionFromJava");
LOG_ALWAYS_FATAL_IF(gAPH_createSessionFn == nullptr,
- "Failed to find required symbol APerformanceHint_createSession!");
+ "Failed to find required symbol APerformanceHint_createSessionFromJava!");
gAPH_updateTargetWorkDurationFn =
(APH_updateTargetWorkDuration)dlsym(handle_,
@@ -106,9 +107,9 @@
"Failed to find required symbol "
"APerformanceHint_reportActualWorkDuration!");
- gAPH_closeSessionFn = (APH_closeSession)dlsym(handle_, "APerformanceHint_closeSession");
+ gAPH_closeSessionFn = (APH_closeSession)dlsym(handle_, "APerformanceHint_closeSessionFromJava");
LOG_ALWAYS_FATAL_IF(gAPH_closeSessionFn == nullptr,
- "Failed to find required symbol APerformanceHint_closeSession!");
+ "Failed to find required symbol APerformanceHint_closeSessionFromJava!");
gAPH_sendHintFn = (APH_sendHint)dlsym(handle_, "APerformanceHint_sendHint");
LOG_ALWAYS_FATAL_IF(gAPH_sendHintFn == nullptr,
diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp
index 49191ee..7ef7829 100644
--- a/core/jni/android_util_Process.cpp
+++ b/core/jni/android_util_Process.cpp
@@ -33,6 +33,7 @@
#include <algorithm>
#include <array>
+#include <cctype>
#include <cstring>
#include <limits>
#include <memory>
@@ -1008,6 +1009,8 @@
}
}
if ((mode&PROC_OUT_STRING) != 0 && di < NS) {
+ std::replace_if(buffer+start, buffer+end,
+ [](unsigned char c){ return !std::isprint(c); }, '?');
jstring str = env->NewStringUTF(buffer+start);
env->SetObjectArrayElement(outStrings, di, str);
}
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 82b463e..593b982 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -758,54 +758,64 @@
auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl*>(nativeObject);
- ScopedIntArrayRW joffsets(env, joffsetArray);
- if (joffsets.get() == nullptr) {
- jniThrowRuntimeException(env, "Failed to get ScopedIntArrayRW from joffsetArray");
- return;
- }
- ScopedIntArrayRW jdimensions(env, jdimensionArray);
- if (jdimensions.get() == nullptr) {
- jniThrowRuntimeException(env, "Failed to get ScopedIntArrayRW from jdimensionArray");
- return;
- }
- ScopedIntArrayRW jsizes(env, jsizeArray);
- if (jsizes.get() == nullptr) {
- jniThrowRuntimeException(env, "Failed to get ScopedIntArrayRW from jsizeArray");
- return;
- }
- ScopedIntArrayRW jsamplingKeys(env, jsamplingKeyArray);
- if (jsamplingKeys.get() == nullptr) {
- jniThrowRuntimeException(env, "Failed to get ScopedIntArrayRW from jsamplingKeyArray");
- return;
- }
+ std::vector<int32_t> offsets;
+ std::vector<int32_t> dimensions;
+ std::vector<int32_t> sizes;
+ std::vector<int32_t> samplingKeys;
+ int32_t fd = -1;
- jsize numLuts = env->GetArrayLength(jdimensionArray);
- std::vector<int32_t> offsets(joffsets.get(), joffsets.get() + numLuts);
- std::vector<int32_t> dimensions(jdimensions.get(), jdimensions.get() + numLuts);
- std::vector<int32_t> sizes(jsizes.get(), jsizes.get() + numLuts);
- std::vector<int32_t> samplingKeys(jsamplingKeys.get(), jsamplingKeys.get() + numLuts);
+ if (jdimensionArray) {
+ jsize numLuts = env->GetArrayLength(jdimensionArray);
+ ScopedIntArrayRW joffsets(env, joffsetArray);
+ if (joffsets.get() == nullptr) {
+ jniThrowRuntimeException(env, "Failed to get ScopedIntArrayRW from joffsetArray");
+ return;
+ }
+ ScopedIntArrayRW jdimensions(env, jdimensionArray);
+ if (jdimensions.get() == nullptr) {
+ jniThrowRuntimeException(env, "Failed to get ScopedIntArrayRW from jdimensionArray");
+ return;
+ }
+ ScopedIntArrayRW jsizes(env, jsizeArray);
+ if (jsizes.get() == nullptr) {
+ jniThrowRuntimeException(env, "Failed to get ScopedIntArrayRW from jsizeArray");
+ return;
+ }
+ ScopedIntArrayRW jsamplingKeys(env, jsamplingKeyArray);
+ if (jsamplingKeys.get() == nullptr) {
+ jniThrowRuntimeException(env, "Failed to get ScopedIntArrayRW from jsamplingKeyArray");
+ return;
+ }
- ScopedFloatArrayRW jbuffers(env, jbufferArray);
- if (jbuffers.get() == nullptr) {
- jniThrowRuntimeException(env, "Failed to get ScopedFloatArrayRW from jbufferArray");
- return;
- }
+ if (numLuts > 0) {
+ offsets = std::vector<int32_t>(joffsets.get(), joffsets.get() + numLuts);
+ dimensions = std::vector<int32_t>(jdimensions.get(), jdimensions.get() + numLuts);
+ sizes = std::vector<int32_t>(jsizes.get(), jsizes.get() + numLuts);
+ samplingKeys = std::vector<int32_t>(jsamplingKeys.get(), jsamplingKeys.get() + numLuts);
- // create the shared memory and copy jbuffers
- size_t bufferSize = jbuffers.size() * sizeof(float);
- int32_t fd = ashmem_create_region("lut_shread_mem", bufferSize);
- if (fd < 0) {
- jniThrowRuntimeException(env, "ashmem_create_region() failed");
- return;
+ ScopedFloatArrayRW jbuffers(env, jbufferArray);
+ if (jbuffers.get() == nullptr) {
+ jniThrowRuntimeException(env, "Failed to get ScopedFloatArrayRW from jbufferArray");
+ return;
+ }
+
+ // create the shared memory and copy jbuffers
+ size_t bufferSize = jbuffers.size() * sizeof(float);
+ fd = ashmem_create_region("lut_shared_mem", bufferSize);
+ if (fd < 0) {
+ jniThrowRuntimeException(env, "ashmem_create_region() failed");
+ return;
+ }
+ void* ptr = mmap(nullptr, bufferSize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ if (ptr == MAP_FAILED) {
+ jniThrowRuntimeException(env, "Failed to map the shared memory");
+ return;
+ }
+ memcpy(ptr, jbuffers.get(), bufferSize);
+ // unmap
+ munmap(ptr, bufferSize);
+ }
}
- void* ptr = mmap(nullptr, bufferSize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
- if (ptr == MAP_FAILED) {
- jniThrowRuntimeException(env, "Failed to map the shared memory");
- return;
- }
- memcpy(ptr, jbuffers.get(), bufferSize);
- // unmap
- munmap(ptr, bufferSize);
transaction->setLuts(ctrl, base::unique_fd(fd), offsets, dimensions, sizes, samplingKeys);
}
@@ -1332,8 +1342,9 @@
}
}
-static jobject convertDeviceProductInfoToJavaObject(
- JNIEnv* env, const std::optional<DeviceProductInfo>& info) {
+static jobject convertDeviceProductInfoToJavaObject(JNIEnv* env,
+ const std::optional<DeviceProductInfo>& info,
+ bool isInternal) {
using ModelYear = android::DeviceProductInfo::ModelYear;
using ManufactureYear = android::DeviceProductInfo::ManufactureYear;
using ManufactureWeekAndYear = android::DeviceProductInfo::ManufactureWeekAndYear;
@@ -1368,7 +1379,8 @@
// Section 8.7 - Physical Address of HDMI Specification Version 1.3a
using android::hardware::display::IDeviceProductInfoConstants;
if (info->relativeAddress.size() != 4) {
- connectionToSinkType = IDeviceProductInfoConstants::CONNECTION_TO_SINK_UNKNOWN;
+ connectionToSinkType = isInternal ? IDeviceProductInfoConstants::CONNECTION_TO_SINK_BUILT_IN
+ : IDeviceProductInfoConstants::CONNECTION_TO_SINK_UNKNOWN;
} else if (info->relativeAddress[0] == 0) {
connectionToSinkType = IDeviceProductInfoConstants::CONNECTION_TO_SINK_BUILT_IN;
} else if (info->relativeAddress[1] == 0) {
@@ -1390,12 +1402,14 @@
jobject object =
env->NewObject(gStaticDisplayInfoClassInfo.clazz, gStaticDisplayInfoClassInfo.ctor);
- env->SetBooleanField(object, gStaticDisplayInfoClassInfo.isInternal,
- info.connectionType == ui::DisplayConnectionType::Internal);
+
+ const bool isInternal = info.connectionType == ui::DisplayConnectionType::Internal;
+ env->SetBooleanField(object, gStaticDisplayInfoClassInfo.isInternal, isInternal);
env->SetFloatField(object, gStaticDisplayInfoClassInfo.density, info.density);
env->SetBooleanField(object, gStaticDisplayInfoClassInfo.secure, info.secure);
env->SetObjectField(object, gStaticDisplayInfoClassInfo.deviceProductInfo,
- convertDeviceProductInfoToJavaObject(env, info.deviceProductInfo));
+ convertDeviceProductInfoToJavaObject(env, info.deviceProductInfo,
+ isInternal));
env->SetIntField(object, gStaticDisplayInfoClassInfo.installOrientation,
static_cast<uint32_t>(info.installOrientation));
return object;
@@ -2187,29 +2201,9 @@
return false;
}
- // Compute the count of data items we'll actually forward to Java.
- size_t count = 0;
- if (mRemovedVsyncId <= 0) {
- count = jankData.size();
- } else {
- for (const gui::JankData& frame : jankData) {
- if (frame.frameVsyncId <= mRemovedVsyncId) {
- count++;
- }
- }
- }
-
- if (count == 0) {
- return false;
- }
-
- jobjectArray jJankDataArray = env->NewObjectArray(count, gJankDataClassInfo.clazz, nullptr);
- for (size_t i = 0, j = 0; i < jankData.size() && j < count; i++) {
- // Filter any data for frames past our removal vsync.
- if (mRemovedVsyncId > 0 && jankData[i].frameVsyncId > mRemovedVsyncId) {
- continue;
- }
-
+ jobjectArray jJankDataArray =
+ env->NewObjectArray(jankData.size(), gJankDataClassInfo.clazz, nullptr);
+ for (size_t i = 0; i < jankData.size(); i++) {
// The exposed constants in SurfaceControl are simplified, so we need to translate the
// jank type we get from SF to what is exposed in Java.
int sfJankType = jankData[i].jankType;
@@ -2236,7 +2230,7 @@
jankData[i].frameVsyncId, javaJankType,
jankData[i].frameIntervalNs, jankData[i].scheduledAppFrameTimeNs,
jankData[i].actualAppFrameTimeNs);
- env->SetObjectArrayElement(jJankDataArray, j++, jJankData);
+ env->SetObjectArrayElement(jJankDataArray, i, jJankData);
env->DeleteLocalRef(jJankData);
}
diff --git a/core/res/Android.bp b/core/res/Android.bp
index 0e4e22b..8042b30 100644
--- a/core/res/Android.bp
+++ b/core/res/Android.bp
@@ -161,6 +161,7 @@
"android.app.flags-aconfig",
"android.appwidget.flags-aconfig",
"android.content.pm.flags-aconfig",
+ "android.media.audio-aconfig",
"android.provider.flags-aconfig",
"camera_platform_flags",
"android.net.platform.flags-aconfig",
@@ -170,6 +171,7 @@
"android.os.vibrator.flags-aconfig",
"android.media.tv.flags-aconfig",
"android.security.flags-aconfig",
+ "device_policy_aconfig_flags",
"com.android.hardware.input.input-aconfig",
"aconfig_trade_in_mode_flags",
"art-aconfig-flags",
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 3e0c120..9b2d338 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -914,13 +914,26 @@
android:featureFlag="android.provider.user_keys" />
<!-- Allows an application to set default account for new contacts.
- <p> This permission is only granted to system applications fulfilling the Contacts app role.
+ <p>This permission is only granted to system applications fulfilling the Contacts app role.
<p>Protection level: internal|role
@SystemApi
@hide
-->
<permission android:name="android.permission.SET_DEFAULT_ACCOUNT_FOR_CONTACTS"
- android:protectionLevel="internal|role" />
+ android:protectionLevel="internal|role"
+ android:featureFlag="!android.provider.new_default_account_api_enabled"/>
+
+ <!-- Allows an application to set default account for new contacts.
+ <p>This permission is only granted to system applications fulfilling the Contacts app role
+ and the application with known signers.
+ <p>Protection level: internal|role|knownSigner
+ @SystemApi
+ @hide
+ -->
+ <permission android:name="android.permission.SET_DEFAULT_ACCOUNT_FOR_CONTACTS"
+ android:protectionLevel="internal|role|knownSigner"
+ android:knownCerts="@array/config_setContactsDefaultAccountKnownSigners"
+ android:featureFlag="android.provider.new_default_account_api_enabled"/>
<!-- ====================================================================== -->
<!-- Permissions for accessing user's calendar -->
@@ -1080,6 +1093,52 @@
<permission android:name="android.permission.SATELLITE_COMMUNICATION"
android:protectionLevel="role|signature|privileged" />
+ <!-- ================================== -->
+ <!-- Permissions associated with picture and sound profiles -->
+ <!-- ================================== -->
+ <eat-comment />
+
+ <!-- @FlaggedApi(android.media.tv.flags.Flags.FLAG_APPLY_PICTURE_PROFILES)
+ Allows an app to apply a {@link MediaQualityManager.PictureProfile} to a layer via
+ {@link MediaCodec.PARAMETER_KEY_PICTURE_PROFILE} and, additionally, system apps via
+ {@link SurfaceControl.Transaction#setPictureProfileHandle}.
+ -->
+ <permission android:name="android.permission.APPLY_PICTURE_PROFILE"
+ android:protectionLevel="normal"
+ android:featureFlag="android.media.tv.flags.apply_picture_profiles"/>
+
+ <!-- @hide
+ Allows MediaQualityService to observe any {@link MediaQualityManager.PictureProfile}
+ applied to any layer in the system by apps via
+ {@link MediaCodec.PARAMETER_KEY_PICTURE_PROFILE} and by system apps via
+ {@link SurfaceControl.Transaction#setPictureProfileHandle}.
+ -->
+ <permission android:name="android.permission.OBSERVE_PICTURE_PROFILES"
+ android:protectionLevel="signature|privileged"
+ android:featureFlag="android.media.tv.flags.apply_picture_profiles"/>
+
+ <!--
+ @SystemApi
+ @FlaggedApi("android.media.tv.flags.media_quality_fw")
+ Allows an application to access its picture profile from the media quality database.
+ <p> Protection level: signature|privileged|vendor privileged
+ @hide
+ -->
+ <permission android:name="android.permission.MANAGE_GLOBAL_PICTURE_QUALITY_SERVICE"
+ android:protectionLevel="signature|privileged|vendorPrivileged"
+ android:featureFlag="android.media.tv.flags.media_quality_fw"/>
+
+ <!--
+ @SystemApi
+ @FlaggedApi("android.media.tv.flags.media_quality_fw")
+ Allows an application to access its sound profile from the media quality database.
+ <p> Protection level: signature|privileged|vendor privileged
+ @hide
+ -->
+ <permission android:name="android.permission.MANAGE_GLOBAL_SOUND_QUALITY_SERVICE"
+ android:protectionLevel="signature|privileged|vendorPrivileged"
+ android:featureFlag="android.media.tv.flags.media_quality_fw"/>
+
<!-- ====================================================================== -->
<!-- Permissions for accessing external storage -->
<!-- ====================================================================== -->
@@ -2590,6 +2649,22 @@
android:label="@string/permlab_getAccounts" />
<uses-permission android:name="android.permission.GET_ACCOUNTS"/>
+ <!-- @SystemApi Allows access to remove an account.
+ @FlaggedApi(android.app.admin.flags.Flags.FLAG_SPLIT_CREATE_MANAGED_PROFILE_ENABLED)
+ <p>Not for use by third-party applications.
+ @hide -->
+ <permission android:name="android.permission.REMOVE_ACCOUNTS"
+ android:protectionLevel="signature|role"
+ android:featureFlag="android.app.admin.flags.split_create_managed_profile_enabled" />
+
+ <!-- @SystemApi Allows access to copy an account to another user.
+ @FlaggedApi(android.app.admin.flags.Flags.FLAG_SPLIT_CREATE_MANAGED_PROFILE_ENABLED)
+ <p>Not for use by third-party applications.
+ @hide -->
+ <permission android:name="android.permission.COPY_ACCOUNTS"
+ android:protectionLevel="signature|role"
+ android:featureFlag="android.app.admin.flags.split_create_managed_profile_enabled" />
+
<!-- Allows applications to call into AccountAuthenticators.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.ACCOUNT_MANAGER"
@@ -4144,6 +4219,37 @@
<uses-permission android:name="android.permission.QUERY_ADVANCED_PROTECTION_MODE"
android:featureFlag="android.security.aapm_api"/>
+ <!-- Allows an application to read the state of the ForensicService
+ @FlaggedApi(android.security.Flags.FLAG_AFL_API)
+ @SystemApi
+ @hide -->
+ <permission android:name="android.permission.READ_FORENSIC_STATE"
+ android:featureFlag="android.security.afl_api"
+ android:protectionLevel="signature|privileged" />
+ <uses-permission android:name="android.permission.READ_FORENSIC_STATE"
+ android:featureFlag="android.security.afl_api"/>
+
+ <!-- Allows an application to change the state of the ForensicService
+ @FlaggedApi(android.security.Flags.FLAG_AFL_API)
+ @SystemApi
+ @hide -->
+ <permission android:name="android.permission.MANAGE_FORENSIC_STATE"
+ android:featureFlag="android.security.afl_api"
+ android:protectionLevel="signature|privileged" />
+ <uses-permission android:name="android.permission.MANAGE_FORENSIC_STATE"
+ android:featureFlag="android.security.afl_api"/>
+
+ <!-- Must be required by any ForensicEventTransportService to ensure that
+ only the system can bind to it.
+ @FlaggedApi(android.security.Flags.FLAG_AFL_API)
+ @SystemApi
+ @hide -->
+ <permission android:name="android.permission.BIND_FORENSIC_EVENT_TRANSPORT_SERVICE"
+ android:featureFlag="android.security.afl_api"
+ android:protectionLevel="signature" />
+ <uses-permission android:name="android.permission.BIND_FORENSIC_EVENT_TRANSPORT_SERVICE"
+ android:featureFlag="android.security.afl_api"/>
+
<!-- @SystemApi @hide Allows an application to set a device owner on retail demo devices.-->
<permission android:name="android.permission.PROVISION_DEMO_DEVICE"
android:protectionLevel="signature|setup|knownSigner"
@@ -4991,16 +5097,16 @@
android:protectionLevel="signature|privileged|role"
android:featureFlag="com.android.settingslib.flags.settings_catalyst" />
- <!-- @FlaggedApi(com.android.settingslib.flags.Flags.FLAG_SETTINGS_CATALYST)
+ <!-- @FlaggedApi(com.android.settingslib.flags.Flags.FLAG_WRITE_SYSTEM_PREFERENCE_PERMISSION_ENABLED)
Allows an application to access the Settings Preference services to write settings
values exposed by the system Settings app and system apps that contribute settings surfaced
in the Settings app.
<p>This allows the calling application to write settings values
through the host application, agnostic of underlying storage.
- <p>Protection Level: signature|privileged|appop - appop to be added in followup -->
+ <p>Protection Level: signature|privileged|appop -->
<permission android:name="android.permission.WRITE_SYSTEM_PREFERENCES"
- android:protectionLevel="signature|privileged"
- android:featureFlag="com.android.settingslib.flags.settings_catalyst" />
+ android:protectionLevel="signature|privileged|appop"
+ android:featureFlag="com.android.settingslib.flags.write_system_preference_permission_enabled" />
<!-- ========================================= -->
<!-- Permissions for special development tools -->
@@ -5497,7 +5603,8 @@
<permission android:name="android.permission.BIND_VISUAL_QUERY_DETECTION_SERVICE"
android:protectionLevel="signature" />
- <!-- Allows an application to subscribe to keyguard locked (i.e., showing) state.
+ <!-- Allows an application to subscribe to device locked and keyguard locked (i.e., showing)
+ state.
<p>Protection level: signature|role
<p>Intended for use by ROLE_ASSISTANT and signature apps only.
-->
@@ -6401,6 +6508,15 @@
<permission android:name="android.permission.CAPTURE_VOICE_COMMUNICATION_OUTPUT"
android:protectionLevel="signature|privileged|role" />
+ <!-- @SystemApi Allows an application to bypass concurrency restrictions while
+ recording audio. For example, apps with this permission can continue to record
+ while a voice call is active.</p>
+ @FlaggedApi(android.media.audio.Flags.FLAG_CONCURRENT_AUDIO_RECORD_BYPASS_PERMISSION)
+ @hide -->
+ <permission android:name="android.permission.BYPASS_CONCURRENT_RECORD_AUDIO_RESTRICTION"
+ android:featureFlag="android.media.audio.concurrent_audio_record_bypass_permission"
+ android:protectionLevel="signature|privileged|role" />
+
<!-- @SystemApi Allows an application to capture audio for hotword detection.
<p>Not for use by third-party applications.</p>
@hide -->
@@ -7844,7 +7960,31 @@
<!-- @SystemApi Allows an application to access shared libraries.
@hide -->
<permission android:name="android.permission.ACCESS_SHARED_LIBRARIES"
- android:protectionLevel="signature|installer" />
+ android:protectionLevel="signature|installer"
+ android:featureFlag="!android.content.pm.sdk_dependency_installer" />
+
+ <!-- @SystemApi Allows an application to access shared libraries.
+ @hide -->
+ <permission android:name="android.permission.ACCESS_SHARED_LIBRARIES"
+ android:protectionLevel="signature|installer|role"
+ android:featureFlag="android.content.pm.sdk_dependency_installer" />
+
+ <!-- @SystemApi Permission held by the system to allow binding to the dependency installer role
+ holder.
+ @FlaggedApi(android.content.pm.Flags.FLAG_SDK_DEPENDENCY_INSTALLER)
+ @hide -->
+ <permission android:name="android.permission.BIND_DEPENDENCY_INSTALLER"
+ android:protectionLevel="signature"
+ android:featureFlag="android.content.pm.sdk_dependency_installer" />
+
+ <!-- @SystemApi Allows an application to install shared libraries of types
+ {@link android.content.pm.SharedLibraryInfo#TYPE_STATIC} or
+ {@link android.content.pm.SharedLibraryInfo#TYPE_SDK_PACKAGE}.
+ @FlaggedApi(android.content.pm.Flags.FLAG_SDK_DEPENDENCY_INSTALLER)
+ @hide -->
+ <permission android:name="android.permission.INSTALL_DEPENDENCY_SHARED_LIBRARIES"
+ android:protectionLevel="signature|role"
+ android:featureFlag="android.content.pm.sdk_dependency_installer" />
<!-- Allows an app to log compat change usage.
@hide <p>Not for use by third-party applications.</p> -->
@@ -8536,6 +8676,17 @@
<permission android:name="android.permission.SETUP_FSVERITY"
android:protectionLevel="signature|privileged"/>
+ <!-- @SystemApi
+ @FlaggedApi(android.security.Flags.FLAG_SECURE_LOCKDOWN)
+ Allows an application to lock down the device into an enhanced security state.
+ <p>Not for use by third-party applications.
+ <p>Protection level: signature|privileged
+ @hide
+ -->
+ <permission android:name="android.permission.MANAGE_SECURE_LOCK_DEVICE"
+ android:protectionLevel="signature|privileged"
+ android:featureFlag="android.security.secure_lockdown" />
+
<!-- Allows app to enter trade-in-mode.
<p>Protection level: signature|privileged
@hide
@@ -8565,27 +8716,6 @@
<permission android:name="android.permission.RESERVED_FOR_TESTING_SIGNATURE"
android:protectionLevel="signature"/>
- <!--
- @SystemApi
- @FlaggedApi("android.media.tv.flags.media_quality_fw")
- Allows an application to access its picture profile from the media quality database.
- <p> Protection level: signature|privileged|vendor privileged
- @hide
- -->
- <permission android:name="android.permission.MANAGE_GLOBAL_PICTURE_QUALITY_SERVICE"
- android:protectionLevel="signature|privileged|vendorPrivileged"
- android:featureFlag="android.media.tv.flags.media_quality_fw"/>
-
- <!--
- @SystemApi
- @FlaggedApi("android.media.tv.flags.media_quality_fw")
- Allows an application to access its sound profile from the media quality database.
- <p> Protection level: signature|privileged|vendor privileged
- @hide
- -->
- <permission android:name="android.permission.MANAGE_GLOBAL_SOUND_QUALITY_SERVICE"
- android:protectionLevel="signature|privileged|vendorPrivileged"
- android:featureFlag="android.media.tv.flags.media_quality_fw"/>
<!-- @SystemApi
@FlaggedApi("android.content.pm.verification_service")
Allows app to be the verification agent to verify packages.
diff --git a/core/res/res/drawable/notification_progress.xml b/core/res/res/drawable/notification_progress.xml
index 3a6b600..5d272fb 100644
--- a/core/res/res/drawable/notification_progress.xml
+++ b/core/res/res/drawable/notification_progress.xml
@@ -24,9 +24,9 @@
android:segPointGap="@dimen/notification_progress_segPoint_gap">
<segments
android:color="?attr/colorProgressBackgroundNormal"
- android:dashGap="@dimen/notification_progress_segments_dash_gap"
- android:dashWidth="@dimen/notification_progress_segments_dash_width"
- android:width="@dimen/notification_progress_segments_height" />
+ android:height="@dimen/notification_progress_segments_height"
+ android:fadedHeight="@dimen/notification_progress_segments_faded_height"
+ android:cornerRadius="@dimen/notification_progress_segments_corner_radius"/>
<points
android:color="?attr/colorProgressBackgroundNormal"
android:radius="@dimen/notification_progress_points_radius"
diff --git a/core/res/res/layout/list_content_simple.xml b/core/res/res/layout/list_content_simple.xml
index 6f9f1e0..961668e 100644
--- a/core/res/res/layout/list_content_simple.xml
+++ b/core/res/res/layout/list_content_simple.xml
@@ -20,5 +20,6 @@
<ListView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@android:id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"
+ android:fitsSystemWindows="true"
android:drawSelectorOnTop="false"
/>
diff --git a/core/res/res/layout/notification_2025_template_collapsed_base.xml b/core/res/res/layout/notification_2025_template_collapsed_base.xml
new file mode 100644
index 0000000..c003820
--- /dev/null
+++ b/core/res/res/layout/notification_2025_template_collapsed_base.xml
@@ -0,0 +1,195 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2024 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/status_bar_latest_event_content"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:minHeight="@dimen/notification_headerless_min_height"
+ android:tag="base"
+ >
+
+ <ImageView
+ android:id="@+id/left_icon"
+ android:layout_width="@dimen/notification_2025_left_icon_size"
+ android:layout_height="@dimen/notification_2025_left_icon_size"
+ android:layout_gravity="center_vertical|start"
+ android:layout_marginStart="@dimen/notification_left_icon_start"
+ android:background="@drawable/notification_large_icon_outline"
+ android:clipToOutline="true"
+ android:importantForAccessibility="no"
+ android:scaleType="centerCrop"
+ android:visibility="gone"
+ />
+
+ <com.android.internal.widget.NotificationRowIconView
+ android:id="@+id/icon"
+ android:layout_width="@dimen/notification_2025_icon_circle_size"
+ android:layout_height="@dimen/notification_2025_icon_circle_size"
+ android:layout_gravity="center_vertical|start"
+ android:layout_marginStart="@dimen/notification_icon_circle_start"
+ android:background="@drawable/notification_icon_circle"
+ android:padding="@dimen/notification_2025_icon_circle_padding"
+ android:maxDrawableWidth="@dimen/notification_2025_icon_circle_size"
+ android:maxDrawableHeight="@dimen/notification_2025_icon_circle_size"
+ />
+
+ <FrameLayout
+ android:id="@+id/alternate_expand_target"
+ android:layout_width="@dimen/notification_2025_content_margin_start"
+ android:layout_height="match_parent"
+ android:layout_gravity="start"
+ android:importantForAccessibility="no"
+ android:focusable="false"
+ />
+
+ <LinearLayout
+ android:id="@+id/notification_headerless_view_row"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_marginStart="@dimen/notification_2025_content_margin_start"
+ android:orientation="horizontal"
+ >
+
+ <LinearLayout
+ android:id="@+id/notification_headerless_view_column"
+ android:layout_width="0px"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:layout_weight="1"
+ android:layout_marginBottom="@dimen/notification_headerless_margin_twoline"
+ android:layout_marginTop="@dimen/notification_headerless_margin_twoline"
+ android:orientation="vertical"
+ >
+
+ <NotificationTopLineView
+ android:id="@+id/notification_top_line"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:minHeight="@dimen/notification_headerless_line_height"
+ android:clipChildren="false"
+ android:theme="@style/Theme.DeviceDefault.Notification"
+ >
+
+ <!--
+ NOTE: The notification_top_line_views layout contains the app_name_text.
+ In order to include the title view at the beginning, the Notification.Builder
+ has logic to hide that view whenever this title view is to be visible.
+ -->
+
+ <TextView
+ android:id="@+id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginEnd="@dimen/notification_header_separating_margin"
+ android:ellipsize="end"
+ android:fadingEdge="horizontal"
+ android:singleLine="true"
+ android:textAlignment="viewStart"
+ android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Title"
+ />
+
+ <include layout="@layout/notification_top_line_views" />
+
+ </NotificationTopLineView>
+
+ <LinearLayout
+ android:id="@+id/notification_main_column"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ >
+
+ <com.android.internal.widget.NotificationVanishingFrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="@dimen/notification_headerless_line_height"
+ >
+ <!-- This is the simplest way to keep this text vertically centered without
+ gravity="center_vertical" which causes jumpiness in expansion animations. -->
+ <include
+ layout="@layout/notification_template_text"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/notification_text_height"
+ android:layout_gravity="center_vertical"
+ android:layout_marginTop="0dp"
+ />
+ </com.android.internal.widget.NotificationVanishingFrameLayout>
+
+ <include
+ layout="@layout/notification_template_progress"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/notification_headerless_line_height"
+ />
+
+ </LinearLayout>
+
+ </LinearLayout>
+
+ <com.android.internal.widget.CachingIconView
+ android:id="@+id/right_icon"
+ android:layout_width="@dimen/notification_right_icon_size"
+ android:layout_height="@dimen/notification_right_icon_size"
+ android:layout_gravity="center_vertical|end"
+ android:layout_marginTop="@dimen/notification_right_icon_headerless_margin"
+ android:layout_marginBottom="@dimen/notification_right_icon_headerless_margin"
+ android:layout_marginStart="@dimen/notification_right_icon_content_margin"
+ android:background="@drawable/notification_large_icon_outline"
+ android:clipToOutline="true"
+ android:importantForAccessibility="no"
+ android:scaleType="centerCrop"
+ android:maxDrawableWidth="@dimen/notification_right_icon_size"
+ android:maxDrawableHeight="@dimen/notification_right_icon_size"
+ />
+
+ <LinearLayout
+ android:id="@+id/notification_buttons_column"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_alignParentEnd="true"
+ android:orientation="vertical"
+ >
+
+ <include layout="@layout/notification_close_button"
+ android:layout_width="@dimen/notification_close_button_size"
+ android:layout_height="@dimen/notification_close_button_size"
+ android:layout_gravity="end"
+ android:layout_marginEnd="20dp"
+ />
+
+ <FrameLayout
+ android:id="@+id/expand_button_touch_container"
+ android:layout_width="wrap_content"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:minWidth="@dimen/notification_content_margin_end"
+ >
+
+ <include layout="@layout/notification_expand_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical|end"
+ />
+
+ </FrameLayout>
+
+ </LinearLayout>
+
+ </LinearLayout>
+
+</FrameLayout>
diff --git a/core/res/res/layout/notification_2025_template_header.xml b/core/res/res/layout/notification_2025_template_header.xml
new file mode 100644
index 0000000..b7fe454
--- /dev/null
+++ b/core/res/res/layout/notification_2025_template_header.xml
@@ -0,0 +1,111 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2024 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+
+<!-- extends RelativeLayout -->
+<NotificationHeaderView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/notification_header"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/notification_2025_header_height"
+ android:layout_marginBottom="@dimen/notification_header_margin_bottom"
+ android:clipChildren="false"
+ android:gravity="center_vertical"
+ android:orientation="horizontal"
+ android:theme="@style/Theme.DeviceDefault.Notification"
+ android:importantForAccessibility="no"
+ >
+
+ <ImageView
+ android:id="@+id/left_icon"
+ android:layout_width="@dimen/notification_2025_left_icon_size"
+ android:layout_height="@dimen/notification_2025_left_icon_size"
+ android:layout_alignParentStart="true"
+ android:layout_centerVertical="true"
+ android:layout_marginStart="@dimen/notification_left_icon_start"
+ android:background="@drawable/notification_large_icon_outline"
+ android:clipToOutline="true"
+ android:importantForAccessibility="no"
+ android:scaleType="centerCrop"
+ android:visibility="gone"
+ />
+
+ <com.android.internal.widget.NotificationRowIconView
+ android:id="@+id/icon"
+ android:layout_width="@dimen/notification_2025_icon_circle_size"
+ android:layout_height="@dimen/notification_2025_icon_circle_size"
+ android:layout_alignParentStart="true"
+ android:layout_centerVertical="true"
+ android:layout_marginStart="@dimen/notification_icon_circle_start"
+ android:background="@drawable/notification_icon_circle"
+ android:padding="@dimen/notification_2025_icon_circle_padding"
+ android:maxDrawableWidth="@dimen/notification_2025_icon_circle_size"
+ android:maxDrawableHeight="@dimen/notification_2025_icon_circle_size"
+ />
+
+ <!-- extends ViewGroup -->
+ <NotificationTopLineView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/notification_top_line"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_alignParentStart="true"
+ android:layout_centerVertical="true"
+ android:layout_toStartOf="@id/notification_buttons_column"
+ android:layout_alignWithParentIfMissing="true"
+ android:clipChildren="false"
+ android:gravity="center_vertical"
+ android:paddingStart="@dimen/notification_2025_content_margin_start"
+ android:theme="@style/Theme.DeviceDefault.Notification"
+ >
+
+ <include layout="@layout/notification_top_line_views" />
+
+ </NotificationTopLineView>
+
+ <FrameLayout
+ android:id="@+id/alternate_expand_target"
+ android:layout_width="@dimen/notification_2025_content_margin_start"
+ android:layout_height="match_parent"
+ android:layout_alignParentStart="true"
+ android:importantForAccessibility="no"
+ android:focusable="false"
+ />
+
+ <LinearLayout
+ android:id="@+id/notification_buttons_column"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentEnd="true"
+ android:orientation="vertical"
+ >
+
+ <include layout="@layout/notification_close_button"
+ android:layout_width="@dimen/notification_close_button_size"
+ android:layout_height="@dimen/notification_close_button_size"
+ android:layout_gravity="end"
+ android:layout_marginEnd="20dp"
+ />
+
+ <include layout="@layout/notification_expand_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentEnd="true"
+ android:layout_centerVertical="true"
+ />
+
+ </LinearLayout>
+
+</NotificationHeaderView>
diff --git a/core/res/res/layout/notification_2025_template_heads_up_base.xml b/core/res/res/layout/notification_2025_template_heads_up_base.xml
new file mode 100644
index 0000000..e4ff835
--- /dev/null
+++ b/core/res/res/layout/notification_2025_template_heads_up_base.xml
@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2014 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/status_bar_latest_event_content"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:clipChildren="false"
+ android:tag="headsUp"
+ >
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:clipChildren="false"
+ android:orientation="vertical"
+ >
+
+ <include
+ layout="@layout/notification_2025_template_collapsed_base"
+ android:id="@null"
+ />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="-20dp"
+ android:clipChildren="false"
+ android:orientation="vertical"
+ >
+
+ <ViewStub
+ android:layout="@layout/notification_material_reply_text"
+ android:id="@+id/notification_material_reply_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ />
+
+ <include
+ layout="@layout/notification_template_smart_reply_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="@dimen/notification_2025_content_margin_start"
+ android:layout_marginEnd="@dimen/notification_content_margin_end"
+ android:layout_marginTop="@dimen/notification_content_margin"
+ />
+
+ <include layout="@layout/notification_material_action_list" />
+ </LinearLayout>
+ </LinearLayout>
+</FrameLayout>
diff --git a/core/res/res/layout/notification_template_material_messaging_compact_heads_up.xml b/core/res/res/layout/notification_template_material_messaging_compact_heads_up.xml
index 82920ba..149a5a9 100644
--- a/core/res/res/layout/notification_template_material_messaging_compact_heads_up.xml
+++ b/core/res/res/layout/notification_template_material_messaging_compact_heads_up.xml
@@ -34,12 +34,14 @@
android:maxDrawableWidth="@dimen/notification_icon_circle_size"
android:maxDrawableHeight="@dimen/notification_icon_circle_size"
/>
- <com.android.internal.widget.NotificationRowIconView
+ <com.android.internal.widget.CachingIconView
android:id="@+id/conversation_icon"
android:layout_width="@dimen/notification_icon_circle_size"
android:layout_height="@dimen/notification_icon_circle_size"
android:layout_gravity="center_vertical|start"
android:layout_marginStart="@dimen/notification_icon_circle_start"
+ android:background="@drawable/notification_icon_circle"
+ android:clipToOutline="true"
android:maxDrawableWidth="@dimen/notification_icon_circle_size"
android:maxDrawableHeight="@dimen/notification_icon_circle_size"
android:scaleType="centerCrop"
diff --git a/core/res/res/values-night/colors_dynamic.xml b/core/res/res/values-night/colors_dynamic.xml
new file mode 100644
index 0000000..7e95ff4
--- /dev/null
+++ b/core/res/res/values-night/colors_dynamic.xml
@@ -0,0 +1,103 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!-- Colors specific to Material themes. -->
+<resources>
+ <color name="materialColorBackground">@color/system_background_dark</color>
+ <color name="materialColorControlActivated">@color/system_control_activated_dark</color>
+ <color name="materialColorControlHighlight">@color/system_control_highlight_dark</color>
+ <color name="materialColorControlNormal">@color/system_control_normal_dark</color>
+ <color name="materialColorError">@color/system_error_dark</color>
+ <color name="materialColorErrorContainer">@color/system_error_container_dark</color>
+ <color name="materialColorInverseOnSurface">@color/system_inverse_on_surface_dark</color>
+ <color name="materialColorInversePrimary">@color/system_inverse_primary_dark</color>
+ <color name="materialColorInverseSurface">@color/system_inverse_surface_dark</color>
+ <color name="materialColorOnBackground">@color/system_on_background_dark</color>
+ <color name="materialColorOnError">@color/system_on_error_dark</color>
+ <color name="materialColorOnErrorContainer">@color/system_on_error_container_dark</color>
+ <color name="materialColorOnPrimary">@color/system_on_primary_dark</color>
+ <color name="materialColorOnPrimaryContainer">@color/system_on_primary_container_dark</color>
+ <color name="materialColorOnSecondary">@color/system_on_secondary_dark</color>
+ <color name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_dark</color>
+ <color name="materialColorOnSurface">@color/system_on_surface_dark</color>
+ <color name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_dark</color>
+ <color name="materialColorOnTertiary">@color/system_on_tertiary_dark</color>
+ <color name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_dark</color>
+ <color name="materialColorOutline">@color/system_outline_dark</color>
+ <color name="materialColorOutlineVariant">@color/system_outline_variant_dark</color>
+ <color name="materialColorPaletteKeyColorNeutral">@color/system_palette_key_color_neutral_dark</color>
+ <color name="materialColorPaletteKeyColorNeutralVariant">@color/system_palette_key_color_neutral_variant_dark</color>
+ <color name="materialColorPaletteKeyColorPrimary">@color/system_palette_key_color_primary_dark</color>
+ <color name="materialColorPaletteKeyColorSecondary">@color/system_palette_key_color_secondary_dark</color>
+ <color name="materialColorPaletteKeyColorTertiary">@color/system_palette_key_color_tertiary_dark</color>
+ <color name="materialColorPrimary">@color/system_primary_dark</color>
+ <color name="materialColorPrimaryContainer">@color/system_primary_container_dark</color>
+ <color name="materialColorScrim">@color/system_scrim_dark</color>
+ <color name="materialColorSecondary">@color/system_secondary_dark</color>
+ <color name="materialColorSecondaryContainer">@color/system_secondary_container_dark</color>
+ <color name="materialColorShadow">@color/system_shadow_dark</color>
+ <color name="materialColorSurface">@color/system_surface_dark</color>
+ <color name="materialColorSurfaceBright">@color/system_surface_bright_dark</color>
+ <color name="materialColorSurfaceContainer">@color/system_surface_container_dark</color>
+ <color name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_dark</color>
+ <color name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_dark</color>
+ <color name="materialColorSurfaceContainerLow">@color/system_surface_container_low_dark</color>
+ <color name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_dark</color>
+ <color name="materialColorSurfaceDim">@color/system_surface_dim_dark</color>
+ <color name="materialColorSurfaceTint">@color/system_surface_tint_dark</color>
+ <color name="materialColorSurfaceVariant">@color/system_surface_variant_dark</color>
+ <color name="materialColorTertiary">@color/system_tertiary_dark</color>
+ <color name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</color>
+ <color name="materialColorTextHintInverse">@color/system_text_hint_inverse_dark</color>
+ <color name="materialColorTextPrimaryInverse">@color/system_text_primary_inverse_dark</color>
+ <color name="materialColorTextPrimaryInverseDisableOnly">@color/system_text_primary_inverse_disable_only_dark</color>
+ <color name="materialColorTextSecondaryAndTertiaryInverse">@color/system_text_secondary_and_tertiary_inverse_dark</color>
+ <color name="materialColorTextSecondaryAndTertiaryInverseDisabled">@color/system_text_secondary_and_tertiary_inverse_disabled_dark</color>
+ <color name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</color>
+ <color name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</color>
+ <color name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</color>
+ <color name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</color>
+ <color name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</color>
+ <color name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</color>
+ <color name="materialColorPrimaryFixed">@color/system_primary_fixed</color>
+ <color name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</color>
+ <color name="materialColorSecondaryFixed">@color/system_secondary_fixed</color>
+ <color name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</color>
+ <color name="materialColorTertiaryFixed">@color/system_tertiary_fixed</color>
+ <color name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</color>
+ <color name="customColorBrandA">@color/system_brand_a_dark</color>
+ <color name="customColorBrandB">@color/system_brand_b_dark</color>
+ <color name="customColorBrandC">@color/system_brand_c_dark</color>
+ <color name="customColorBrandD">@color/system_brand_d_dark</color>
+ <color name="customColorClockHour">@color/system_clock_hour_dark</color>
+ <color name="customColorClockMinute">@color/system_clock_minute_dark</color>
+ <color name="customColorClockSecond">@color/system_clock_second_dark</color>
+ <color name="customColorOnShadeActive">@color/system_on_shade_active_dark</color>
+ <color name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</color>
+ <color name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</color>
+ <color name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</color>
+ <color name="customColorOnThemeApp">@color/system_on_theme_app_dark</color>
+ <color name="customColorOverviewBackground">@color/system_overview_background_dark</color>
+ <color name="customColorShadeActive">@color/system_shade_active_dark</color>
+ <color name="customColorShadeDisabled">@color/system_shade_disabled_dark</color>
+ <color name="customColorShadeInactive">@color/system_shade_inactive_dark</color>
+ <color name="customColorThemeApp">@color/system_theme_app_dark</color>
+ <color name="customColorThemeAppRing">@color/system_theme_app_ring_dark</color>
+ <color name="customColorThemeNotif">@color/system_theme_notif_dark</color>
+ <color name="customColorUnderSurface">@color/system_under_surface_dark</color>
+ <color name="customColorWeatherTemp">@color/system_weather_temp_dark</color>
+ <color name="customColorWidgetBackground">@color/system_widget_background_dark</color>
+</resources>
diff --git a/core/res/res/values-watch/themes_device_defaults.xml b/core/res/res/values-watch/themes_device_defaults.xml
index 4d2085bb..7ac1759 100644
--- a/core/res/res/values-watch/themes_device_defaults.xml
+++ b/core/res/res/values-watch/themes_device_defaults.xml
@@ -238,16 +238,16 @@
<item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
<item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
<item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
+ <item name="materialColorInverseOnSurface">@color/system_on_surface_light</item>
<item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
<item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
<item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
<item name="materialColorErrorContainer">@color/system_error_container_dark</item>
<item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
+ <item name="materialColorInversePrimary">@color/system_primary_light</item>
<item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
+ <item name="materialColorInverseSurface">@color/system_surface_light</item>
<item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
<item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
<item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index e6dedce..f6590b1 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -1218,155 +1218,132 @@
it prevent any 'false' in any of its children. -->
<attr name="forceDarkAllowed" format="boolean" />
- <!-- A lower-emphasized variant of the color on the fixed secondary branding color. @hide
- -->
- <attr name="materialColorOnSecondaryFixedVariant" format="color"/>
- <!-- A lower-emphasized variant of the color on the fixed tertiary branding color. @hide
- -->
- <attr name="materialColorOnTertiaryFixedVariant" format="color"/>
- <!-- The container color of surface the most lowered. @hide -->
- <attr name="materialColorSurfaceContainerLowest" format="color"/>
- <!-- A lower-emphasized variant of the color on the fixed primary branding color. @hide -->
- <attr name="materialColorOnPrimaryFixedVariant" format="color"/>
- <!-- A color that passes accessibility guidelines for text/iconography when drawn on top of
- the secondary container color. @hide -->
- <attr name="materialColorOnSecondaryContainer" format="color"/>
- <!-- A color that passes accessibility guidelines for text/iconography when drawn on top of
- the tertiary container color. @hide -->
- <attr name="materialColorOnTertiaryContainer" format="color"/>
- <!-- The container color of surface slightly lowered, which replaces the previous surface
- at elevation level 1. @hide -->
- <attr name="materialColorSurfaceContainerLow" format="color"/>
- <!-- A color that passes accessibility guidelines for text/iconography when drawn on top of
- the primary container color. @hide -->
- <attr name="materialColorOnPrimaryContainer" format="color"/>
- <!-- A stronger, more emphasized variant of the fixed secondary branding color. @hide -->
- <attr name="materialColorSecondaryFixedDim" format="color"/>
- <!-- A tonal variation of the on error color that passes accessibility guidelines for
- text/iconography when drawn on top of error container. @hide -->
- <attr name="materialColorOnErrorContainer" format="color"/>
- <!-- The color text/iconography when drawn on top of the fixed secondary branding color.
- @hide -->
- <attr name="materialColorOnSecondaryFixed" format="color"/>
- <!-- The "on surface" inverse color, useful for inverted backgrounds. @hide -->
- <attr name="materialColorOnSurfaceInverse" format="color"/>
- <!-- A stronger, more emphasized variant of the fixed tertiary branding color. @hide -->
- <attr name="materialColorTertiaryFixedDim" format="color"/>
- <!-- The color text/iconography when drawn on top of the fixed tertiary branding color.
- @hide -->
- <attr name="materialColorOnTertiaryFixed" format="color"/>
- <!-- A stronger, more emphasized variant of the fixed primary branding color. @hide -->
- <attr name="materialColorPrimaryFixedDim" format="color"/>
- <!-- A tonal variation of the secondary color suitable for background color of container
- views. @hide -->
- <attr name="materialColorSecondaryContainer" format="color"/>
- <!-- A tonal variation of the error color suitable for background color of container views.
- @hide -->
- <attr name="materialColorErrorContainer" format="color"/>
- <!-- The color text/iconography when drawn on top of the fixed primary branding color.
- @hide -->
- <attr name="materialColorOnPrimaryFixed" format="color"/>
- <!-- The inverse color of colorPrimary. @hide -->
- <attr name="materialColorPrimaryInverse" format="color"/>
- <!-- A secondary branding color for the app, which stays the same between light and dark
- themes. @hide -->
- <attr name="materialColorSecondaryFixed" format="color"/>
- <!-- The surface inverse color, useful for inverted backgrounds. @hide -->
- <attr name="materialColorSurfaceInverse" format="color"/>
- <!-- A tonal variation of the surface color. @hide -->
- <attr name="materialColorSurfaceVariant" format="color"/>
- <!-- A tonal variation of the tertiary color suitable for background color of container
- views. @hide -->
- <attr name="materialColorTertiaryContainer" format="color"/>
- <!-- A tertiary branding color for the app, which stays the same between light and dark
- themes. @hide -->
- <attr name="materialColorTertiaryFixed" format="color"/>
- <!-- A tonal variation of the primary color suitable for background color of container
- views. @hide -->
- <attr name="materialColorPrimaryContainer" format="color"/>
- <!-- A color that passes accessibility guidelines for text/iconography when drawn on top of
- background. @hide -->
- <attr name="materialColorOnBackground" format="color"/>
- <!-- A primary branding color for the app, which stays the same between light and dark
- themes. @hide -->
- <attr name="materialColorPrimaryFixed" format="color"/>
- <!-- A color that passes accessibility guidelines for text/iconography when drawn on top of
- secondary. @hide -->
- <attr name="materialColorOnSecondary" format="color"/>
- <!-- A color that passes accessibility guidelines for text/iconography when drawn on top of
- tertiary. @hide -->
- <attr name="materialColorOnTertiary" format="color"/>
- <!-- The surface color which always stay the dimmest in either dark or light theme. @hide
- -->
- <attr name="materialColorSurfaceDim" format="color"/>
- <!-- The surface color which always stay the brightest in either dark or light theme. @hide
- -->
- <attr name="materialColorSurfaceBright" format="color"/>
- <!-- The secondary branding color for the app, usually a bright complement to the primary
- branding color. @hide -->
- <attr name="materialColorSecondary" format="color"/>
- <!-- A color that passes accessibility guidelines for text/iconography when drawn on top of
- error. @hide -->
- <attr name="materialColorOnError" format="color"/>
- <!-- The color of surfaces such as cards, sheets, menus. @hide -->
- <attr name="materialColorSurface" format="color"/>
- <!-- The container color of surface slightly elevated, which replaces the previous surface
- at elevation level 3. @hide -->
- <attr name="materialColorSurfaceContainerHigh" format="color"/>
- <!-- The tertiary branding color for the app, usually a bright complement to the primary
- branding color. @hide -->
- <attr name="materialColorTertiary" format="color"/>
- <!-- The container color of surface the most elevated, which replaces the previous surface
- variant. @hide -->
- <attr name="materialColorSurfaceContainerHighest" format="color"/>
- <!-- A tonal variation of the on surface color that passes accessibility guidelines for
- text/iconography when drawn on top of surface variant. @hide -->
- <attr name="materialColorOnSurfaceVariant" format="color"/>
- <!-- A color meant to be used in element outlines. @hide -->
- <attr name="materialColorOutline" format="color"/>
- <!-- A color meant to be used in element outlines on the surface-variant color. @hide -->
- <attr name="materialColorOutlineVariant" format="color"/>
- <!-- A color that passes accessibility guidelines for text/iconography when drawn on top of
- primary. @hide -->
- <attr name="materialColorOnPrimary" format="color"/>
- <!-- A color that passes accessibility guidelines for text/iconography when drawn on top of
- surface. @hide -->
- <attr name="materialColorOnSurface" format="color"/>
- <!-- The container color of surface, which replaces the previous surface at elevation level
- 2. @hide -->
- <attr name="materialColorSurfaceContainer" format="color"/>
- <!-- The container color of surface, which replaces the previous surface at elevation level
- 2. @hide -->
- <attr name="materialColorSurfaceContainer" format="color"/>
- <!-- The primary branding color for the app. By default, this is the color applied to the
- action bar background. @hide -->
- <attr name="materialColorPrimary" format="color"/>
- <!-- The secondary branding color for the app, usually a bright complement to the primary
- branding color. @hide -->
- <attr name="materialColorSecondary" format="color"/>
- <!-- A color that passes accessibility guidelines for text/iconography when drawn on top
- of tertiary. @hide -->
- <attr name="materialColorTertiary" format="color"/>
- <!-- The error color for the app, intended to draw attention to error conditions. @hide -->
- <attr name="materialColorError" format="color"/>
+ <!-- Dynamic Tokens -->
- <!-- System Custom Tokens-->
<!-- @hide -->
- <attr name="customColorWidgetBackground" format="color"/>
+ <attr name="materialColorBackground" format="color"/>
<!-- @hide -->
- <attr name="customColorClockHour" format="color"/>
+ <attr name="materialColorControlActivated" format="color"/>
<!-- @hide -->
- <attr name="customColorClockMinute" format="color"/>
+ <attr name="materialColorControlHighlight" format="color"/>
<!-- @hide -->
- <attr name="customColorClockSecond" format="color"/>
+ <attr name="materialColorControlNormal" format="color"/>
<!-- @hide -->
- <attr name="customColorThemeApp" format="color"/>
+ <attr name="materialColorError" format="color"/>
<!-- @hide -->
- <attr name="customColorOnThemeApp" format="color"/>
+ <attr name="materialColorErrorContainer" format="color"/>
<!-- @hide -->
- <attr name="customColorThemeAppRing" format="color"/>
+ <attr name="materialColorInverseOnSurface" format="color"/>
<!-- @hide -->
- <attr name="customColorThemeNotif" format="color"/>
+ <attr name="materialColorInversePrimary" format="color"/>
+ <!-- @hide -->
+ <attr name="materialColorInverseSurface" format="color"/>
+ <!-- @hide -->
+ <attr name="materialColorOnBackground" format="color"/>
+ <!-- @hide -->
+ <attr name="materialColorOnError" format="color"/>
+ <!-- @hide -->
+ <attr name="materialColorOnErrorContainer" format="color"/>
+ <!-- @hide -->
+ <attr name="materialColorOnPrimary" format="color"/>
+ <!-- @hide -->
+ <attr name="materialColorOnPrimaryContainer" format="color"/>
+ <!-- @hide -->
+ <attr name="materialColorOnSecondary" format="color"/>
+ <!-- @hide -->
+ <attr name="materialColorOnSecondaryContainer" format="color"/>
+ <!-- @hide -->
+ <attr name="materialColorOnSurface" format="color"/>
+ <!-- @hide -->
+ <attr name="materialColorOnSurfaceVariant" format="color"/>
+ <!-- @hide -->
+ <attr name="materialColorOnTertiary" format="color"/>
+ <!-- @hide -->
+ <attr name="materialColorOnTertiaryContainer" format="color"/>
+ <!-- @hide -->
+ <attr name="materialColorOutline" format="color"/>
+ <!-- @hide -->
+ <attr name="materialColorOutlineVariant" format="color"/>
+ <!-- @hide -->
+ <attr name="materialColorPaletteKeyColorNeutral" format="color"/>
+ <!-- @hide -->
+ <attr name="materialColorPaletteKeyColorNeutralVariant" format="color"/>
+ <!-- @hide -->
+ <attr name="materialColorPaletteKeyColorPrimary" format="color"/>
+ <!-- @hide -->
+ <attr name="materialColorPaletteKeyColorSecondary" format="color"/>
+ <!-- @hide -->
+ <attr name="materialColorPaletteKeyColorTertiary" format="color"/>
+ <!-- @hide -->
+ <attr name="materialColorPrimary" format="color"/>
+ <!-- @hide -->
+ <attr name="materialColorPrimaryContainer" format="color"/>
+ <!-- @hide -->
+ <attr name="materialColorScrim" format="color"/>
+ <!-- @hide -->
+ <attr name="materialColorSecondary" format="color"/>
+ <!-- @hide -->
+ <attr name="materialColorSecondaryContainer" format="color"/>
+ <!-- @hide -->
+ <attr name="materialColorShadow" format="color"/>
+ <!-- @hide -->
+ <attr name="materialColorSurface" format="color"/>
+ <!-- @hide -->
+ <attr name="materialColorSurfaceBright" format="color"/>
+ <!-- @hide -->
+ <attr name="materialColorSurfaceContainer" format="color"/>
+ <!-- @hide -->
+ <attr name="materialColorSurfaceContainerHigh" format="color"/>
+ <!-- @hide -->
+ <attr name="materialColorSurfaceContainerHighest" format="color"/>
+ <!-- @hide -->
+ <attr name="materialColorSurfaceContainerLow" format="color"/>
+ <!-- @hide -->
+ <attr name="materialColorSurfaceContainerLowest" format="color"/>
+ <!-- @hide -->
+ <attr name="materialColorSurfaceDim" format="color"/>
+ <!-- @hide -->
+ <attr name="materialColorSurfaceTint" format="color"/>
+ <!-- @hide -->
+ <attr name="materialColorSurfaceVariant" format="color"/>
+ <!-- @hide -->
+ <attr name="materialColorTertiary" format="color"/>
+ <!-- @hide -->
+ <attr name="materialColorTertiaryContainer" format="color"/>
+ <!-- @hide -->
+ <attr name="materialColorTextHintInverse" format="color"/>
+ <!-- @hide -->
+ <attr name="materialColorTextPrimaryInverse" format="color"/>
+ <!-- @hide -->
+ <attr name="materialColorTextPrimaryInverseDisableOnly" format="color"/>
+ <!-- @hide -->
+ <attr name="materialColorTextSecondaryAndTertiaryInverse" format="color"/>
+ <!-- @hide -->
+ <attr name="materialColorTextSecondaryAndTertiaryInverseDisabled" format="color"/>
+ <!-- @hide -->
+ <attr name="materialColorOnPrimaryFixed" format="color"/>
+ <!-- @hide -->
+ <attr name="materialColorOnPrimaryFixedVariant" format="color"/>
+ <!-- @hide -->
+ <attr name="materialColorOnSecondaryFixed" format="color"/>
+ <!-- @hide -->
+ <attr name="materialColorOnSecondaryFixedVariant" format="color"/>
+ <!-- @hide -->
+ <attr name="materialColorOnTertiaryFixed" format="color"/>
+ <!-- @hide -->
+ <attr name="materialColorOnTertiaryFixedVariant" format="color"/>
+ <!-- @hide -->
+ <attr name="materialColorPrimaryFixed" format="color"/>
+ <!-- @hide -->
+ <attr name="materialColorPrimaryFixedDim" format="color"/>
+ <!-- @hide -->
+ <attr name="materialColorSecondaryFixed" format="color"/>
+ <!-- @hide -->
+ <attr name="materialColorSecondaryFixedDim" format="color"/>
+ <!-- @hide -->
+ <attr name="materialColorTertiaryFixed" format="color"/>
+ <!-- @hide -->
+ <attr name="materialColorTertiaryFixedDim" format="color"/>
<!-- @hide -->
<attr name="customColorBrandA" format="color"/>
<!-- @hide -->
@@ -1376,23 +1353,41 @@
<!-- @hide -->
<attr name="customColorBrandD" format="color"/>
<!-- @hide -->
- <attr name="customColorUnderSurface" format="color"/>
+ <attr name="customColorClockHour" format="color"/>
<!-- @hide -->
- <attr name="customColorShadeActive" format="color"/>
+ <attr name="customColorClockMinute" format="color"/>
+ <!-- @hide -->
+ <attr name="customColorClockSecond" format="color"/>
<!-- @hide -->
<attr name="customColorOnShadeActive" format="color"/>
<!-- @hide -->
<attr name="customColorOnShadeActiveVariant" format="color"/>
<!-- @hide -->
- <attr name="customColorShadeInactive" format="color"/>
- <!-- @hide -->
<attr name="customColorOnShadeInactive" format="color"/>
<!-- @hide -->
<attr name="customColorOnShadeInactiveVariant" format="color"/>
<!-- @hide -->
- <attr name="customColorShadeDisabled" format="color"/>
+ <attr name="customColorOnThemeApp" format="color"/>
<!-- @hide -->
<attr name="customColorOverviewBackground" format="color"/>
+ <!-- @hide -->
+ <attr name="customColorShadeActive" format="color"/>
+ <!-- @hide -->
+ <attr name="customColorShadeDisabled" format="color"/>
+ <!-- @hide -->
+ <attr name="customColorShadeInactive" format="color"/>
+ <!-- @hide -->
+ <attr name="customColorThemeApp" format="color"/>
+ <!-- @hide -->
+ <attr name="customColorThemeAppRing" format="color"/>
+ <!-- @hide -->
+ <attr name="customColorThemeNotif" format="color"/>
+ <!-- @hide -->
+ <attr name="customColorUnderSurface" format="color"/>
+ <!-- @hide -->
+ <attr name="customColorWeatherTemp" format="color"/>
+ <!-- @hide -->
+ <attr name="customColorWidgetBackground" format="color"/>
</declare-styleable>
@@ -7753,14 +7748,14 @@
<!-- Used to config the segments of a NotificationProgressDrawable. -->
<!-- @hide internal use only -->
<declare-styleable name="NotificationProgressDrawableSegments">
- <!-- Width of the stroke. -->
- <attr name="width" />
- <!-- Default color of the stroke. -->
+ <!-- Height of the solid segments -->
+ <attr name="height" />
+ <!-- Height of the faded segments -->
+ <attr name="fadedHeight" format="dimension"/>
+ <!-- Corner radius of the segment rect. -->
+ <attr name="cornerRadius" format="dimension" />
+ <!-- Default color of the segment. -->
<attr name="color" />
- <!-- Length of a dash in the stroke for the dashed segments. -->
- <attr name="dashWidth" />
- <!-- Gap between dashes in the stroke for the dashed segments. -->
- <attr name="dashGap" />
</declare-styleable>
<!-- Used to config the points of a NotificationProgressDrawable. -->
@@ -7771,7 +7766,7 @@
<!-- Inset of the point icon or rect. -->
<attr name="inset" />
<!-- Corner radius of the point rect. -->
- <attr name="cornerRadius" format="dimension" />
+ <attr name="cornerRadius"/>
<!-- Default color of the point rect. -->
<attr name="color" />
</declare-styleable>
@@ -10285,22 +10280,25 @@
</declare-styleable>
<!-- @hide -->
+ <attr name="modifierState">
+ <flag name="META" value="0x10000" />
+ <flag name="CTRL" value="0x1000" />
+ <flag name="ALT" value="0x02" />
+ <flag name="SHIFT" value="0x1" />
+ <flag name="SYM" value="0x4" />
+ <flag name="FUNCTION" value="0x8" />
+ <flag name="CAPS_LOCK" value="0x100000" />
+ <flag name="NUM_LOCK" value="0x200000" />
+ <flag name="SCROLL_LOCK" value="0x400000" />
+ </attr>
+
+ <!-- @hide -->
<declare-styleable name="HardwareDefinedShortcut">
<attr name="keycode" />
<!-- The values are taken from public constants for modifier state defined in
{@see KeyEvent.java}. Here we allow multiple modifier flags as value, since this
represents the modifier state -->
- <attr name="modifierState">
- <flag name="META" value="0x10000" />
- <flag name="CTRL" value="0x1000" />
- <flag name="ALT" value="0x02" />
- <flag name="SHIFT" value="0x1" />
- <flag name="SYM" value="0x4" />
- <flag name="FUNCTION" value="0x8" />
- <flag name="CAPS_LOCK" value="0x100000" />
- <flag name="NUM_LOCK" value="0x200000" />
- <flag name="SCROLL_LOCK" value="0x400000" />
- </attr>
+ <attr name="modifierState" />
<attr name="outKeycode" />
</declare-styleable>
@@ -10309,6 +10307,11 @@
<attr name="keycode" />
</declare-styleable>
+ <declare-styleable name="Bookmark">
+ <attr name="keycode" />
+ <attr name="modifierState" />
+ </declare-styleable>
+
<declare-styleable name="MediaRouteButton">
<!-- This drawable is a state list where the "activated" state
indicates active media routing. Non-activated indicates
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 41dec37..7ef5394 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -1855,13 +1855,23 @@
{@link android.R.styleable#AndroidManifestProcess process} tag, or to an
{@link android.R.styleable#AndroidManifestApplication application} tag (to supply
a default setting for all application components). -->
- <attr name="memtagMode">
+ <attr name="memtagMode">
<enum name="default" value="-1" />
<enum name="off" value="0" />
<enum name="async" value="1" />
<enum name="sync" value="2" />
</attr>
+ <!-- This attribute will be used to override app compatibility mode on 16 KB devices.
+ If set to enabled, Natives lib will be extracted from APK if they are not page aligned on
+ 16 KB device. 4 KB natives libs will be loaded app-compat mode if they are eligible.
+ @FlaggedApi(android.content.pm.Flags.FLAG_APP_COMPAT_OPTION_16KB) -->
+ <attr name="pageSizeCompat">
+ <enum name="enabled" value="5" />
+ <enum name="disabled" value="6" />
+ </attr>
+
+
<!-- Attribution tag to be used for permission sub-attribution if a
permission is checked in {@link android.content.Context#sendBroadcast(Intent, String)}.
Multiple tags can be specified separated by '|'.
@@ -2212,6 +2222,9 @@
<attr name="memtagMode" />
+ <!-- @FlaggedApi(android.content.pm.Flags.FLAG_APP_COMPAT_OPTION_16KB) -->
+ <attr name="pageSizeCompat" />
+
<!-- If {@code true} enables automatic zero initialization of all native heap
allocations. -->
<attr name="nativeHeapZeroInitialized" format="boolean" />
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index f5bb554..13dd4a3 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -238,393 +238,319 @@
<color name="conversation_important_highlight">#F9AB00</color>
- <!--Lightest shade of the Primary color used by the system. White.
- This value can be overlaid at runtime by OverlayManager RROs.-->
+ <!--Lightest shade of the Primary color used by the system. White. This value can be overlaid at runtime by OverlayManager RROs.-->
<color name="system_accent1_0">#FFFFFF</color>
- <!--Shade of the Primary system color at 99% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs.-->
+ <!--Shade of the Primary system color at 99% perceptual luminance (L* in L*a*b* color space). This value can be overlaid at runtime by OverlayManager RROs.-->
<color name="system_accent1_10">#FEFBFF</color>
- <!--Shade of the Primary system color at 95% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs.-->
+ <!--Shade of the Primary system color at 95% perceptual luminance (L* in L*a*b* color space). This value can be overlaid at runtime by OverlayManager RROs.-->
<color name="system_accent1_50">#EEF0FF</color>
- <!--Shade of the Primary system color at 90% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs.-->
+ <!--Shade of the Primary system color at 90% perceptual luminance (L* in L*a*b* color space). This value can be overlaid at runtime by OverlayManager RROs.-->
<color name="system_accent1_100">#D9E2FF</color>
- <!--Shade of the Primary system color at 80% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs.-->
+ <!--Shade of the Primary system color at 80% perceptual luminance (L* in L*a*b* color space). This value can be overlaid at runtime by OverlayManager RROs.-->
<color name="system_accent1_200">#B0C6FF</color>
- <!--Shade of the Primary system color at 70% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs.-->
+ <!--Shade of the Primary system color at 70% perceptual luminance (L* in L*a*b* color space). This value can be overlaid at runtime by OverlayManager RROs.-->
<color name="system_accent1_300">#94AAE4</color>
- <!--Shade of the Primary system color at 60% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs.-->
+ <!--Shade of the Primary system color at 60% perceptual luminance (L* in L*a*b* color space). This value can be overlaid at runtime by OverlayManager RROs.-->
<color name="system_accent1_400">#7A90C8</color>
- <!--Shade of the Primary system color at 50% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs.-->
+ <!--Shade of the Primary system color at 50% perceptual luminance (L* in L*a*b* color space). This value can be overlaid at runtime by OverlayManager RROs.-->
<color name="system_accent1_500">#6076AC</color>
- <!--Shade of the Primary system color at 40% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs.-->
+ <!--Shade of the Primary system color at 40% perceptual luminance (L* in L*a*b* color space). This value can be overlaid at runtime by OverlayManager RROs.-->
<color name="system_accent1_600">#475D92</color>
- <!--Shade of the Primary system color at 30% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs.-->
+ <!--Shade of the Primary system color at 30% perceptual luminance (L* in L*a*b* color space). This value can be overlaid at runtime by OverlayManager RROs.-->
<color name="system_accent1_700">#2F4578</color>
- <!--Shade of the Primary system color at 20% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs.-->
+ <!--Shade of the Primary system color at 20% perceptual luminance (L* in L*a*b* color space). This value can be overlaid at runtime by OverlayManager RROs.-->
<color name="system_accent1_800">#152E60</color>
- <!--Shade of the Primary system color at 10% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs.-->
+ <!--Shade of the Primary system color at 10% perceptual luminance (L* in L*a*b* color space). This value can be overlaid at runtime by OverlayManager RROs.-->
<color name="system_accent1_900">#001945</color>
- <!--Darkest shade of the Primary color used by the system. Black.
- This value can be overlaid at runtime by OverlayManager RROs.-->
+ <!--Darkest shade of the Primary color used by the system. Black. This value can be overlaid at runtime by OverlayManager RROs.-->
<color name="system_accent1_1000">#000000</color>
-
- <!--Lightest shade of the Secondary color used by the system. White.
- This value can be overlaid at runtime by OverlayManager RROs.-->
+ <!--Lightest shade of the Secondary color used by the system. White. This value can be overlaid at runtime by OverlayManager RROs.-->
<color name="system_accent2_0">#FFFFFF</color>
- <!--Shade of the Secondary system color at 99% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs.-->
+ <!--Shade of the Secondary system color at 99% perceptual luminance (L* in L*a*b* color space). This value can be overlaid at runtime by OverlayManager RROs.-->
<color name="system_accent2_10">#FEFBFF</color>
- <!--Shade of the Secondary system color at 95% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs.-->
+ <!--Shade of the Secondary system color at 95% perceptual luminance (L* in L*a*b* color space). This value can be overlaid at runtime by OverlayManager RROs.-->
<color name="system_accent2_50">#EEF0FF</color>
- <!--Shade of the Secondary system color at 90% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs.-->
+ <!--Shade of the Secondary system color at 90% perceptual luminance (L* in L*a*b* color space). This value can be overlaid at runtime by OverlayManager RROs.-->
<color name="system_accent2_100">#DCE2F9</color>
- <!--Shade of the Secondary system color at 80% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs.-->
+ <!--Shade of the Secondary system color at 80% perceptual luminance (L* in L*a*b* color space). This value can be overlaid at runtime by OverlayManager RROs.-->
<color name="system_accent2_200">#C0C6DC</color>
- <!--Shade of the Secondary system color at 70% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs.-->
+ <!--Shade of the Secondary system color at 70% perceptual luminance (L* in L*a*b* color space). This value can be overlaid at runtime by OverlayManager RROs.-->
<color name="system_accent2_300">#A4ABC1</color>
- <!--Shade of the Secondary system color at 60% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs.-->
+ <!--Shade of the Secondary system color at 60% perceptual luminance (L* in L*a*b* color space). This value can be overlaid at runtime by OverlayManager RROs.-->
<color name="system_accent2_400">#8A90A5</color>
- <!--Shade of the Secondary system color at 50% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs.-->
+ <!--Shade of the Secondary system color at 50% perceptual luminance (L* in L*a*b* color space). This value can be overlaid at runtime by OverlayManager RROs.-->
<color name="system_accent2_500">#70778B</color>
- <!--Shade of the Secondary system color at 40% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs.-->
+ <!--Shade of the Secondary system color at 40% perceptual luminance (L* in L*a*b* color space). This value can be overlaid at runtime by OverlayManager RROs.-->
<color name="system_accent2_600">#575E71</color>
- <!--Shade of the Secondary system color at 30% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs.-->
+ <!--Shade of the Secondary system color at 30% perceptual luminance (L* in L*a*b* color space). This value can be overlaid at runtime by OverlayManager RROs.-->
<color name="system_accent2_700">#404659</color>
- <!--Shade of the Secondary system color at 20% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs.-->
+ <!--Shade of the Secondary system color at 20% perceptual luminance (L* in L*a*b* color space). This value can be overlaid at runtime by OverlayManager RROs.-->
<color name="system_accent2_800">#2A3042</color>
- <!--Shade of the Secondary system color at 10% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs.-->
+ <!--Shade of the Secondary system color at 10% perceptual luminance (L* in L*a*b* color space). This value can be overlaid at runtime by OverlayManager RROs.-->
<color name="system_accent2_900">#151B2C</color>
- <!--Darkest shade of the Secondary color used by the system. Black.
- This value can be overlaid at runtime by OverlayManager RROs.-->
+ <!--Darkest shade of the Secondary color used by the system. Black. This value can be overlaid at runtime by OverlayManager RROs.-->
<color name="system_accent2_1000">#000000</color>
-
- <!--Lightest shade of the Tertiary color used by the system. White.
- This value can be overlaid at runtime by OverlayManager RROs.-->
+ <!--Lightest shade of the Tertiary color used by the system. White. This value can be overlaid at runtime by OverlayManager RROs.-->
<color name="system_accent3_0">#FFFFFF</color>
- <!--Shade of the Tertiary system color at 99% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs.-->
+ <!--Shade of the Tertiary system color at 99% perceptual luminance (L* in L*a*b* color space). This value can be overlaid at runtime by OverlayManager RROs.-->
<color name="system_accent3_10">#FFFBFF</color>
- <!--Shade of the Tertiary system color at 95% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs.-->
+ <!--Shade of the Tertiary system color at 95% perceptual luminance (L* in L*a*b* color space). This value can be overlaid at runtime by OverlayManager RROs.-->
<color name="system_accent3_50">#FFEBFA</color>
- <!--Shade of the Tertiary system color at 90% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs.-->
+ <!--Shade of the Tertiary system color at 90% perceptual luminance (L* in L*a*b* color space). This value can be overlaid at runtime by OverlayManager RROs.-->
<color name="system_accent3_100">#FDD7FA</color>
- <!--Shade of the Tertiary system color at 80% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs.-->
+ <!--Shade of the Tertiary system color at 80% perceptual luminance (L* in L*a*b* color space). This value can be overlaid at runtime by OverlayManager RROs.-->
<color name="system_accent3_200">#E0BBDD</color>
- <!--Shade of the Tertiary system color at 70% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs.-->
+ <!--Shade of the Tertiary system color at 70% perceptual luminance (L* in L*a*b* color space). This value can be overlaid at runtime by OverlayManager RROs.-->
<color name="system_accent3_300">#C3A0C1</color>
- <!--Shade of the Tertiary system color at 60% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs.-->
+ <!--Shade of the Tertiary system color at 60% perceptual luminance (L* in L*a*b* color space). This value can be overlaid at runtime by OverlayManager RROs.-->
<color name="system_accent3_400">#A886A6</color>
- <!--Shade of the Tertiary system color at 50% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs.-->
+ <!--Shade of the Tertiary system color at 50% perceptual luminance (L* in L*a*b* color space). This value can be overlaid at runtime by OverlayManager RROs.-->
<color name="system_accent3_500">#8C6D8C</color>
- <!--Shade of the Tertiary system color at 40% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs.-->
+ <!--Shade of the Tertiary system color at 40% perceptual luminance (L* in L*a*b* color space). This value can be overlaid at runtime by OverlayManager RROs.-->
<color name="system_accent3_600">#725572</color>
- <!--Shade of the Tertiary system color at 30% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs.-->
+ <!--Shade of the Tertiary system color at 30% perceptual luminance (L* in L*a*b* color space). This value can be overlaid at runtime by OverlayManager RROs.-->
<color name="system_accent3_700">#593D59</color>
- <!--Shade of the Tertiary system color at 20% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs.-->
+ <!--Shade of the Tertiary system color at 20% perceptual luminance (L* in L*a*b* color space). This value can be overlaid at runtime by OverlayManager RROs.-->
<color name="system_accent3_800">#412742</color>
- <!--Shade of the Tertiary system color at 10% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs.-->
+ <!--Shade of the Tertiary system color at 10% perceptual luminance (L* in L*a*b* color space). This value can be overlaid at runtime by OverlayManager RROs.-->
<color name="system_accent3_900">#2A122C</color>
- <!--Darkest shade of the Tertiary color used by the system. Black.
- This value can be overlaid at runtime by OverlayManager RROs.-->
+ <!--Darkest shade of the Tertiary color used by the system. Black. This value can be overlaid at runtime by OverlayManager RROs.-->
<color name="system_accent3_1000">#000000</color>
-
- <!--Lightest shade of the Neutral color used by the system. White.
- This value can be overlaid at runtime by OverlayManager RROs.-->
+ <!--Lightest shade of the Neutral color used by the system. White. This value can be overlaid at runtime by OverlayManager RROs.-->
<color name="system_neutral1_0">#FFFFFF</color>
- <!--Shade of the Neutral system color at 99% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs.-->
+ <!--Shade of the Neutral system color at 99% perceptual luminance (L* in L*a*b* color space). This value can be overlaid at runtime by OverlayManager RROs.-->
<color name="system_neutral1_10">#FEFBFF</color>
- <!--Shade of the Neutral system color at 95% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs.-->
+ <!--Shade of the Neutral system color at 95% perceptual luminance (L* in L*a*b* color space). This value can be overlaid at runtime by OverlayManager RROs.-->
<color name="system_neutral1_50">#F1F0F7</color>
- <!--Shade of the Neutral system color at 90% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs.-->
+ <!--Shade of the Neutral system color at 90% perceptual luminance (L* in L*a*b* color space). This value can be overlaid at runtime by OverlayManager RROs.-->
<color name="system_neutral1_100">#E2E2E9</color>
- <!--Shade of the Neutral system color at 80% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs.-->
+ <!--Shade of the Neutral system color at 80% perceptual luminance (L* in L*a*b* color space). This value can be overlaid at runtime by OverlayManager RROs.-->
<color name="system_neutral1_200">#C6C6CD</color>
- <!--Shade of the Neutral system color at 70% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs.-->
+ <!--Shade of the Neutral system color at 70% perceptual luminance (L* in L*a*b* color space). This value can be overlaid at runtime by OverlayManager RROs.-->
<color name="system_neutral1_300">#ABABB1</color>
- <!--Shade of the Neutral system color at 60% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs.-->
+ <!--Shade of the Neutral system color at 60% perceptual luminance (L* in L*a*b* color space). This value can be overlaid at runtime by OverlayManager RROs.-->
<color name="system_neutral1_400">#909097</color>
- <!--Shade of the Neutral system color at 50% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs.-->
+ <!--Shade of the Neutral system color at 50% perceptual luminance (L* in L*a*b* color space). This value can be overlaid at runtime by OverlayManager RROs.-->
<color name="system_neutral1_500">#76777D</color>
- <!--Shade of the Neutral system color at 40% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs.-->
+ <!--Shade of the Neutral system color at 40% perceptual luminance (L* in L*a*b* color space). This value can be overlaid at runtime by OverlayManager RROs.-->
<color name="system_neutral1_600">#5D5E64</color>
- <!--Shade of the Neutral system color at 30% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs.-->
+ <!--Shade of the Neutral system color at 30% perceptual luminance (L* in L*a*b* color space). This value can be overlaid at runtime by OverlayManager RROs.-->
<color name="system_neutral1_700">#45464C</color>
- <!--Shade of the Neutral system color at 20% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs.-->
+ <!--Shade of the Neutral system color at 20% perceptual luminance (L* in L*a*b* color space). This value can be overlaid at runtime by OverlayManager RROs.-->
<color name="system_neutral1_800">#2F3036</color>
- <!--Shade of the Neutral system color at 10% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs.-->
+ <!--Shade of the Neutral system color at 10% perceptual luminance (L* in L*a*b* color space). This value can be overlaid at runtime by OverlayManager RROs.-->
<color name="system_neutral1_900">#1A1B20</color>
- <!--Darkest shade of the Neutral color used by the system. Black.
- This value can be overlaid at runtime by OverlayManager RROs.-->
+ <!--Darkest shade of the Neutral color used by the system. Black. This value can be overlaid at runtime by OverlayManager RROs.-->
<color name="system_neutral1_1000">#000000</color>
-
- <!--Lightest shade of the Secondary Neutral color used by the system. White.
- This value can be overlaid at runtime by OverlayManager RROs.-->
+ <!--Lightest shade of the Secondary Neutral color used by the system. White. This value can be overlaid at runtime by OverlayManager RROs.-->
<color name="system_neutral2_0">#FFFFFF</color>
- <!--Shade of the Secondary Neutral system color at 99% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs.-->
+ <!--Shade of the Secondary Neutral system color at 99% perceptual luminance (L* in L*a*b* color space). This value can be overlaid at runtime by OverlayManager RROs.-->
<color name="system_neutral2_10">#FEFBFF</color>
- <!--Shade of the Secondary Neutral system color at 95% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs.-->
+ <!--Shade of the Secondary Neutral system color at 95% perceptual luminance (L* in L*a*b* color space). This value can be overlaid at runtime by OverlayManager RROs.-->
<color name="system_neutral2_50">#F0F0FA</color>
- <!--Shade of the Secondary Neutral system color at 90% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs.-->
+ <!--Shade of the Secondary Neutral system color at 90% perceptual luminance (L* in L*a*b* color space). This value can be overlaid at runtime by OverlayManager RROs.-->
<color name="system_neutral2_100">#E1E2EC</color>
- <!--Shade of the Secondary Neutral system color at 80% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs.-->
+ <!--Shade of the Secondary Neutral system color at 80% perceptual luminance (L* in L*a*b* color space). This value can be overlaid at runtime by OverlayManager RROs.-->
<color name="system_neutral2_200">#C5C6D0</color>
- <!--Shade of the Secondary Neutral system color at 70% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs.-->
+ <!--Shade of the Secondary Neutral system color at 70% perceptual luminance (L* in L*a*b* color space). This value can be overlaid at runtime by OverlayManager RROs.-->
<color name="system_neutral2_300">#A9ABB4</color>
- <!--Shade of the Secondary Neutral system color at 60% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs.-->
+ <!--Shade of the Secondary Neutral system color at 60% perceptual luminance (L* in L*a*b* color space). This value can be overlaid at runtime by OverlayManager RROs.-->
<color name="system_neutral2_400">#8F9099</color>
- <!--Shade of the Secondary Neutral system color at 50% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs.-->
+ <!--Shade of the Secondary Neutral system color at 50% perceptual luminance (L* in L*a*b* color space). This value can be overlaid at runtime by OverlayManager RROs.-->
<color name="system_neutral2_500">#757780</color>
- <!--Shade of the Secondary Neutral system color at 40% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs.-->
+ <!--Shade of the Secondary Neutral system color at 40% perceptual luminance (L* in L*a*b* color space). This value can be overlaid at runtime by OverlayManager RROs.-->
<color name="system_neutral2_600">#5C5E67</color>
- <!--Shade of the Secondary Neutral system color at 30% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs.-->
+ <!--Shade of the Secondary Neutral system color at 30% perceptual luminance (L* in L*a*b* color space). This value can be overlaid at runtime by OverlayManager RROs.-->
<color name="system_neutral2_700">#44464F</color>
- <!--Shade of the Secondary Neutral system color at 20% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs.-->
+ <!--Shade of the Secondary Neutral system color at 20% perceptual luminance (L* in L*a*b* color space). This value can be overlaid at runtime by OverlayManager RROs.-->
<color name="system_neutral2_800">#2E3038</color>
- <!--Shade of the Secondary Neutral system color at 10% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs.-->
+ <!--Shade of the Secondary Neutral system color at 10% perceptual luminance (L* in L*a*b* color space). This value can be overlaid at runtime by OverlayManager RROs.-->
<color name="system_neutral2_900">#191B23</color>
- <!--Darkest shade of the Secondary Neutral color used by the system. Black.
- This value can be overlaid at runtime by OverlayManager RROs.-->
+ <!--Darkest shade of the Secondary Neutral color used by the system. Black. This value can be overlaid at runtime by OverlayManager RROs.-->
<color name="system_neutral2_1000">#000000</color>
-
- <!-- Lightest shade of the error color used by the system. White.
- This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_error_0">#ffffff</color>
- <!-- Shade of the error system color at 99% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_error_10">#FFFBF9</color>
- <!-- Shade of the error system color at 95% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_error_50">#FCEEEE</color>
- <!-- Shade of the error system color at 90% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_error_100">#F9DEDC</color>
- <!-- Shade of the error system color at 80% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_error_200">#F2B8B5</color>
- <!-- Shade of the error system color at 70% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_error_300">#EC928E</color>
- <!-- Shade of the error system color at 60% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_error_400">#E46962</color>
- <!-- Shade of the error system color at 49% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_error_500">#DC362E</color>
- <!-- Shade of the error system color at 40% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_error_600">#B3261E</color>
- <!-- Shade of the error system color at 30% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_error_700">#8C1D18</color>
- <!-- Shade of the error system color at 20% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_error_800">#601410</color>
- <!-- Shade of the error system color at 10% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_error_900">#410E0B</color>
- <!-- Darkest shade of the error color used by the system. Black.
- This value can be overlaid at runtime by OverlayManager RROs. -->
+ <!--Lightest shade of the Error color used by the system. White. This value can be overlaid at runtime by OverlayManager RROs.-->
+ <color name="system_error_0">#FFFFFF</color>
+ <!--Shade of the Error system color at 99% perceptual luminance (L* in L*a*b* color space). This value can be overlaid at runtime by OverlayManager RROs.-->
+ <color name="system_error_10">#FFFBFF</color>
+ <!--Shade of the Error system color at 95% perceptual luminance (L* in L*a*b* color space). This value can be overlaid at runtime by OverlayManager RROs.-->
+ <color name="system_error_50">#FFEDEA</color>
+ <!--Shade of the Error system color at 90% perceptual luminance (L* in L*a*b* color space). This value can be overlaid at runtime by OverlayManager RROs.-->
+ <color name="system_error_100">#FFDAD6</color>
+ <!--Shade of the Error system color at 80% perceptual luminance (L* in L*a*b* color space). This value can be overlaid at runtime by OverlayManager RROs.-->
+ <color name="system_error_200">#FFB4AB</color>
+ <!--Shade of the Error system color at 70% perceptual luminance (L* in L*a*b* color space). This value can be overlaid at runtime by OverlayManager RROs.-->
+ <color name="system_error_300">#FF897D</color>
+ <!--Shade of the Error system color at 60% perceptual luminance (L* in L*a*b* color space). This value can be overlaid at runtime by OverlayManager RROs.-->
+ <color name="system_error_400">#FF5449</color>
+ <!--Shade of the Error system color at 50% perceptual luminance (L* in L*a*b* color space). This value can be overlaid at runtime by OverlayManager RROs.-->
+ <color name="system_error_500">#DE3730</color>
+ <!--Shade of the Error system color at 40% perceptual luminance (L* in L*a*b* color space). This value can be overlaid at runtime by OverlayManager RROs.-->
+ <color name="system_error_600">#BA1A1A</color>
+ <!--Shade of the Error system color at 30% perceptual luminance (L* in L*a*b* color space). This value can be overlaid at runtime by OverlayManager RROs.-->
+ <color name="system_error_700">#93000A</color>
+ <!--Shade of the Error system color at 20% perceptual luminance (L* in L*a*b* color space). This value can be overlaid at runtime by OverlayManager RROs.-->
+ <color name="system_error_800">#690005</color>
+ <!--Shade of the Error system color at 10% perceptual luminance (L* in L*a*b* color space). This value can be overlaid at runtime by OverlayManager RROs.-->
+ <color name="system_error_900">#410002</color>
+ <!--Darkest shade of the Error color used by the system. Black. This value can be overlaid at runtime by OverlayManager RROs.-->
<color name="system_error_1000">#000000</color>
- <!-- Colors used in Android system, from design system.
- These values can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_primary_container_light">#D9E2FF</color>
- <color name="system_on_primary_container_light">#001945</color>
- <color name="system_primary_light">#475D92</color>
- <color name="system_on_primary_light">#FFFFFF</color>
- <color name="system_secondary_container_light">#DCE2F9</color>
- <color name="system_on_secondary_container_light">#151B2C</color>
- <color name="system_secondary_light">#575E71</color>
- <color name="system_on_secondary_light">#FFFFFF</color>
- <color name="system_tertiary_container_light">#FDD7FA</color>
- <color name="system_on_tertiary_container_light">#2A122C</color>
- <color name="system_tertiary_light">#725572</color>
- <color name="system_on_tertiary_light">#FFFFFF</color>
- <color name="system_background_light">#FAF8FF</color>
+ <!--Colors used in Android system, from design system. These values can be overlaid at runtime by OverlayManager RROs.--><color name="system_background_light">#FAF8FF</color>
+ <color name="system_control_activated_light">#D9E2FF</color>
+ <color name="system_control_highlight_light">#000000</color>
+ <color name="system_control_normal_light">#44464F</color>
+ <color name="system_error_light">#BA1A1A</color>
+ <color name="system_error_container_light">#FFDAD6</color>
+ <color name="system_inverse_on_surface_light">#F1F0F7</color>
+ <color name="system_inverse_primary_light">#B0C6FF</color>
+ <color name="system_inverse_surface_light">#2F3036</color>
<color name="system_on_background_light">#1A1B20</color>
- <color name="system_surface_light">#FAF8FF</color>
+ <color name="system_on_error_light">#FFFFFF</color>
+ <color name="system_on_error_container_light">#93000A</color>
+ <color name="system_on_primary_light">#FFFFFF</color>
+ <color name="system_on_primary_container_light">#2F4578</color>
+ <color name="system_on_secondary_light">#FFFFFF</color>
+ <color name="system_on_secondary_container_light">#404659</color>
<color name="system_on_surface_light">#1A1B20</color>
- <color name="system_surface_container_low_light">#F4F3FA</color>
- <color name="system_surface_container_lowest_light">#FFFFFF</color>
- <color name="system_surface_container_light">#EEEDF4</color>
- <color name="system_surface_container_high_light">#E8E7EF</color>
- <color name="system_surface_container_highest_light">#E2E2E9</color>
- <color name="system_surface_bright_light">#FAF8FF</color>
- <color name="system_surface_dim_light">#DAD9E0</color>
- <color name="system_surface_variant_light">#E1E2EC</color>
<color name="system_on_surface_variant_light">#44464F</color>
+ <color name="system_on_tertiary_light">#FFFFFF</color>
+ <color name="system_on_tertiary_container_light">#593D59</color>
<color name="system_outline_light">#757780</color>
<color name="system_outline_variant_light">#C5C6D0</color>
- <color name="system_error_light">#BA1A1A</color>
- <color name="system_on_error_light">#FFFFFF</color>
- <color name="system_error_container_light">#FFDAD6</color>
- <color name="system_on_error_container_light">#410002</color>
- <color name="system_control_activated_light">#D9E2FF</color>
- <color name="system_control_normal_light">#44464F</color>
- <color name="system_control_highlight_light">#000000</color>
-<color name="system_text_primary_inverse_light">#E2E2E9</color>
- <color name="system_text_secondary_and_tertiary_inverse_light">#C5C6D0</color>
- <color name="system_text_primary_inverse_disable_only_light">#E2E2E9</color>
- <color name="system_text_secondary_and_tertiary_inverse_disabled_light">#E2E2E9</color>
- <color name="system_text_hint_inverse_light">#E2E2E9</color>
+ <color name="system_palette_key_color_neutral_light">#76777D</color>
+ <color name="system_palette_key_color_neutral_variant_light">#757780</color>
<color name="system_palette_key_color_primary_light">#6076AC</color>
<color name="system_palette_key_color_secondary_light">#70778B</color>
<color name="system_palette_key_color_tertiary_light">#8C6D8C</color>
- <color name="system_palette_key_color_neutral_light">#76777D</color>
- <color name="system_palette_key_color_neutral_variant_light">#757780</color>
- <color name="system_primary_container_dark">#2F4578</color>
- <color name="system_on_primary_container_dark">#D9E2FF</color>
- <color name="system_primary_dark">#B0C6FF</color>
- <color name="system_on_primary_dark">#152E60</color>
- <color name="system_secondary_container_dark">#404659</color>
- <color name="system_on_secondary_container_dark">#DCE2F9</color>
- <color name="system_secondary_dark">#C0C6DC</color>
- <color name="system_on_secondary_dark">#2A3042</color>
- <color name="system_tertiary_container_dark">#593D59</color>
- <color name="system_on_tertiary_container_dark">#FDD7FA</color>
- <color name="system_tertiary_dark">#E0BBDD</color>
- <color name="system_on_tertiary_dark">#412742</color>
+ <color name="system_primary_light">#475D92</color>
+ <color name="system_primary_container_light">#D9E2FF</color>
+ <color name="system_scrim_light">#000000</color>
+ <color name="system_secondary_light">#575E71</color>
+ <color name="system_secondary_container_light">#DCE2F9</color>
+ <color name="system_shadow_light">#000000</color>
+ <color name="system_surface_light">#FAF8FF</color>
+ <color name="system_surface_bright_light">#FAF8FF</color>
+ <color name="system_surface_container_light">#EEEDF4</color>
+ <color name="system_surface_container_high_light">#E8E7EF</color>
+ <color name="system_surface_container_highest_light">#E2E2E9</color>
+ <color name="system_surface_container_low_light">#F4F3FA</color>
+ <color name="system_surface_container_lowest_light">#FFFFFF</color>
+ <color name="system_surface_dim_light">#DAD9E0</color>
+ <color name="system_surface_tint_light">#475D92</color>
+ <color name="system_surface_variant_light">#E1E2EC</color>
+ <color name="system_tertiary_light">#725572</color>
+ <color name="system_tertiary_container_light">#FDD7FA</color>
+ <color name="system_text_hint_inverse_light">#E2E2E9</color>
+ <color name="system_text_primary_inverse_light">#E2E2E9</color>
+ <color name="system_text_primary_inverse_disable_only_light">#E2E2E9</color>
+ <color name="system_text_secondary_and_tertiary_inverse_light">#C5C6D0</color>
+ <color name="system_text_secondary_and_tertiary_inverse_disabled_light">#E2E2E9</color>
<color name="system_background_dark">#121318</color>
+ <color name="system_control_activated_dark">#2F4578</color>
+ <color name="system_control_highlight_dark">#FFFFFF</color>
+ <color name="system_control_normal_dark">#C5C6D0</color>
+ <color name="system_error_dark">#FFB4AB</color>
+ <color name="system_error_container_dark">#93000A</color>
+ <color name="system_inverse_on_surface_dark">#2F3036</color>
+ <color name="system_inverse_primary_dark">#475D92</color>
+ <color name="system_inverse_surface_dark">#E2E2E9</color>
<color name="system_on_background_dark">#E2E2E9</color>
- <color name="system_surface_dark">#121318</color>
+ <color name="system_on_error_dark">#690005</color>
+ <color name="system_on_error_container_dark">#FFDAD6</color>
+ <color name="system_on_primary_dark">#152E60</color>
+ <color name="system_on_primary_container_dark">#D9E2FF</color>
+ <color name="system_on_secondary_dark">#2A3042</color>
+ <color name="system_on_secondary_container_dark">#DCE2F9</color>
<color name="system_on_surface_dark">#E2E2E9</color>
- <color name="system_surface_container_low_dark">#1A1B20</color>
- <color name="system_surface_container_lowest_dark">#0C0E13</color>
- <color name="system_surface_container_dark">#1E1F25</color>
- <color name="system_surface_container_high_dark">#282A2F</color>
- <color name="system_surface_container_highest_dark">#33343A</color>
- <color name="system_surface_bright_dark">#38393F</color>
- <color name="system_surface_dim_dark">#121318</color>
- <color name="system_surface_variant_dark">#44464F</color>
<color name="system_on_surface_variant_dark">#C5C6D0</color>
+ <color name="system_on_tertiary_dark">#412742</color>
+ <color name="system_on_tertiary_container_dark">#FDD7FA</color>
<color name="system_outline_dark">#8F9099</color>
<color name="system_outline_variant_dark">#44464F</color>
- <color name="system_error_dark">#FFB4AB</color>
- <color name="system_on_error_dark">#690005</color>
- <color name="system_error_container_dark">#93000A</color>
- <color name="system_on_error_container_dark">#FFDAD6</color>
- <color name="system_control_activated_dark">#2F4578</color>
- <color name="system_control_normal_dark">#C5C6D0</color>
- <color name="system_control_highlight_dark">#FFFFFF</color>
- <color name="system_text_primary_inverse_dark">#1A1B20</color>
- <color name="system_text_secondary_and_tertiary_inverse_dark">#44464F</color>
- <color name="system_text_primary_inverse_disable_only_dark">#1A1B20</color>
- <color name="system_text_secondary_and_tertiary_inverse_disabled_dark">#1A1B20</color>
- <color name="system_text_hint_inverse_dark">#1A1B20</color>
+ <color name="system_palette_key_color_neutral_dark">#76777D</color>
+ <color name="system_palette_key_color_neutral_variant_dark">#757780</color>
<color name="system_palette_key_color_primary_dark">#6076AC</color>
<color name="system_palette_key_color_secondary_dark">#70778B</color>
<color name="system_palette_key_color_tertiary_dark">#8C6D8C</color>
- <color name="system_palette_key_color_neutral_dark">#76777D</color>
- <color name="system_palette_key_color_neutral_variant_dark">#757780</color>
- <color name="system_primary_fixed">#D9E2FF</color>
- <color name="system_primary_fixed_dim">#B0C6FF</color>
+ <color name="system_primary_dark">#B0C6FF</color>
+ <color name="system_primary_container_dark">#2F4578</color>
+ <color name="system_scrim_dark">#000000</color>
+ <color name="system_secondary_dark">#C0C6DC</color>
+ <color name="system_secondary_container_dark">#404659</color>
+ <color name="system_shadow_dark">#000000</color>
+ <color name="system_surface_dark">#121318</color>
+ <color name="system_surface_bright_dark">#38393F</color>
+ <color name="system_surface_container_dark">#1E1F25</color>
+ <color name="system_surface_container_high_dark">#282A2F</color>
+ <color name="system_surface_container_highest_dark">#33343A</color>
+ <color name="system_surface_container_low_dark">#1A1B20</color>
+ <color name="system_surface_container_lowest_dark">#0C0E13</color>
+ <color name="system_surface_dim_dark">#121318</color>
+ <color name="system_surface_tint_dark">#B0C6FF</color>
+ <color name="system_surface_variant_dark">#44464F</color>
+ <color name="system_tertiary_dark">#E0BBDD</color>
+ <color name="system_tertiary_container_dark">#593D59</color>
+ <color name="system_text_hint_inverse_dark">#1A1B20</color>
+ <color name="system_text_primary_inverse_dark">#1A1B20</color>
+ <color name="system_text_primary_inverse_disable_only_dark">#1A1B20</color>
+ <color name="system_text_secondary_and_tertiary_inverse_dark">#44464F</color>
+ <color name="system_text_secondary_and_tertiary_inverse_disabled_dark">#1A1B20</color>
<color name="system_on_primary_fixed">#001945</color>
<color name="system_on_primary_fixed_variant">#2F4578</color>
- <color name="system_secondary_fixed">#DCE2F9</color>
- <color name="system_secondary_fixed_dim">#C0C6DC</color>
<color name="system_on_secondary_fixed">#151B2C</color>
<color name="system_on_secondary_fixed_variant">#404659</color>
- <color name="system_tertiary_fixed">#FDD7FA</color>
- <color name="system_tertiary_fixed_dim">#E0BBDD</color>
<color name="system_on_tertiary_fixed">#2A122C</color>
<color name="system_on_tertiary_fixed_variant">#593D59</color>
-
- <!--Colors used in Android system, from design system. These values can be overlaid at runtime
- by OverlayManager RROs.-->
- <color name="system_widget_background_light">#EEF0FF</color>
- <color name="system_clock_hour_light">#373D50</color>
- <color name="system_clock_minute_light">#3D5487</color>
- <color name="system_clock_second_light">#4F659A</color>
- <color name="system_theme_app_light">#D9E2FF</color>
- <color name="system_on_theme_app_light">#475D92</color>
- <color name="system_theme_app_ring_light">#94AAE4</color>
- <color name="system_theme_notif_light">#E0BBDD</color>
+ <color name="system_primary_fixed">#D9E2FF</color>
+ <color name="system_primary_fixed_dim">#B0C6FF</color>
+ <color name="system_secondary_fixed">#DCE2F9</color>
+ <color name="system_secondary_fixed_dim">#C0C6DC</color>
+ <color name="system_tertiary_fixed">#FDD7FA</color>
+ <color name="system_tertiary_fixed_dim">#E0BBDD</color>
<color name="system_brand_a_light">#475D92</color>
<color name="system_brand_b_light">#6E7488</color>
<color name="system_brand_c_light">#5E73A9</color>
<color name="system_brand_d_light">#8A6A89</color>
- <color name="system_under_surface_light">#000000</color>
-<color name="system_shade_active_light">#D9E2FF</color>
+ <color name="system_clock_hour_light">#373D50</color>
+ <color name="system_clock_minute_light">#3D5487</color>
+ <color name="system_clock_second_light">#725572</color>
<color name="system_on_shade_active_light">#152E60</color>
<color name="system_on_shade_active_variant_light">#2F4578</color>
- <color name="system_shade_inactive_light">#2F3036</color>
<color name="system_on_shade_inactive_light">#E1E2EC</color>
<color name="system_on_shade_inactive_variant_light">#C5C6D0</color>
- <color name="system_shade_disabled_light">#0C0E13</color>
+ <color name="system_on_theme_app_light">#475D92</color>
<color name="system_overview_background_light">#C5C6D0</color>
- <color name="system_widget_background_dark">#152E60</color>
- <color name="system_clock_hour_dark">#8A90A5</color>
- <color name="system_clock_minute_dark">#D9E2FF</color>
- <color name="system_clock_second_dark">#B0C6FF</color>
- <color name="system_theme_app_dark">#2F4578</color>
- <color name="system_on_theme_app_dark">#B0C6FF</color>
- <color name="system_theme_app_ring_dark">#94AAE4</color>
- <color name="system_theme_notif_dark">#FDD7FA</color>
+ <color name="system_shade_active_light">#D9E2FF</color>
+ <color name="system_shade_disabled_light">#0C0E13</color>
+ <color name="system_shade_inactive_light">#2F3036</color>
+ <color name="system_theme_app_light">#D9E2FF</color>
+ <color name="system_theme_app_ring_light">#94AAE4</color>
+ <color name="system_theme_notif_light">#E0BBDD</color>
+ <color name="system_under_surface_light">#000000</color>
+ <color name="system_weather_temp_light">#4F659A</color>
+ <color name="system_widget_background_light">#EEF0FF</color>
<color name="system_brand_a_dark">#B0C6FF</color>
<color name="system_brand_b_dark">#DCE2F9</color>
<color name="system_brand_c_dark">#7A90C8</color>
<color name="system_brand_d_dark">#FDD7FA</color>
- <color name="system_under_surface_dark">#000000</color>
-<color name="system_shade_active_dark">#D9E2FF</color>
+ <color name="system_clock_hour_dark">#8A90A5</color>
+ <color name="system_clock_minute_dark">#D9E2FF</color>
+ <color name="system_clock_second_dark">#FDD7FA</color>
<color name="system_on_shade_active_dark">#001945</color>
<color name="system_on_shade_active_variant_dark">#2F4578</color>
- <color name="system_shade_inactive_dark">#2F3036</color>
<color name="system_on_shade_inactive_dark">#E1E2EC</color>
<color name="system_on_shade_inactive_variant_dark">#C5C6D0</color>
- <color name="system_shade_disabled_dark">#0C0E13</color>
+ <color name="system_on_theme_app_dark">#B0C6FF</color>
<color name="system_overview_background_dark">#50525A</color>
+ <color name="system_shade_active_dark">#D9E2FF</color>
+ <color name="system_shade_disabled_dark">#0C0E13</color>
+ <color name="system_shade_inactive_dark">#2F3036</color>
+ <color name="system_theme_app_dark">#2F4578</color>
+ <color name="system_theme_app_ring_dark">#94AAE4</color>
+ <color name="system_theme_notif_dark">#FDD7FA</color>
+ <color name="system_under_surface_dark">#000000</color>
+ <color name="system_weather_temp_dark">#B0C6FF</color>
+ <color name="system_widget_background_dark">#152E60</color>
<!-- Accessibility shortcut icon background color -->
<color name="accessibility_feature_background">#5F6368</color> <!-- Google grey 700 -->
diff --git a/core/res/res/values/colors_dynamic.xml b/core/res/res/values/colors_dynamic.xml
new file mode 100644
index 0000000..ab283eb
--- /dev/null
+++ b/core/res/res/values/colors_dynamic.xml
@@ -0,0 +1,103 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!-- Colors specific to Material themes. -->
+<resources>
+ <color name="materialColorBackground">@color/system_background_light</color>
+ <color name="materialColorControlActivated">@color/system_control_activated_light</color>
+ <color name="materialColorControlHighlight">@color/system_control_highlight_light</color>
+ <color name="materialColorControlNormal">@color/system_control_normal_light</color>
+ <color name="materialColorError">@color/system_error_light</color>
+ <color name="materialColorErrorContainer">@color/system_error_container_light</color>
+ <color name="materialColorInverseOnSurface">@color/system_inverse_on_surface_light</color>
+ <color name="materialColorInversePrimary">@color/system_inverse_primary_light</color>
+ <color name="materialColorInverseSurface">@color/system_inverse_surface_light</color>
+ <color name="materialColorOnBackground">@color/system_on_background_light</color>
+ <color name="materialColorOnError">@color/system_on_error_light</color>
+ <color name="materialColorOnErrorContainer">@color/system_on_error_container_light</color>
+ <color name="materialColorOnPrimary">@color/system_on_primary_light</color>
+ <color name="materialColorOnPrimaryContainer">@color/system_on_primary_container_light</color>
+ <color name="materialColorOnSecondary">@color/system_on_secondary_light</color>
+ <color name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_light</color>
+ <color name="materialColorOnSurface">@color/system_on_surface_light</color>
+ <color name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_light</color>
+ <color name="materialColorOnTertiary">@color/system_on_tertiary_light</color>
+ <color name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_light</color>
+ <color name="materialColorOutline">@color/system_outline_light</color>
+ <color name="materialColorOutlineVariant">@color/system_outline_variant_light</color>
+ <color name="materialColorPaletteKeyColorNeutral">@color/system_palette_key_color_neutral_light</color>
+ <color name="materialColorPaletteKeyColorNeutralVariant">@color/system_palette_key_color_neutral_variant_light</color>
+ <color name="materialColorPaletteKeyColorPrimary">@color/system_palette_key_color_primary_light</color>
+ <color name="materialColorPaletteKeyColorSecondary">@color/system_palette_key_color_secondary_light</color>
+ <color name="materialColorPaletteKeyColorTertiary">@color/system_palette_key_color_tertiary_light</color>
+ <color name="materialColorPrimary">@color/system_primary_light</color>
+ <color name="materialColorPrimaryContainer">@color/system_primary_container_light</color>
+ <color name="materialColorScrim">@color/system_scrim_light</color>
+ <color name="materialColorSecondary">@color/system_secondary_light</color>
+ <color name="materialColorSecondaryContainer">@color/system_secondary_container_light</color>
+ <color name="materialColorShadow">@color/system_shadow_light</color>
+ <color name="materialColorSurface">@color/system_surface_light</color>
+ <color name="materialColorSurfaceBright">@color/system_surface_bright_light</color>
+ <color name="materialColorSurfaceContainer">@color/system_surface_container_light</color>
+ <color name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</color>
+ <color name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</color>
+ <color name="materialColorSurfaceContainerLow">@color/system_surface_container_low_light</color>
+ <color name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_light</color>
+ <color name="materialColorSurfaceDim">@color/system_surface_dim_light</color>
+ <color name="materialColorSurfaceTint">@color/system_surface_tint_light</color>
+ <color name="materialColorSurfaceVariant">@color/system_surface_variant_light</color>
+ <color name="materialColorTertiary">@color/system_tertiary_light</color>
+ <color name="materialColorTertiaryContainer">@color/system_tertiary_container_light</color>
+ <color name="materialColorTextHintInverse">@color/system_text_hint_inverse_light</color>
+ <color name="materialColorTextPrimaryInverse">@color/system_text_primary_inverse_light</color>
+ <color name="materialColorTextPrimaryInverseDisableOnly">@color/system_text_primary_inverse_disable_only_light</color>
+ <color name="materialColorTextSecondaryAndTertiaryInverse">@color/system_text_secondary_and_tertiary_inverse_light</color>
+ <color name="materialColorTextSecondaryAndTertiaryInverseDisabled">@color/system_text_secondary_and_tertiary_inverse_disabled_light</color>
+ <color name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</color>
+ <color name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</color>
+ <color name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</color>
+ <color name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</color>
+ <color name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</color>
+ <color name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</color>
+ <color name="materialColorPrimaryFixed">@color/system_primary_fixed</color>
+ <color name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</color>
+ <color name="materialColorSecondaryFixed">@color/system_secondary_fixed</color>
+ <color name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</color>
+ <color name="materialColorTertiaryFixed">@color/system_tertiary_fixed</color>
+ <color name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</color>
+ <color name="customColorBrandA">@color/system_brand_a_light</color>
+ <color name="customColorBrandB">@color/system_brand_b_light</color>
+ <color name="customColorBrandC">@color/system_brand_c_light</color>
+ <color name="customColorBrandD">@color/system_brand_d_light</color>
+ <color name="customColorClockHour">@color/system_clock_hour_light</color>
+ <color name="customColorClockMinute">@color/system_clock_minute_light</color>
+ <color name="customColorClockSecond">@color/system_clock_second_light</color>
+ <color name="customColorOnShadeActive">@color/system_on_shade_active_light</color>
+ <color name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</color>
+ <color name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</color>
+ <color name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</color>
+ <color name="customColorOnThemeApp">@color/system_on_theme_app_light</color>
+ <color name="customColorOverviewBackground">@color/system_overview_background_light</color>
+ <color name="customColorShadeActive">@color/system_shade_active_light</color>
+ <color name="customColorShadeDisabled">@color/system_shade_disabled_light</color>
+ <color name="customColorShadeInactive">@color/system_shade_inactive_light</color>
+ <color name="customColorThemeApp">@color/system_theme_app_light</color>
+ <color name="customColorThemeAppRing">@color/system_theme_app_ring_light</color>
+ <color name="customColorThemeNotif">@color/system_theme_notif_light</color>
+ <color name="customColorUnderSurface">@color/system_under_surface_light</color>
+ <color name="customColorWeatherTemp">@color/system_weather_temp_light</color>
+ <color name="customColorWidgetBackground">@color/system_widget_background_light</color>
+</resources>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 969ee2e..3f4ea2d 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -4205,9 +4205,17 @@
must match the value of config_cameraLaunchGestureSensorType in OEM's HAL -->
<string translatable="false" name="config_cameraLaunchGestureSensorStringType"></string>
+ <!-- Allow the gesture to double tap the power button to trigger a target action. -->
+ <bool name="config_doubleTapPowerGestureEnabled">true</bool>
<!-- Allow the gesture to double tap the power button twice to start the camera while the device
is non-interactive. -->
<bool name="config_cameraDoubleTapPowerGestureEnabled">true</bool>
+ <!-- Allow the gesture to double tap the power button twice to launch the wallet. -->
+ <bool name="config_walletDoubleTapPowerGestureEnabled">true</bool>
+ <!-- Default target action for double tap of the power button gesture.
+ 0: Launch camera
+ 1: Launch wallet -->
+ <integer name="config_defaultDoubleTapPowerGestureAction">0</integer>
<!-- Allow the gesture to quick tap the power button multiple times to start the emergency sos
experience while the device is non-interactive. -->
@@ -5898,6 +5906,11 @@
<!-- <item>com.android.settings</item> -->
</string-array>
+ <!-- Certificate digests for trusted apps that will be allowed to obtain the knownSigner
+ SET_DEFAULT_ACCOUNT_FOR_CONTACTS permissions. The digest should be computed over the DER
+ encoding of the trusted certificate using the SHA-256 digest algorithm. -->
+ <string-array name="config_setContactsDefaultAccountKnownSigners">
+ </string-array>
<!-- Class name of the custom country detector to be used. -->
<string name="config_customCountryDetector" translatable="false">com.android.server.location.ComprehensiveCountryDetector</string>
@@ -6567,7 +6580,7 @@
</string-array>
<!-- the number of the max cached processes in the system. -->
- <integer name="config_customizedMaxCachedProcesses">32</integer>
+ <integer name="config_customizedMaxCachedProcesses">1024</integer>
<!-- Whether this device should support taking app snapshots on closure -->
<bool name="config_disableTaskSnapshots">false</bool>
@@ -7212,9 +7225,12 @@
<!-- Package for opening identity check settings page [CHAR LIMIT=NONE] [DO NOT TRANSLATE] -->
<string name="identity_check_settings_package_name">com\u002eandroid\u002esettings</string>
- <!-- The name of the service for forensic backup transport. -->
- <string name="config_forensicBackupTransport" translatable="false"></string>
+ <!-- The name of the service for forensic event transport. -->
+ <string name="config_forensicEventTransport" translatable="false"></string>
<!-- Whether to enable fp unlock when screen turns off on udfps devices -->
<bool name="config_screen_off_udfps_enabled">false</bool>
+
+ <!-- The name of the system package that will hold the dependency installer role. -->
+ <string name="config_systemDependencyInstaller" translatable="false" />
</resources>
diff --git a/core/res/res/values/config_telephony.xml b/core/res/res/values/config_telephony.xml
index 31e9913..4ec27a3 100644
--- a/core/res/res/values/config_telephony.xml
+++ b/core/res/res/values/config_telephony.xml
@@ -318,6 +318,12 @@
<bool name="config_oem_enabled_satellite_access_allow">true</bool>
<java-symbol type="bool" name="config_oem_enabled_satellite_access_allow" />
+ <!-- Whether the satellite modem support concurrent TN scanning while device is in
+ NTN mode.
+ -->
+ <bool name="config_satellite_modem_support_concurrent_tn_scanning">true</bool>
+ <java-symbol type="bool" name="config_satellite_modem_support_concurrent_tn_scanning" />
+
<!-- The time duration in seconds which is used to decide whether the Location returned from
LocationManager#getLastKnownLocation is fresh.
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index db75206..3757487 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -247,9 +247,14 @@
<!-- Default padding for dialogs. -->
<dimen name="dialog_padding">16dp</dimen>
- <!-- The margin on the start of the content view (accommodates the icon) -->
+ <!-- The margin on the start of the content view (accommodates the icon)
+ This represents 16dp for the left margin + 24dp for the icon + 12dp for the right margin -->
<dimen name="notification_content_margin_start">52dp</dimen>
+ <!-- The margin on the start of the content view (accommodates the icon), 2025 redesign version
+ This represents 16dp for the left margin + 40dp for the icon + 16dp for the right margin -->
+ <dimen name="notification_2025_content_margin_start">72dp</dimen>
+
<!-- The margin on the end of most content views (ignores the expander) -->
<dimen name="notification_content_margin_end">16dp</dimen>
@@ -310,6 +315,9 @@
<!-- height of the notification header -->
<dimen name="notification_header_height">56dp</dimen>
+ <!-- height of the notification header (2025 redesign version) -->
+ <dimen name="notification_2025_header_height">72dp</dimen>
+
<!-- The height of the background for a notification header on a group -->
<dimen name="notification_header_background_height">49.5dp</dimen>
@@ -331,9 +339,17 @@
<!-- size (width and height) of the circle around the icon in the notification header -->
<dimen name="notification_icon_circle_size">24dp</dimen>
+ <!-- size (width and height) of the circular icon in the notification header
+ (2025 redesign version) -->
+ <dimen name="notification_2025_icon_circle_size">40dp</dimen>
+
<!-- padding between the notification icon and the circle containing it -->
<dimen name="notification_icon_circle_padding">4dp</dimen>
+ <!-- padding between the notification icon and the circle containing it
+ (2025 redesign version) -->
+ <dimen name="notification_2025_icon_circle_padding">8dp</dimen>
+
<!-- start margin of the icon circle in the notification view -->
<dimen name="notification_icon_circle_start">16dp</dimen>
@@ -803,6 +819,8 @@
<dimen name="notification_right_icon_big_margin_top">16dp</dimen>
<!-- The size of the left icon -->
<dimen name="notification_left_icon_size">@dimen/notification_icon_circle_size</dimen>
+ <!-- The size of the left icon (2025 redesign version) -->
+ <dimen name="notification_2025_left_icon_size">@dimen/notification_2025_icon_circle_size</dimen>
<!-- The left padding of the left icon -->
<dimen name="notification_left_icon_start">@dimen/notification_icon_circle_start</dimen>
<!-- The alpha of a disabled notification button -->
@@ -820,13 +838,13 @@
<!-- The gap between segments in the notification progress bar -->
<dimen name="notification_progress_segSeg_gap">2dp</dimen>
<!-- The gap between a segment and a point in the notification progress bar -->
- <dimen name="notification_progress_segPoint_gap">8dp</dimen>
- <!-- The dash gap of the notification progress bar segments -->
- <dimen name="notification_progress_segments_dash_gap">8dp</dimen>
- <!-- The dash width of the notification progress bar segments -->
- <dimen name="notification_progress_segments_dash_width">3dp</dimen>
+ <dimen name="notification_progress_segPoint_gap">4dp</dimen>
<!-- The height of the notification progress bar segments -->
<dimen name="notification_progress_segments_height">6dp</dimen>
+ <!-- The height of the notification progress bar faded segments -->
+ <dimen name="notification_progress_segments_faded_height">2dp</dimen>
+ <!-- The corner radius of the notification progress bar segments -->
+ <dimen name="notification_progress_segments_corner_radius">16dp</dimen>
<!-- The radius of the notification progress bar points -->
<dimen name="notification_progress_points_radius">6dp</dimen>
<!-- The corner radius of the notification progress bar points drawn as rects -->
diff --git a/core/res/res/values/public-staging.xml b/core/res/res/values/public-staging.xml
index b6436d0..b0b87d1d 100644
--- a/core/res/res/values/public-staging.xml
+++ b/core/res/res/values/public-staging.xml
@@ -131,6 +131,8 @@
<public name="alternateLauncherIcons"/>
<!-- @FlaggedApi(android.content.pm.Flags.FLAG_CHANGE_LAUNCHER_BADGING) -->
<public name="alternateLauncherLabels"/>
+ <!-- @FlaggedApi(android.content.pm.Flags.FLAG_APP_COMPAT_OPTION_16KB) -->
+ <public name="pageSizeCompat" />
</staging-public-group>
<staging-public-group type="id" first-id="0x01b60000">
@@ -140,12 +142,39 @@
</staging-public-group>
<staging-public-group type="string" first-id="0x01b40000">
+ <!-- @FlaggedApi(android.content.pm.Flags.FLAG_SDK_DEPENDENCY_INSTALLER)
+ @hide @SystemApi -->
+ <public name="config_systemDependencyInstaller" />
</staging-public-group>
<staging-public-group type="dimen" first-id="0x01b30000">
</staging-public-group>
<staging-public-group type="color" first-id="0x01b20000">
+ <!-- @FlaggedApi(android.os.Flags.FLAG_MATERIAL_COLORS_10_2024)-->
+ <public name="system_inverse_on_surface_light"/>
+ <!-- @FlaggedApi(android.os.Flags.FLAG_MATERIAL_COLORS_10_2024)-->
+ <public name="system_inverse_primary_light"/>
+ <!-- @FlaggedApi(android.os.Flags.FLAG_MATERIAL_COLORS_10_2024)-->
+ <public name="system_inverse_surface_light"/>
+ <!-- @FlaggedApi(android.os.Flags.FLAG_MATERIAL_COLORS_10_2024)-->
+ <public name="system_scrim_light"/>
+ <!-- @FlaggedApi(android.os.Flags.FLAG_MATERIAL_COLORS_10_2024)-->
+ <public name="system_shadow_light"/>
+ <!-- @FlaggedApi(android.os.Flags.FLAG_MATERIAL_COLORS_10_2024)-->
+ <public name="system_surface_tint_light"/>
+ <!-- @FlaggedApi(android.os.Flags.FLAG_MATERIAL_COLORS_10_2024)-->
+ <public name="system_inverse_on_surface_dark"/>
+ <!-- @FlaggedApi(android.os.Flags.FLAG_MATERIAL_COLORS_10_2024)-->
+ <public name="system_inverse_primary_dark"/>
+ <!-- @FlaggedApi(android.os.Flags.FLAG_MATERIAL_COLORS_10_2024)-->
+ <public name="system_inverse_surface_dark"/>
+ <!-- @FlaggedApi(android.os.Flags.FLAG_MATERIAL_COLORS_10_2024)-->
+ <public name="system_scrim_dark"/>
+ <!-- @FlaggedApi(android.os.Flags.FLAG_MATERIAL_COLORS_10_2024)-->
+ <public name="system_shadow_dark"/>
+ <!-- @FlaggedApi(android.os.Flags.FLAG_MATERIAL_COLORS_10_2024)-->
+ <public name="system_surface_tint_dark"/>
</staging-public-group>
<staging-public-group type="array" first-id="0x01b10000">
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 9dd3027..5a6b66c 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2389,6 +2389,9 @@
<java-symbol type="layout" name="notification_material_action" />
<java-symbol type="layout" name="notification_material_action_list" />
<java-symbol type="layout" name="notification_material_action_tombstone" />
+ <java-symbol type="layout" name="notification_2025_template_collapsed_base" />
+ <java-symbol type="layout" name="notification_2025_template_heads_up_base" />
+ <java-symbol type="layout" name="notification_2025_template_header" />
<java-symbol type="layout" name="notification_template_material_base" />
<java-symbol type="layout" name="notification_template_material_heads_up_base" />
<java-symbol type="layout" name="notification_template_material_compact_heads_up_base" />
@@ -3140,9 +3143,12 @@
<!-- Gesture -->
<java-symbol type="integer" name="config_cameraLaunchGestureSensorType" />
<java-symbol type="string" name="config_cameraLaunchGestureSensorStringType" />
- <java-symbol type="bool" name="config_cameraDoubleTapPowerGestureEnabled" />
<java-symbol type="integer" name="config_cameraLiftTriggerSensorType" />
<java-symbol type="string" name="config_cameraLiftTriggerSensorStringType" />
+ <java-symbol type="bool" name="config_doubleTapPowerGestureEnabled" />
+ <java-symbol type="bool" name="config_cameraDoubleTapPowerGestureEnabled" />
+ <java-symbol type="bool" name="config_walletDoubleTapPowerGestureEnabled" />
+ <java-symbol type="integer" name="config_defaultDoubleTapPowerGestureAction" />
<java-symbol type="bool" name="config_emergencyGestureEnabled" />
<java-symbol type="bool" name="config_defaultEmergencyGestureEnabled" />
<java-symbol type="bool" name="config_defaultEmergencyGestureSoundEnabled" />
@@ -3876,9 +3882,9 @@
<java-symbol type="dimen" name="notification_progress_tracker_height" />
<java-symbol type="dimen" name="notification_progress_segSeg_gap" />
<java-symbol type="dimen" name="notification_progress_segPoint_gap" />
- <java-symbol type="dimen" name="notification_progress_segments_dash_gap" />
- <java-symbol type="dimen" name="notification_progress_segments_dash_width" />
<java-symbol type="dimen" name="notification_progress_segments_height" />
+ <java-symbol type="dimen" name="notification_progress_segments_faded_height" />
+ <java-symbol type="dimen" name="notification_progress_segments_corner_radius" />
<java-symbol type="dimen" name="notification_progress_points_radius" />
<java-symbol type="dimen" name="notification_progress_points_corner_radius" />
<java-symbol type="dimen" name="notification_progress_points_inset" />
@@ -5311,73 +5317,91 @@
<java-symbol type="integer" name="config_aggregatedPowerStatsSpanDuration" />
<java-symbol type="integer" name="config_accumulatedBatteryUsageStatsSpanSize" />
- <java-symbol name="materialColorOnSecondaryFixedVariant" type="attr"/>
- <java-symbol name="materialColorOnTertiaryFixedVariant" type="attr"/>
- <java-symbol name="materialColorSurfaceContainerLowest" type="attr"/>
- <java-symbol name="materialColorOnPrimaryFixedVariant" type="attr"/>
- <java-symbol name="materialColorOnSecondaryContainer" type="attr"/>
- <java-symbol name="materialColorOnTertiaryContainer" type="attr"/>
- <java-symbol name="materialColorSurfaceContainerLow" type="attr"/>
- <java-symbol name="materialColorOnPrimaryContainer" type="attr"/>
- <java-symbol name="materialColorSecondaryFixedDim" type="attr"/>
- <java-symbol name="materialColorOnErrorContainer" type="attr"/>
- <java-symbol name="materialColorOnSecondaryFixed" type="attr"/>
- <java-symbol name="materialColorOnSurfaceInverse" type="attr"/>
- <java-symbol name="materialColorTertiaryFixedDim" type="attr"/>
- <java-symbol name="materialColorOnTertiaryFixed" type="attr"/>
- <java-symbol name="materialColorPrimaryFixedDim" type="attr"/>
- <java-symbol name="materialColorSecondaryContainer" type="attr"/>
- <java-symbol name="materialColorErrorContainer" type="attr"/>
- <java-symbol name="materialColorOnPrimaryFixed" type="attr"/>
- <java-symbol name="materialColorPrimaryInverse" type="attr"/>
- <java-symbol name="materialColorSecondaryFixed" type="attr"/>
- <java-symbol name="materialColorSurfaceInverse" type="attr"/>
- <java-symbol name="materialColorSurfaceVariant" type="attr"/>
- <java-symbol name="materialColorTertiaryContainer" type="attr"/>
- <java-symbol name="materialColorTertiaryFixed" type="attr"/>
- <java-symbol name="materialColorPrimaryContainer" type="attr"/>
- <java-symbol name="materialColorOnBackground" type="attr"/>
- <java-symbol name="materialColorPrimaryFixed" type="attr"/>
- <java-symbol name="materialColorOnSecondary" type="attr"/>
- <java-symbol name="materialColorOnTertiary" type="attr"/>
- <java-symbol name="materialColorSurfaceDim" type="attr"/>
- <java-symbol name="materialColorSurfaceBright" type="attr"/>
- <java-symbol name="materialColorOnError" type="attr"/>
- <java-symbol name="materialColorSurface" type="attr"/>
- <java-symbol name="materialColorSurfaceContainerHigh" type="attr"/>
- <java-symbol name="materialColorSurfaceContainerHighest" type="attr"/>
- <java-symbol name="materialColorOnSurfaceVariant" type="attr"/>
- <java-symbol name="materialColorOutline" type="attr"/>
- <java-symbol name="materialColorOutlineVariant" type="attr"/>
- <java-symbol name="materialColorOnPrimary" type="attr"/>
- <java-symbol name="materialColorOnSurface" type="attr"/>
- <java-symbol name="materialColorSurfaceContainer" type="attr"/>
- <java-symbol name="materialColorPrimary" type="attr"/>
- <java-symbol name="materialColorSecondary" type="attr"/>
- <java-symbol name="materialColorTertiary" type="attr"/>
- <java-symbol name="materialColorError" type="attr"/>
-
- <java-symbol name="customColorWidgetBackground" type="attr"/>
- <java-symbol name="customColorClockHour" type="attr"/>
- <java-symbol name="customColorClockMinute" type="attr"/>
- <java-symbol name="customColorClockSecond" type="attr"/>
- <java-symbol name="customColorThemeApp" type="attr"/>
- <java-symbol name="customColorOnThemeApp" type="attr"/>
- <java-symbol name="customColorThemeAppRing" type="attr"/>
- <java-symbol name="customColorThemeNotif" type="attr"/>
- <java-symbol name="customColorBrandA" type="attr"/>
- <java-symbol name="customColorBrandB" type="attr"/>
- <java-symbol name="customColorBrandC" type="attr"/>
- <java-symbol name="customColorBrandD" type="attr"/>
- <java-symbol name="customColorUnderSurface" type="attr"/>
- <java-symbol name="customColorShadeActive" type="attr"/>
- <java-symbol name="customColorOnShadeActive" type="attr"/>
- <java-symbol name="customColorOnShadeActiveVariant" type="attr"/>
- <java-symbol name="customColorShadeInactive" type="attr"/>
- <java-symbol name="customColorOnShadeInactive" type="attr"/>
- <java-symbol name="customColorOnShadeInactiveVariant" type="attr"/>
- <java-symbol name="customColorShadeDisabled" type="attr"/>
- <java-symbol name="customColorOverviewBackground" type="attr"/>
+ <!--Dynamic Tokens-->
+ <java-symbol type="attr" name="materialColorBackground"/>
+ <java-symbol type="attr" name="materialColorControlActivated"/>
+ <java-symbol type="attr" name="materialColorControlHighlight"/>
+ <java-symbol type="attr" name="materialColorControlNormal"/>
+ <java-symbol type="attr" name="materialColorError"/>
+ <java-symbol type="attr" name="materialColorErrorContainer"/>
+ <java-symbol type="attr" name="materialColorInverseOnSurface"/>
+ <java-symbol type="attr" name="materialColorInversePrimary"/>
+ <java-symbol type="attr" name="materialColorInverseSurface"/>
+ <java-symbol type="attr" name="materialColorOnBackground"/>
+ <java-symbol type="attr" name="materialColorOnError"/>
+ <java-symbol type="attr" name="materialColorOnErrorContainer"/>
+ <java-symbol type="attr" name="materialColorOnPrimary"/>
+ <java-symbol type="attr" name="materialColorOnPrimaryContainer"/>
+ <java-symbol type="attr" name="materialColorOnSecondary"/>
+ <java-symbol type="attr" name="materialColorOnSecondaryContainer"/>
+ <java-symbol type="attr" name="materialColorOnSurface"/>
+ <java-symbol type="attr" name="materialColorOnSurfaceVariant"/>
+ <java-symbol type="attr" name="materialColorOnTertiary"/>
+ <java-symbol type="attr" name="materialColorOnTertiaryContainer"/>
+ <java-symbol type="attr" name="materialColorOutline"/>
+ <java-symbol type="attr" name="materialColorOutlineVariant"/>
+ <java-symbol type="attr" name="materialColorPaletteKeyColorNeutral"/>
+ <java-symbol type="attr" name="materialColorPaletteKeyColorNeutralVariant"/>
+ <java-symbol type="attr" name="materialColorPaletteKeyColorPrimary"/>
+ <java-symbol type="attr" name="materialColorPaletteKeyColorSecondary"/>
+ <java-symbol type="attr" name="materialColorPaletteKeyColorTertiary"/>
+ <java-symbol type="attr" name="materialColorPrimary"/>
+ <java-symbol type="attr" name="materialColorPrimaryContainer"/>
+ <java-symbol type="attr" name="materialColorScrim"/>
+ <java-symbol type="attr" name="materialColorSecondary"/>
+ <java-symbol type="attr" name="materialColorSecondaryContainer"/>
+ <java-symbol type="attr" name="materialColorShadow"/>
+ <java-symbol type="attr" name="materialColorSurface"/>
+ <java-symbol type="attr" name="materialColorSurfaceBright"/>
+ <java-symbol type="attr" name="materialColorSurfaceContainer"/>
+ <java-symbol type="attr" name="materialColorSurfaceContainerHigh"/>
+ <java-symbol type="attr" name="materialColorSurfaceContainerHighest"/>
+ <java-symbol type="attr" name="materialColorSurfaceContainerLow"/>
+ <java-symbol type="attr" name="materialColorSurfaceContainerLowest"/>
+ <java-symbol type="attr" name="materialColorSurfaceDim"/>
+ <java-symbol type="attr" name="materialColorSurfaceTint"/>
+ <java-symbol type="attr" name="materialColorSurfaceVariant"/>
+ <java-symbol type="attr" name="materialColorTertiary"/>
+ <java-symbol type="attr" name="materialColorTertiaryContainer"/>
+ <java-symbol type="attr" name="materialColorTextHintInverse"/>
+ <java-symbol type="attr" name="materialColorTextPrimaryInverse"/>
+ <java-symbol type="attr" name="materialColorTextPrimaryInverseDisableOnly"/>
+ <java-symbol type="attr" name="materialColorTextSecondaryAndTertiaryInverse"/>
+ <java-symbol type="attr" name="materialColorTextSecondaryAndTertiaryInverseDisabled"/>
+ <java-symbol type="attr" name="materialColorOnPrimaryFixed"/>
+ <java-symbol type="attr" name="materialColorOnPrimaryFixedVariant"/>
+ <java-symbol type="attr" name="materialColorOnSecondaryFixed"/>
+ <java-symbol type="attr" name="materialColorOnSecondaryFixedVariant"/>
+ <java-symbol type="attr" name="materialColorOnTertiaryFixed"/>
+ <java-symbol type="attr" name="materialColorOnTertiaryFixedVariant"/>
+ <java-symbol type="attr" name="materialColorPrimaryFixed"/>
+ <java-symbol type="attr" name="materialColorPrimaryFixedDim"/>
+ <java-symbol type="attr" name="materialColorSecondaryFixed"/>
+ <java-symbol type="attr" name="materialColorSecondaryFixedDim"/>
+ <java-symbol type="attr" name="materialColorTertiaryFixed"/>
+ <java-symbol type="attr" name="materialColorTertiaryFixedDim"/>
+ <java-symbol type="attr" name="customColorBrandA"/>
+ <java-symbol type="attr" name="customColorBrandB"/>
+ <java-symbol type="attr" name="customColorBrandC"/>
+ <java-symbol type="attr" name="customColorBrandD"/>
+ <java-symbol type="attr" name="customColorClockHour"/>
+ <java-symbol type="attr" name="customColorClockMinute"/>
+ <java-symbol type="attr" name="customColorClockSecond"/>
+ <java-symbol type="attr" name="customColorOnShadeActive"/>
+ <java-symbol type="attr" name="customColorOnShadeActiveVariant"/>
+ <java-symbol type="attr" name="customColorOnShadeInactive"/>
+ <java-symbol type="attr" name="customColorOnShadeInactiveVariant"/>
+ <java-symbol type="attr" name="customColorOnThemeApp"/>
+ <java-symbol type="attr" name="customColorOverviewBackground"/>
+ <java-symbol type="attr" name="customColorShadeActive"/>
+ <java-symbol type="attr" name="customColorShadeDisabled"/>
+ <java-symbol type="attr" name="customColorShadeInactive"/>
+ <java-symbol type="attr" name="customColorThemeApp"/>
+ <java-symbol type="attr" name="customColorThemeAppRing"/>
+ <java-symbol type="attr" name="customColorThemeNotif"/>
+ <java-symbol type="attr" name="customColorUnderSurface"/>
+ <java-symbol type="attr" name="customColorWeatherTemp"/>
+ <java-symbol type="attr" name="customColorWidgetBackground"/>
<java-symbol name="system_widget_background_light" type="color"/>
<java-symbol name="system_clock_hour_light" type="color"/>
@@ -5679,8 +5703,8 @@
<java-symbol type="string" name="identity_check_settings_action" />
<java-symbol type="string" name="identity_check_settings_package_name" />
- <!-- Forensic backup transport -->
- <java-symbol type="string" name="config_forensicBackupTransport" />
+ <!-- Forensic event transport -->
+ <java-symbol type="string" name="config_forensicEventTransport" />
<!-- Fingerprint screen off unlock config -->
<java-symbol type="bool" name="config_screen_off_udfps_enabled" />
diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml
index 352c390..d8346d8 100644
--- a/core/res/res/values/themes_device_defaults.xml
+++ b/core/res/res/values/themes_device_defaults.xml
@@ -239,73 +239,90 @@
<item name="colorForeground">@color/foreground_device_default_dark</item>
<item name="colorForegroundInverse">@color/foreground_device_default_light</item>
- <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
- <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
- <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_dark</item>
- <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
- <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_dark</item>
- <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_dark</item>
- <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_dark</item>
- <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_dark</item>
- <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
- <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
- <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
- <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
- <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
- <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
- <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
+ <item name="materialColorBackground">@color/system_background_dark</item>
+ <item name="materialColorControlActivated">@color/system_control_activated_dark</item>
+ <item name="materialColorControlHighlight">@color/system_control_highlight_dark</item>
+ <item name="materialColorControlNormal">@color/system_control_normal_dark</item>
+ <item name="materialColorError">@color/system_error_dark</item>
<item name="materialColorErrorContainer">@color/system_error_container_dark</item>
- <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
- <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
- <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
- <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
- <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
- <item name="materialColorPrimaryContainer">@color/system_primary_container_dark</item>
+ <item name="materialColorInverseOnSurface">@color/system_inverse_on_surface_dark</item>
+ <item name="materialColorInversePrimary">@color/system_inverse_primary_dark</item>
+ <item name="materialColorInverseSurface">@color/system_inverse_surface_dark</item>
<item name="materialColorOnBackground">@color/system_on_background_dark</item>
- <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
- <item name="materialColorOnSecondary">@color/system_on_secondary_dark</item>
- <item name="materialColorOnTertiary">@color/system_on_tertiary_dark</item>
- <item name="materialColorSurfaceDim">@color/system_surface_dim_dark</item>
- <item name="materialColorSurfaceBright">@color/system_surface_bright_dark</item>
<item name="materialColorOnError">@color/system_on_error_dark</item>
- <item name="materialColorSurface">@color/system_surface_dark</item>
- <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_dark</item>
- <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_dark</item>
+ <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
+ <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
+ <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_dark</item>
+ <item name="materialColorOnSecondary">@color/system_on_secondary_dark</item>
+ <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_dark</item>
+ <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
<item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_dark</item>
+ <item name="materialColorOnTertiary">@color/system_on_tertiary_dark</item>
+ <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_dark</item>
<item name="materialColorOutline">@color/system_outline_dark</item>
<item name="materialColorOutlineVariant">@color/system_outline_variant_dark</item>
- <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
- <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
+ <item name="materialColorPaletteKeyColorNeutral">@color/system_palette_key_color_neutral_dark</item>
+ <item name="materialColorPaletteKeyColorNeutralVariant">@color/system_palette_key_color_neutral_variant_dark</item>
+ <item name="materialColorPaletteKeyColorPrimary">@color/system_palette_key_color_primary_dark</item>
+ <item name="materialColorPaletteKeyColorSecondary">@color/system_palette_key_color_secondary_dark</item>
+ <item name="materialColorPaletteKeyColorTertiary">@color/system_palette_key_color_tertiary_dark</item>
<item name="materialColorPrimary">@color/system_primary_dark</item>
+ <item name="materialColorPrimaryContainer">@color/system_primary_container_dark</item>
+ <item name="materialColorScrim">@color/system_scrim_dark</item>
<item name="materialColorSecondary">@color/system_secondary_dark</item>
+ <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
+ <item name="materialColorShadow">@color/system_shadow_dark</item>
+ <item name="materialColorSurface">@color/system_surface_dark</item>
+ <item name="materialColorSurfaceBright">@color/system_surface_bright_dark</item>
+ <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
+ <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_dark</item>
+ <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_dark</item>
+ <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_dark</item>
+ <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_dark</item>
+ <item name="materialColorSurfaceDim">@color/system_surface_dim_dark</item>
+ <item name="materialColorSurfaceTint">@color/system_surface_tint_dark</item>
+ <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
<item name="materialColorTertiary">@color/system_tertiary_dark</item>
- <item name="materialColorError">@color/system_error_dark</item>
-
- <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
- <item name="customColorClockHour">@color/system_clock_hour_dark</item>
- <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
- <item name="customColorClockSecond">@color/system_clock_second_dark</item>
- <item name="customColorThemeApp">@color/system_theme_app_dark</item>
- <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
- <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
- <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
+ <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
+ <item name="materialColorTextHintInverse">@color/system_text_hint_inverse_dark</item>
+ <item name="materialColorTextPrimaryInverse">@color/system_text_primary_inverse_dark</item>
+ <item name="materialColorTextPrimaryInverseDisableOnly">@color/system_text_primary_inverse_disable_only_dark</item>
+ <item name="materialColorTextSecondaryAndTertiaryInverse">@color/system_text_secondary_and_tertiary_inverse_dark</item>
+ <item name="materialColorTextSecondaryAndTertiaryInverseDisabled">@color/system_text_secondary_and_tertiary_inverse_disabled_dark</item>
+ <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
+ <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
+ <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
+ <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
+ <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
+ <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
+ <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
+ <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
+ <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
+ <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
+ <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
+ <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="customColorBrandA">@color/system_brand_a_dark</item>
<item name="customColorBrandB">@color/system_brand_b_dark</item>
<item name="customColorBrandC">@color/system_brand_c_dark</item>
<item name="customColorBrandD">@color/system_brand_d_dark</item>
- <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
- <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+ <item name="customColorClockHour">@color/system_clock_hour_dark</item>
+ <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
+ <item name="customColorClockSecond">@color/system_clock_second_dark</item>
<item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
<item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
- <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
<item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
<item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
- <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+ <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
<item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
+ <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+ <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+ <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
+ <item name="customColorThemeApp">@color/system_theme_app_dark</item>
+ <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
+ <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
+ <item name="customColorWeatherTemp">@color/system_weather_temp_dark</item>
+ <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
</style>
<style name="Theme.DeviceDefault" parent="Theme.DeviceDefaultBase" />
@@ -357,73 +374,90 @@
<!-- Toolbar attributes -->
<item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
- <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
- <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
- <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_dark</item>
- <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
- <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_dark</item>
- <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_dark</item>
- <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_dark</item>
- <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_dark</item>
- <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
- <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
- <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
- <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
- <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
- <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
- <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
+ <item name="materialColorBackground">@color/system_background_dark</item>
+ <item name="materialColorControlActivated">@color/system_control_activated_dark</item>
+ <item name="materialColorControlHighlight">@color/system_control_highlight_dark</item>
+ <item name="materialColorControlNormal">@color/system_control_normal_dark</item>
+ <item name="materialColorError">@color/system_error_dark</item>
<item name="materialColorErrorContainer">@color/system_error_container_dark</item>
- <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
- <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
- <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
- <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
- <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
- <item name="materialColorPrimaryContainer">@color/system_primary_container_dark</item>
+ <item name="materialColorInverseOnSurface">@color/system_inverse_on_surface_dark</item>
+ <item name="materialColorInversePrimary">@color/system_inverse_primary_dark</item>
+ <item name="materialColorInverseSurface">@color/system_inverse_surface_dark</item>
<item name="materialColorOnBackground">@color/system_on_background_dark</item>
- <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
- <item name="materialColorOnSecondary">@color/system_on_secondary_dark</item>
- <item name="materialColorOnTertiary">@color/system_on_tertiary_dark</item>
- <item name="materialColorSurfaceDim">@color/system_surface_dim_dark</item>
- <item name="materialColorSurfaceBright">@color/system_surface_bright_dark</item>
<item name="materialColorOnError">@color/system_on_error_dark</item>
- <item name="materialColorSurface">@color/system_surface_dark</item>
- <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_dark</item>
- <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_dark</item>
+ <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
+ <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
+ <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_dark</item>
+ <item name="materialColorOnSecondary">@color/system_on_secondary_dark</item>
+ <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_dark</item>
+ <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
<item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_dark</item>
+ <item name="materialColorOnTertiary">@color/system_on_tertiary_dark</item>
+ <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_dark</item>
<item name="materialColorOutline">@color/system_outline_dark</item>
<item name="materialColorOutlineVariant">@color/system_outline_variant_dark</item>
- <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
- <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
+ <item name="materialColorPaletteKeyColorNeutral">@color/system_palette_key_color_neutral_dark</item>
+ <item name="materialColorPaletteKeyColorNeutralVariant">@color/system_palette_key_color_neutral_variant_dark</item>
+ <item name="materialColorPaletteKeyColorPrimary">@color/system_palette_key_color_primary_dark</item>
+ <item name="materialColorPaletteKeyColorSecondary">@color/system_palette_key_color_secondary_dark</item>
+ <item name="materialColorPaletteKeyColorTertiary">@color/system_palette_key_color_tertiary_dark</item>
<item name="materialColorPrimary">@color/system_primary_dark</item>
+ <item name="materialColorPrimaryContainer">@color/system_primary_container_dark</item>
+ <item name="materialColorScrim">@color/system_scrim_dark</item>
<item name="materialColorSecondary">@color/system_secondary_dark</item>
+ <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
+ <item name="materialColorShadow">@color/system_shadow_dark</item>
+ <item name="materialColorSurface">@color/system_surface_dark</item>
+ <item name="materialColorSurfaceBright">@color/system_surface_bright_dark</item>
+ <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
+ <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_dark</item>
+ <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_dark</item>
+ <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_dark</item>
+ <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_dark</item>
+ <item name="materialColorSurfaceDim">@color/system_surface_dim_dark</item>
+ <item name="materialColorSurfaceTint">@color/system_surface_tint_dark</item>
+ <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
<item name="materialColorTertiary">@color/system_tertiary_dark</item>
- <item name="materialColorError">@color/system_error_dark</item>
-
- <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
- <item name="customColorClockHour">@color/system_clock_hour_dark</item>
- <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
- <item name="customColorClockSecond">@color/system_clock_second_dark</item>
- <item name="customColorThemeApp">@color/system_theme_app_dark</item>
- <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
- <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
- <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
+ <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
+ <item name="materialColorTextHintInverse">@color/system_text_hint_inverse_dark</item>
+ <item name="materialColorTextPrimaryInverse">@color/system_text_primary_inverse_dark</item>
+ <item name="materialColorTextPrimaryInverseDisableOnly">@color/system_text_primary_inverse_disable_only_dark</item>
+ <item name="materialColorTextSecondaryAndTertiaryInverse">@color/system_text_secondary_and_tertiary_inverse_dark</item>
+ <item name="materialColorTextSecondaryAndTertiaryInverseDisabled">@color/system_text_secondary_and_tertiary_inverse_disabled_dark</item>
+ <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
+ <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
+ <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
+ <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
+ <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
+ <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
+ <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
+ <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
+ <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
+ <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
+ <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
+ <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="customColorBrandA">@color/system_brand_a_dark</item>
<item name="customColorBrandB">@color/system_brand_b_dark</item>
<item name="customColorBrandC">@color/system_brand_c_dark</item>
<item name="customColorBrandD">@color/system_brand_d_dark</item>
- <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
- <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+ <item name="customColorClockHour">@color/system_clock_hour_dark</item>
+ <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
+ <item name="customColorClockSecond">@color/system_clock_second_dark</item>
<item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
<item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
- <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
<item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
<item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
- <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+ <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
<item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
+ <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+ <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+ <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
+ <item name="customColorThemeApp">@color/system_theme_app_dark</item>
+ <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
+ <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
+ <item name="customColorWeatherTemp">@color/system_weather_temp_dark</item>
+ <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault} with no action bar and no status bar. This theme
@@ -474,73 +508,90 @@
<!-- Toolbar attributes -->
<item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
- <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
- <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
- <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_dark</item>
- <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
- <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_dark</item>
- <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_dark</item>
- <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_dark</item>
- <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_dark</item>
- <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
- <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
- <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
- <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
- <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
- <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
- <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
+ <item name="materialColorBackground">@color/system_background_dark</item>
+ <item name="materialColorControlActivated">@color/system_control_activated_dark</item>
+ <item name="materialColorControlHighlight">@color/system_control_highlight_dark</item>
+ <item name="materialColorControlNormal">@color/system_control_normal_dark</item>
+ <item name="materialColorError">@color/system_error_dark</item>
<item name="materialColorErrorContainer">@color/system_error_container_dark</item>
- <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
- <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
- <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
- <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
- <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
- <item name="materialColorPrimaryContainer">@color/system_primary_container_dark</item>
+ <item name="materialColorInverseOnSurface">@color/system_inverse_on_surface_dark</item>
+ <item name="materialColorInversePrimary">@color/system_inverse_primary_dark</item>
+ <item name="materialColorInverseSurface">@color/system_inverse_surface_dark</item>
<item name="materialColorOnBackground">@color/system_on_background_dark</item>
- <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
- <item name="materialColorOnSecondary">@color/system_on_secondary_dark</item>
- <item name="materialColorOnTertiary">@color/system_on_tertiary_dark</item>
- <item name="materialColorSurfaceDim">@color/system_surface_dim_dark</item>
- <item name="materialColorSurfaceBright">@color/system_surface_bright_dark</item>
<item name="materialColorOnError">@color/system_on_error_dark</item>
- <item name="materialColorSurface">@color/system_surface_dark</item>
- <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_dark</item>
- <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_dark</item>
+ <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
+ <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
+ <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_dark</item>
+ <item name="materialColorOnSecondary">@color/system_on_secondary_dark</item>
+ <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_dark</item>
+ <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
<item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_dark</item>
+ <item name="materialColorOnTertiary">@color/system_on_tertiary_dark</item>
+ <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_dark</item>
<item name="materialColorOutline">@color/system_outline_dark</item>
<item name="materialColorOutlineVariant">@color/system_outline_variant_dark</item>
- <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
- <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
+ <item name="materialColorPaletteKeyColorNeutral">@color/system_palette_key_color_neutral_dark</item>
+ <item name="materialColorPaletteKeyColorNeutralVariant">@color/system_palette_key_color_neutral_variant_dark</item>
+ <item name="materialColorPaletteKeyColorPrimary">@color/system_palette_key_color_primary_dark</item>
+ <item name="materialColorPaletteKeyColorSecondary">@color/system_palette_key_color_secondary_dark</item>
+ <item name="materialColorPaletteKeyColorTertiary">@color/system_palette_key_color_tertiary_dark</item>
<item name="materialColorPrimary">@color/system_primary_dark</item>
+ <item name="materialColorPrimaryContainer">@color/system_primary_container_dark</item>
+ <item name="materialColorScrim">@color/system_scrim_dark</item>
<item name="materialColorSecondary">@color/system_secondary_dark</item>
+ <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
+ <item name="materialColorShadow">@color/system_shadow_dark</item>
+ <item name="materialColorSurface">@color/system_surface_dark</item>
+ <item name="materialColorSurfaceBright">@color/system_surface_bright_dark</item>
+ <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
+ <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_dark</item>
+ <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_dark</item>
+ <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_dark</item>
+ <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_dark</item>
+ <item name="materialColorSurfaceDim">@color/system_surface_dim_dark</item>
+ <item name="materialColorSurfaceTint">@color/system_surface_tint_dark</item>
+ <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
<item name="materialColorTertiary">@color/system_tertiary_dark</item>
- <item name="materialColorError">@color/system_error_dark</item>
-
- <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
- <item name="customColorClockHour">@color/system_clock_hour_dark</item>
- <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
- <item name="customColorClockSecond">@color/system_clock_second_dark</item>
- <item name="customColorThemeApp">@color/system_theme_app_dark</item>
- <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
- <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
- <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
+ <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
+ <item name="materialColorTextHintInverse">@color/system_text_hint_inverse_dark</item>
+ <item name="materialColorTextPrimaryInverse">@color/system_text_primary_inverse_dark</item>
+ <item name="materialColorTextPrimaryInverseDisableOnly">@color/system_text_primary_inverse_disable_only_dark</item>
+ <item name="materialColorTextSecondaryAndTertiaryInverse">@color/system_text_secondary_and_tertiary_inverse_dark</item>
+ <item name="materialColorTextSecondaryAndTertiaryInverseDisabled">@color/system_text_secondary_and_tertiary_inverse_disabled_dark</item>
+ <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
+ <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
+ <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
+ <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
+ <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
+ <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
+ <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
+ <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
+ <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
+ <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
+ <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
+ <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="customColorBrandA">@color/system_brand_a_dark</item>
<item name="customColorBrandB">@color/system_brand_b_dark</item>
<item name="customColorBrandC">@color/system_brand_c_dark</item>
<item name="customColorBrandD">@color/system_brand_d_dark</item>
- <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
- <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+ <item name="customColorClockHour">@color/system_clock_hour_dark</item>
+ <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
+ <item name="customColorClockSecond">@color/system_clock_second_dark</item>
<item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
<item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
- <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
<item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
<item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
- <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+ <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
<item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
+ <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+ <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+ <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
+ <item name="customColorThemeApp">@color/system_theme_app_dark</item>
+ <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
+ <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
+ <item name="customColorWeatherTemp">@color/system_weather_temp_dark</item>
+ <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault} with no action bar and no status bar and
@@ -593,73 +644,90 @@
<!-- Toolbar attributes -->
<item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
- <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
- <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
- <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_dark</item>
- <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
- <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_dark</item>
- <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_dark</item>
- <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_dark</item>
- <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_dark</item>
- <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
- <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
- <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
- <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
- <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
- <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
- <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
+ <item name="materialColorBackground">@color/system_background_dark</item>
+ <item name="materialColorControlActivated">@color/system_control_activated_dark</item>
+ <item name="materialColorControlHighlight">@color/system_control_highlight_dark</item>
+ <item name="materialColorControlNormal">@color/system_control_normal_dark</item>
+ <item name="materialColorError">@color/system_error_dark</item>
<item name="materialColorErrorContainer">@color/system_error_container_dark</item>
- <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
- <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
- <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
- <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
- <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
- <item name="materialColorPrimaryContainer">@color/system_primary_container_dark</item>
+ <item name="materialColorInverseOnSurface">@color/system_inverse_on_surface_dark</item>
+ <item name="materialColorInversePrimary">@color/system_inverse_primary_dark</item>
+ <item name="materialColorInverseSurface">@color/system_inverse_surface_dark</item>
<item name="materialColorOnBackground">@color/system_on_background_dark</item>
- <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
- <item name="materialColorOnSecondary">@color/system_on_secondary_dark</item>
- <item name="materialColorOnTertiary">@color/system_on_tertiary_dark</item>
- <item name="materialColorSurfaceDim">@color/system_surface_dim_dark</item>
- <item name="materialColorSurfaceBright">@color/system_surface_bright_dark</item>
<item name="materialColorOnError">@color/system_on_error_dark</item>
- <item name="materialColorSurface">@color/system_surface_dark</item>
- <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_dark</item>
- <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_dark</item>
+ <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
+ <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
+ <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_dark</item>
+ <item name="materialColorOnSecondary">@color/system_on_secondary_dark</item>
+ <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_dark</item>
+ <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
<item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_dark</item>
+ <item name="materialColorOnTertiary">@color/system_on_tertiary_dark</item>
+ <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_dark</item>
<item name="materialColorOutline">@color/system_outline_dark</item>
<item name="materialColorOutlineVariant">@color/system_outline_variant_dark</item>
- <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
- <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
+ <item name="materialColorPaletteKeyColorNeutral">@color/system_palette_key_color_neutral_dark</item>
+ <item name="materialColorPaletteKeyColorNeutralVariant">@color/system_palette_key_color_neutral_variant_dark</item>
+ <item name="materialColorPaletteKeyColorPrimary">@color/system_palette_key_color_primary_dark</item>
+ <item name="materialColorPaletteKeyColorSecondary">@color/system_palette_key_color_secondary_dark</item>
+ <item name="materialColorPaletteKeyColorTertiary">@color/system_palette_key_color_tertiary_dark</item>
<item name="materialColorPrimary">@color/system_primary_dark</item>
+ <item name="materialColorPrimaryContainer">@color/system_primary_container_dark</item>
+ <item name="materialColorScrim">@color/system_scrim_dark</item>
<item name="materialColorSecondary">@color/system_secondary_dark</item>
+ <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
+ <item name="materialColorShadow">@color/system_shadow_dark</item>
+ <item name="materialColorSurface">@color/system_surface_dark</item>
+ <item name="materialColorSurfaceBright">@color/system_surface_bright_dark</item>
+ <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
+ <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_dark</item>
+ <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_dark</item>
+ <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_dark</item>
+ <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_dark</item>
+ <item name="materialColorSurfaceDim">@color/system_surface_dim_dark</item>
+ <item name="materialColorSurfaceTint">@color/system_surface_tint_dark</item>
+ <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
<item name="materialColorTertiary">@color/system_tertiary_dark</item>
- <item name="materialColorError">@color/system_error_dark</item>
-
- <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
- <item name="customColorClockHour">@color/system_clock_hour_dark</item>
- <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
- <item name="customColorClockSecond">@color/system_clock_second_dark</item>
- <item name="customColorThemeApp">@color/system_theme_app_dark</item>
- <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
- <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
- <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
+ <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
+ <item name="materialColorTextHintInverse">@color/system_text_hint_inverse_dark</item>
+ <item name="materialColorTextPrimaryInverse">@color/system_text_primary_inverse_dark</item>
+ <item name="materialColorTextPrimaryInverseDisableOnly">@color/system_text_primary_inverse_disable_only_dark</item>
+ <item name="materialColorTextSecondaryAndTertiaryInverse">@color/system_text_secondary_and_tertiary_inverse_dark</item>
+ <item name="materialColorTextSecondaryAndTertiaryInverseDisabled">@color/system_text_secondary_and_tertiary_inverse_disabled_dark</item>
+ <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
+ <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
+ <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
+ <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
+ <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
+ <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
+ <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
+ <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
+ <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
+ <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
+ <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
+ <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="customColorBrandA">@color/system_brand_a_dark</item>
<item name="customColorBrandB">@color/system_brand_b_dark</item>
<item name="customColorBrandC">@color/system_brand_c_dark</item>
<item name="customColorBrandD">@color/system_brand_d_dark</item>
- <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
- <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+ <item name="customColorClockHour">@color/system_clock_hour_dark</item>
+ <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
+ <item name="customColorClockSecond">@color/system_clock_second_dark</item>
<item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
<item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
- <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
<item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
<item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
- <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+ <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
<item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
+ <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+ <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+ <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
+ <item name="customColorThemeApp">@color/system_theme_app_dark</item>
+ <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
+ <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
+ <item name="customColorWeatherTemp">@color/system_weather_temp_dark</item>
+ <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault} that has no title bar and translucent
@@ -711,73 +779,90 @@
<!-- Toolbar attributes -->
<item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
- <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
- <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
- <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_dark</item>
- <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
- <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_dark</item>
- <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_dark</item>
- <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_dark</item>
- <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_dark</item>
- <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
- <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
- <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
- <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
- <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
- <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
- <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
+ <item name="materialColorBackground">@color/system_background_dark</item>
+ <item name="materialColorControlActivated">@color/system_control_activated_dark</item>
+ <item name="materialColorControlHighlight">@color/system_control_highlight_dark</item>
+ <item name="materialColorControlNormal">@color/system_control_normal_dark</item>
+ <item name="materialColorError">@color/system_error_dark</item>
<item name="materialColorErrorContainer">@color/system_error_container_dark</item>
- <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
- <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
- <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
- <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
- <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
- <item name="materialColorPrimaryContainer">@color/system_primary_container_dark</item>
+ <item name="materialColorInverseOnSurface">@color/system_inverse_on_surface_dark</item>
+ <item name="materialColorInversePrimary">@color/system_inverse_primary_dark</item>
+ <item name="materialColorInverseSurface">@color/system_inverse_surface_dark</item>
<item name="materialColorOnBackground">@color/system_on_background_dark</item>
- <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
- <item name="materialColorOnSecondary">@color/system_on_secondary_dark</item>
- <item name="materialColorOnTertiary">@color/system_on_tertiary_dark</item>
- <item name="materialColorSurfaceDim">@color/system_surface_dim_dark</item>
- <item name="materialColorSurfaceBright">@color/system_surface_bright_dark</item>
<item name="materialColorOnError">@color/system_on_error_dark</item>
- <item name="materialColorSurface">@color/system_surface_dark</item>
- <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_dark</item>
- <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_dark</item>
+ <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
+ <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
+ <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_dark</item>
+ <item name="materialColorOnSecondary">@color/system_on_secondary_dark</item>
+ <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_dark</item>
+ <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
<item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_dark</item>
+ <item name="materialColorOnTertiary">@color/system_on_tertiary_dark</item>
+ <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_dark</item>
<item name="materialColorOutline">@color/system_outline_dark</item>
<item name="materialColorOutlineVariant">@color/system_outline_variant_dark</item>
- <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
- <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
+ <item name="materialColorPaletteKeyColorNeutral">@color/system_palette_key_color_neutral_dark</item>
+ <item name="materialColorPaletteKeyColorNeutralVariant">@color/system_palette_key_color_neutral_variant_dark</item>
+ <item name="materialColorPaletteKeyColorPrimary">@color/system_palette_key_color_primary_dark</item>
+ <item name="materialColorPaletteKeyColorSecondary">@color/system_palette_key_color_secondary_dark</item>
+ <item name="materialColorPaletteKeyColorTertiary">@color/system_palette_key_color_tertiary_dark</item>
<item name="materialColorPrimary">@color/system_primary_dark</item>
+ <item name="materialColorPrimaryContainer">@color/system_primary_container_dark</item>
+ <item name="materialColorScrim">@color/system_scrim_dark</item>
<item name="materialColorSecondary">@color/system_secondary_dark</item>
+ <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
+ <item name="materialColorShadow">@color/system_shadow_dark</item>
+ <item name="materialColorSurface">@color/system_surface_dark</item>
+ <item name="materialColorSurfaceBright">@color/system_surface_bright_dark</item>
+ <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
+ <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_dark</item>
+ <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_dark</item>
+ <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_dark</item>
+ <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_dark</item>
+ <item name="materialColorSurfaceDim">@color/system_surface_dim_dark</item>
+ <item name="materialColorSurfaceTint">@color/system_surface_tint_dark</item>
+ <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
<item name="materialColorTertiary">@color/system_tertiary_dark</item>
- <item name="materialColorError">@color/system_error_dark</item>
-
- <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
- <item name="customColorClockHour">@color/system_clock_hour_dark</item>
- <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
- <item name="customColorClockSecond">@color/system_clock_second_dark</item>
- <item name="customColorThemeApp">@color/system_theme_app_dark</item>
- <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
- <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
- <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
+ <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
+ <item name="materialColorTextHintInverse">@color/system_text_hint_inverse_dark</item>
+ <item name="materialColorTextPrimaryInverse">@color/system_text_primary_inverse_dark</item>
+ <item name="materialColorTextPrimaryInverseDisableOnly">@color/system_text_primary_inverse_disable_only_dark</item>
+ <item name="materialColorTextSecondaryAndTertiaryInverse">@color/system_text_secondary_and_tertiary_inverse_dark</item>
+ <item name="materialColorTextSecondaryAndTertiaryInverseDisabled">@color/system_text_secondary_and_tertiary_inverse_disabled_dark</item>
+ <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
+ <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
+ <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
+ <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
+ <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
+ <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
+ <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
+ <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
+ <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
+ <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
+ <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
+ <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="customColorBrandA">@color/system_brand_a_dark</item>
<item name="customColorBrandB">@color/system_brand_b_dark</item>
<item name="customColorBrandC">@color/system_brand_c_dark</item>
<item name="customColorBrandD">@color/system_brand_d_dark</item>
- <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
- <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+ <item name="customColorClockHour">@color/system_clock_hour_dark</item>
+ <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
+ <item name="customColorClockSecond">@color/system_clock_second_dark</item>
<item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
<item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
- <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
<item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
<item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
- <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+ <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
<item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
+ <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+ <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+ <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
+ <item name="customColorThemeApp">@color/system_theme_app_dark</item>
+ <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
+ <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
+ <item name="customColorWeatherTemp">@color/system_weather_temp_dark</item>
+ <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
</style>
<!-- DeviceDefault theme for dialog windows and activities. This changes the window to be
@@ -837,73 +922,90 @@
<!-- Toolbar attributes -->
<item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
- <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
- <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
- <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_dark</item>
- <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
- <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_dark</item>
- <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_dark</item>
- <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_dark</item>
- <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_dark</item>
- <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
- <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
- <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
- <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
- <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
- <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
- <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
+ <item name="materialColorBackground">@color/system_background_dark</item>
+ <item name="materialColorControlActivated">@color/system_control_activated_dark</item>
+ <item name="materialColorControlHighlight">@color/system_control_highlight_dark</item>
+ <item name="materialColorControlNormal">@color/system_control_normal_dark</item>
+ <item name="materialColorError">@color/system_error_dark</item>
<item name="materialColorErrorContainer">@color/system_error_container_dark</item>
- <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
- <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
- <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
- <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
- <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
- <item name="materialColorPrimaryContainer">@color/system_primary_container_dark</item>
+ <item name="materialColorInverseOnSurface">@color/system_inverse_on_surface_dark</item>
+ <item name="materialColorInversePrimary">@color/system_inverse_primary_dark</item>
+ <item name="materialColorInverseSurface">@color/system_inverse_surface_dark</item>
<item name="materialColorOnBackground">@color/system_on_background_dark</item>
- <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
- <item name="materialColorOnSecondary">@color/system_on_secondary_dark</item>
- <item name="materialColorOnTertiary">@color/system_on_tertiary_dark</item>
- <item name="materialColorSurfaceDim">@color/system_surface_dim_dark</item>
- <item name="materialColorSurfaceBright">@color/system_surface_bright_dark</item>
<item name="materialColorOnError">@color/system_on_error_dark</item>
- <item name="materialColorSurface">@color/system_surface_dark</item>
- <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_dark</item>
- <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_dark</item>
+ <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
+ <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
+ <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_dark</item>
+ <item name="materialColorOnSecondary">@color/system_on_secondary_dark</item>
+ <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_dark</item>
+ <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
<item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_dark</item>
+ <item name="materialColorOnTertiary">@color/system_on_tertiary_dark</item>
+ <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_dark</item>
<item name="materialColorOutline">@color/system_outline_dark</item>
<item name="materialColorOutlineVariant">@color/system_outline_variant_dark</item>
- <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
- <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
+ <item name="materialColorPaletteKeyColorNeutral">@color/system_palette_key_color_neutral_dark</item>
+ <item name="materialColorPaletteKeyColorNeutralVariant">@color/system_palette_key_color_neutral_variant_dark</item>
+ <item name="materialColorPaletteKeyColorPrimary">@color/system_palette_key_color_primary_dark</item>
+ <item name="materialColorPaletteKeyColorSecondary">@color/system_palette_key_color_secondary_dark</item>
+ <item name="materialColorPaletteKeyColorTertiary">@color/system_palette_key_color_tertiary_dark</item>
<item name="materialColorPrimary">@color/system_primary_dark</item>
+ <item name="materialColorPrimaryContainer">@color/system_primary_container_dark</item>
+ <item name="materialColorScrim">@color/system_scrim_dark</item>
<item name="materialColorSecondary">@color/system_secondary_dark</item>
+ <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
+ <item name="materialColorShadow">@color/system_shadow_dark</item>
+ <item name="materialColorSurface">@color/system_surface_dark</item>
+ <item name="materialColorSurfaceBright">@color/system_surface_bright_dark</item>
+ <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
+ <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_dark</item>
+ <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_dark</item>
+ <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_dark</item>
+ <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_dark</item>
+ <item name="materialColorSurfaceDim">@color/system_surface_dim_dark</item>
+ <item name="materialColorSurfaceTint">@color/system_surface_tint_dark</item>
+ <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
<item name="materialColorTertiary">@color/system_tertiary_dark</item>
- <item name="materialColorError">@color/system_error_dark</item>
-
- <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
- <item name="customColorClockHour">@color/system_clock_hour_dark</item>
- <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
- <item name="customColorClockSecond">@color/system_clock_second_dark</item>
- <item name="customColorThemeApp">@color/system_theme_app_dark</item>
- <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
- <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
- <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
+ <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
+ <item name="materialColorTextHintInverse">@color/system_text_hint_inverse_dark</item>
+ <item name="materialColorTextPrimaryInverse">@color/system_text_primary_inverse_dark</item>
+ <item name="materialColorTextPrimaryInverseDisableOnly">@color/system_text_primary_inverse_disable_only_dark</item>
+ <item name="materialColorTextSecondaryAndTertiaryInverse">@color/system_text_secondary_and_tertiary_inverse_dark</item>
+ <item name="materialColorTextSecondaryAndTertiaryInverseDisabled">@color/system_text_secondary_and_tertiary_inverse_disabled_dark</item>
+ <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
+ <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
+ <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
+ <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
+ <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
+ <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
+ <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
+ <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
+ <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
+ <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
+ <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
+ <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="customColorBrandA">@color/system_brand_a_dark</item>
<item name="customColorBrandB">@color/system_brand_b_dark</item>
<item name="customColorBrandC">@color/system_brand_c_dark</item>
<item name="customColorBrandD">@color/system_brand_d_dark</item>
- <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
- <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+ <item name="customColorClockHour">@color/system_clock_hour_dark</item>
+ <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
+ <item name="customColorClockSecond">@color/system_clock_second_dark</item>
<item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
<item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
- <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
<item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
<item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
- <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+ <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
<item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
+ <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+ <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+ <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
+ <item name="customColorThemeApp">@color/system_theme_app_dark</item>
+ <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
+ <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
+ <item name="customColorWeatherTemp">@color/system_weather_temp_dark</item>
+ <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault_Dialog} that has a nice minimum width for a
@@ -954,73 +1056,90 @@
<!-- Toolbar attributes -->
<item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
- <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
- <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
- <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_dark</item>
- <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
- <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_dark</item>
- <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_dark</item>
- <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_dark</item>
- <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_dark</item>
- <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
- <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
- <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
- <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
- <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
- <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
- <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
+ <item name="materialColorBackground">@color/system_background_dark</item>
+ <item name="materialColorControlActivated">@color/system_control_activated_dark</item>
+ <item name="materialColorControlHighlight">@color/system_control_highlight_dark</item>
+ <item name="materialColorControlNormal">@color/system_control_normal_dark</item>
+ <item name="materialColorError">@color/system_error_dark</item>
<item name="materialColorErrorContainer">@color/system_error_container_dark</item>
- <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
- <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
- <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
- <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
- <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
- <item name="materialColorPrimaryContainer">@color/system_primary_container_dark</item>
+ <item name="materialColorInverseOnSurface">@color/system_inverse_on_surface_dark</item>
+ <item name="materialColorInversePrimary">@color/system_inverse_primary_dark</item>
+ <item name="materialColorInverseSurface">@color/system_inverse_surface_dark</item>
<item name="materialColorOnBackground">@color/system_on_background_dark</item>
- <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
- <item name="materialColorOnSecondary">@color/system_on_secondary_dark</item>
- <item name="materialColorOnTertiary">@color/system_on_tertiary_dark</item>
- <item name="materialColorSurfaceDim">@color/system_surface_dim_dark</item>
- <item name="materialColorSurfaceBright">@color/system_surface_bright_dark</item>
<item name="materialColorOnError">@color/system_on_error_dark</item>
- <item name="materialColorSurface">@color/system_surface_dark</item>
- <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_dark</item>
- <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_dark</item>
+ <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
+ <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
+ <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_dark</item>
+ <item name="materialColorOnSecondary">@color/system_on_secondary_dark</item>
+ <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_dark</item>
+ <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
<item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_dark</item>
+ <item name="materialColorOnTertiary">@color/system_on_tertiary_dark</item>
+ <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_dark</item>
<item name="materialColorOutline">@color/system_outline_dark</item>
<item name="materialColorOutlineVariant">@color/system_outline_variant_dark</item>
- <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
- <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
+ <item name="materialColorPaletteKeyColorNeutral">@color/system_palette_key_color_neutral_dark</item>
+ <item name="materialColorPaletteKeyColorNeutralVariant">@color/system_palette_key_color_neutral_variant_dark</item>
+ <item name="materialColorPaletteKeyColorPrimary">@color/system_palette_key_color_primary_dark</item>
+ <item name="materialColorPaletteKeyColorSecondary">@color/system_palette_key_color_secondary_dark</item>
+ <item name="materialColorPaletteKeyColorTertiary">@color/system_palette_key_color_tertiary_dark</item>
<item name="materialColorPrimary">@color/system_primary_dark</item>
+ <item name="materialColorPrimaryContainer">@color/system_primary_container_dark</item>
+ <item name="materialColorScrim">@color/system_scrim_dark</item>
<item name="materialColorSecondary">@color/system_secondary_dark</item>
+ <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
+ <item name="materialColorShadow">@color/system_shadow_dark</item>
+ <item name="materialColorSurface">@color/system_surface_dark</item>
+ <item name="materialColorSurfaceBright">@color/system_surface_bright_dark</item>
+ <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
+ <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_dark</item>
+ <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_dark</item>
+ <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_dark</item>
+ <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_dark</item>
+ <item name="materialColorSurfaceDim">@color/system_surface_dim_dark</item>
+ <item name="materialColorSurfaceTint">@color/system_surface_tint_dark</item>
+ <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
<item name="materialColorTertiary">@color/system_tertiary_dark</item>
- <item name="materialColorError">@color/system_error_dark</item>
-
- <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
- <item name="customColorClockHour">@color/system_clock_hour_dark</item>
- <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
- <item name="customColorClockSecond">@color/system_clock_second_dark</item>
- <item name="customColorThemeApp">@color/system_theme_app_dark</item>
- <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
- <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
- <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
+ <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
+ <item name="materialColorTextHintInverse">@color/system_text_hint_inverse_dark</item>
+ <item name="materialColorTextPrimaryInverse">@color/system_text_primary_inverse_dark</item>
+ <item name="materialColorTextPrimaryInverseDisableOnly">@color/system_text_primary_inverse_disable_only_dark</item>
+ <item name="materialColorTextSecondaryAndTertiaryInverse">@color/system_text_secondary_and_tertiary_inverse_dark</item>
+ <item name="materialColorTextSecondaryAndTertiaryInverseDisabled">@color/system_text_secondary_and_tertiary_inverse_disabled_dark</item>
+ <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
+ <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
+ <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
+ <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
+ <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
+ <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
+ <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
+ <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
+ <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
+ <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
+ <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
+ <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="customColorBrandA">@color/system_brand_a_dark</item>
<item name="customColorBrandB">@color/system_brand_b_dark</item>
<item name="customColorBrandC">@color/system_brand_c_dark</item>
<item name="customColorBrandD">@color/system_brand_d_dark</item>
- <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
- <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+ <item name="customColorClockHour">@color/system_clock_hour_dark</item>
+ <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
+ <item name="customColorClockSecond">@color/system_clock_second_dark</item>
<item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
<item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
- <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
<item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
<item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
- <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+ <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
<item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
+ <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+ <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+ <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
+ <item name="customColorThemeApp">@color/system_theme_app_dark</item>
+ <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
+ <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
+ <item name="customColorWeatherTemp">@color/system_weather_temp_dark</item>
+ <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault_Dialog} without an action bar -->
@@ -1070,73 +1189,90 @@
<!-- Toolbar attributes -->
<item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
- <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
- <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
- <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_dark</item>
- <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
- <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_dark</item>
- <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_dark</item>
- <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_dark</item>
- <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_dark</item>
- <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
- <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
- <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
- <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
- <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
- <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
- <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
+ <item name="materialColorBackground">@color/system_background_dark</item>
+ <item name="materialColorControlActivated">@color/system_control_activated_dark</item>
+ <item name="materialColorControlHighlight">@color/system_control_highlight_dark</item>
+ <item name="materialColorControlNormal">@color/system_control_normal_dark</item>
+ <item name="materialColorError">@color/system_error_dark</item>
<item name="materialColorErrorContainer">@color/system_error_container_dark</item>
- <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
- <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
- <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
- <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
- <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
- <item name="materialColorPrimaryContainer">@color/system_primary_container_dark</item>
+ <item name="materialColorInverseOnSurface">@color/system_inverse_on_surface_dark</item>
+ <item name="materialColorInversePrimary">@color/system_inverse_primary_dark</item>
+ <item name="materialColorInverseSurface">@color/system_inverse_surface_dark</item>
<item name="materialColorOnBackground">@color/system_on_background_dark</item>
- <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
- <item name="materialColorOnSecondary">@color/system_on_secondary_dark</item>
- <item name="materialColorOnTertiary">@color/system_on_tertiary_dark</item>
- <item name="materialColorSurfaceDim">@color/system_surface_dim_dark</item>
- <item name="materialColorSurfaceBright">@color/system_surface_bright_dark</item>
<item name="materialColorOnError">@color/system_on_error_dark</item>
- <item name="materialColorSurface">@color/system_surface_dark</item>
- <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_dark</item>
- <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_dark</item>
+ <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
+ <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
+ <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_dark</item>
+ <item name="materialColorOnSecondary">@color/system_on_secondary_dark</item>
+ <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_dark</item>
+ <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
<item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_dark</item>
+ <item name="materialColorOnTertiary">@color/system_on_tertiary_dark</item>
+ <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_dark</item>
<item name="materialColorOutline">@color/system_outline_dark</item>
<item name="materialColorOutlineVariant">@color/system_outline_variant_dark</item>
- <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
- <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
+ <item name="materialColorPaletteKeyColorNeutral">@color/system_palette_key_color_neutral_dark</item>
+ <item name="materialColorPaletteKeyColorNeutralVariant">@color/system_palette_key_color_neutral_variant_dark</item>
+ <item name="materialColorPaletteKeyColorPrimary">@color/system_palette_key_color_primary_dark</item>
+ <item name="materialColorPaletteKeyColorSecondary">@color/system_palette_key_color_secondary_dark</item>
+ <item name="materialColorPaletteKeyColorTertiary">@color/system_palette_key_color_tertiary_dark</item>
<item name="materialColorPrimary">@color/system_primary_dark</item>
+ <item name="materialColorPrimaryContainer">@color/system_primary_container_dark</item>
+ <item name="materialColorScrim">@color/system_scrim_dark</item>
<item name="materialColorSecondary">@color/system_secondary_dark</item>
+ <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
+ <item name="materialColorShadow">@color/system_shadow_dark</item>
+ <item name="materialColorSurface">@color/system_surface_dark</item>
+ <item name="materialColorSurfaceBright">@color/system_surface_bright_dark</item>
+ <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
+ <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_dark</item>
+ <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_dark</item>
+ <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_dark</item>
+ <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_dark</item>
+ <item name="materialColorSurfaceDim">@color/system_surface_dim_dark</item>
+ <item name="materialColorSurfaceTint">@color/system_surface_tint_dark</item>
+ <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
<item name="materialColorTertiary">@color/system_tertiary_dark</item>
- <item name="materialColorError">@color/system_error_dark</item>
-
- <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
- <item name="customColorClockHour">@color/system_clock_hour_dark</item>
- <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
- <item name="customColorClockSecond">@color/system_clock_second_dark</item>
- <item name="customColorThemeApp">@color/system_theme_app_dark</item>
- <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
- <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
- <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
+ <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
+ <item name="materialColorTextHintInverse">@color/system_text_hint_inverse_dark</item>
+ <item name="materialColorTextPrimaryInverse">@color/system_text_primary_inverse_dark</item>
+ <item name="materialColorTextPrimaryInverseDisableOnly">@color/system_text_primary_inverse_disable_only_dark</item>
+ <item name="materialColorTextSecondaryAndTertiaryInverse">@color/system_text_secondary_and_tertiary_inverse_dark</item>
+ <item name="materialColorTextSecondaryAndTertiaryInverseDisabled">@color/system_text_secondary_and_tertiary_inverse_disabled_dark</item>
+ <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
+ <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
+ <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
+ <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
+ <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
+ <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
+ <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
+ <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
+ <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
+ <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
+ <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
+ <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="customColorBrandA">@color/system_brand_a_dark</item>
<item name="customColorBrandB">@color/system_brand_b_dark</item>
<item name="customColorBrandC">@color/system_brand_c_dark</item>
<item name="customColorBrandD">@color/system_brand_d_dark</item>
- <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
- <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+ <item name="customColorClockHour">@color/system_clock_hour_dark</item>
+ <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
+ <item name="customColorClockSecond">@color/system_clock_second_dark</item>
<item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
<item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
- <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
<item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
<item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
- <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+ <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
<item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
+ <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+ <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+ <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
+ <item name="customColorThemeApp">@color/system_theme_app_dark</item>
+ <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
+ <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
+ <item name="customColorWeatherTemp">@color/system_weather_temp_dark</item>
+ <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault_Dialog_NoActionBar} that has a nice minimum width
@@ -1187,73 +1323,90 @@
<!-- Toolbar attributes -->
<item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
- <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
- <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
- <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_dark</item>
- <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
- <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_dark</item>
- <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_dark</item>
- <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_dark</item>
- <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_dark</item>
- <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
- <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
- <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
- <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
- <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
- <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
- <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
+ <item name="materialColorBackground">@color/system_background_dark</item>
+ <item name="materialColorControlActivated">@color/system_control_activated_dark</item>
+ <item name="materialColorControlHighlight">@color/system_control_highlight_dark</item>
+ <item name="materialColorControlNormal">@color/system_control_normal_dark</item>
+ <item name="materialColorError">@color/system_error_dark</item>
<item name="materialColorErrorContainer">@color/system_error_container_dark</item>
- <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
- <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
- <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
- <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
- <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
- <item name="materialColorPrimaryContainer">@color/system_primary_container_dark</item>
+ <item name="materialColorInverseOnSurface">@color/system_inverse_on_surface_dark</item>
+ <item name="materialColorInversePrimary">@color/system_inverse_primary_dark</item>
+ <item name="materialColorInverseSurface">@color/system_inverse_surface_dark</item>
<item name="materialColorOnBackground">@color/system_on_background_dark</item>
- <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
- <item name="materialColorOnSecondary">@color/system_on_secondary_dark</item>
- <item name="materialColorOnTertiary">@color/system_on_tertiary_dark</item>
- <item name="materialColorSurfaceDim">@color/system_surface_dim_dark</item>
- <item name="materialColorSurfaceBright">@color/system_surface_bright_dark</item>
<item name="materialColorOnError">@color/system_on_error_dark</item>
- <item name="materialColorSurface">@color/system_surface_dark</item>
- <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_dark</item>
- <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_dark</item>
+ <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
+ <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
+ <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_dark</item>
+ <item name="materialColorOnSecondary">@color/system_on_secondary_dark</item>
+ <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_dark</item>
+ <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
<item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_dark</item>
+ <item name="materialColorOnTertiary">@color/system_on_tertiary_dark</item>
+ <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_dark</item>
<item name="materialColorOutline">@color/system_outline_dark</item>
<item name="materialColorOutlineVariant">@color/system_outline_variant_dark</item>
- <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
- <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
+ <item name="materialColorPaletteKeyColorNeutral">@color/system_palette_key_color_neutral_dark</item>
+ <item name="materialColorPaletteKeyColorNeutralVariant">@color/system_palette_key_color_neutral_variant_dark</item>
+ <item name="materialColorPaletteKeyColorPrimary">@color/system_palette_key_color_primary_dark</item>
+ <item name="materialColorPaletteKeyColorSecondary">@color/system_palette_key_color_secondary_dark</item>
+ <item name="materialColorPaletteKeyColorTertiary">@color/system_palette_key_color_tertiary_dark</item>
<item name="materialColorPrimary">@color/system_primary_dark</item>
+ <item name="materialColorPrimaryContainer">@color/system_primary_container_dark</item>
+ <item name="materialColorScrim">@color/system_scrim_dark</item>
<item name="materialColorSecondary">@color/system_secondary_dark</item>
+ <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
+ <item name="materialColorShadow">@color/system_shadow_dark</item>
+ <item name="materialColorSurface">@color/system_surface_dark</item>
+ <item name="materialColorSurfaceBright">@color/system_surface_bright_dark</item>
+ <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
+ <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_dark</item>
+ <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_dark</item>
+ <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_dark</item>
+ <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_dark</item>
+ <item name="materialColorSurfaceDim">@color/system_surface_dim_dark</item>
+ <item name="materialColorSurfaceTint">@color/system_surface_tint_dark</item>
+ <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
<item name="materialColorTertiary">@color/system_tertiary_dark</item>
- <item name="materialColorError">@color/system_error_dark</item>
-
- <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
- <item name="customColorClockHour">@color/system_clock_hour_dark</item>
- <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
- <item name="customColorClockSecond">@color/system_clock_second_dark</item>
- <item name="customColorThemeApp">@color/system_theme_app_dark</item>
- <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
- <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
- <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
+ <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
+ <item name="materialColorTextHintInverse">@color/system_text_hint_inverse_dark</item>
+ <item name="materialColorTextPrimaryInverse">@color/system_text_primary_inverse_dark</item>
+ <item name="materialColorTextPrimaryInverseDisableOnly">@color/system_text_primary_inverse_disable_only_dark</item>
+ <item name="materialColorTextSecondaryAndTertiaryInverse">@color/system_text_secondary_and_tertiary_inverse_dark</item>
+ <item name="materialColorTextSecondaryAndTertiaryInverseDisabled">@color/system_text_secondary_and_tertiary_inverse_disabled_dark</item>
+ <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
+ <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
+ <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
+ <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
+ <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
+ <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
+ <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
+ <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
+ <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
+ <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
+ <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
+ <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="customColorBrandA">@color/system_brand_a_dark</item>
<item name="customColorBrandB">@color/system_brand_b_dark</item>
<item name="customColorBrandC">@color/system_brand_c_dark</item>
<item name="customColorBrandD">@color/system_brand_d_dark</item>
- <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
- <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+ <item name="customColorClockHour">@color/system_clock_hour_dark</item>
+ <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
+ <item name="customColorClockSecond">@color/system_clock_second_dark</item>
<item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
<item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
- <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
<item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
<item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
- <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+ <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
<item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
+ <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+ <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+ <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
+ <item name="customColorThemeApp">@color/system_theme_app_dark</item>
+ <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
+ <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
+ <item name="customColorWeatherTemp">@color/system_weather_temp_dark</item>
+ <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
</style>
<!-- Variant of Theme.DeviceDefault.Dialog that has a fixed size. -->
@@ -1320,73 +1473,90 @@
<!-- Toolbar attributes -->
<item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
- <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
- <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
- <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_dark</item>
- <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
- <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_dark</item>
- <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_dark</item>
- <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_dark</item>
- <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_dark</item>
- <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
- <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
- <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
- <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
- <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
- <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
- <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
+ <item name="materialColorBackground">@color/system_background_dark</item>
+ <item name="materialColorControlActivated">@color/system_control_activated_dark</item>
+ <item name="materialColorControlHighlight">@color/system_control_highlight_dark</item>
+ <item name="materialColorControlNormal">@color/system_control_normal_dark</item>
+ <item name="materialColorError">@color/system_error_dark</item>
<item name="materialColorErrorContainer">@color/system_error_container_dark</item>
- <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
- <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
- <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
- <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
- <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
- <item name="materialColorPrimaryContainer">@color/system_primary_container_dark</item>
+ <item name="materialColorInverseOnSurface">@color/system_inverse_on_surface_dark</item>
+ <item name="materialColorInversePrimary">@color/system_inverse_primary_dark</item>
+ <item name="materialColorInverseSurface">@color/system_inverse_surface_dark</item>
<item name="materialColorOnBackground">@color/system_on_background_dark</item>
- <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
- <item name="materialColorOnSecondary">@color/system_on_secondary_dark</item>
- <item name="materialColorOnTertiary">@color/system_on_tertiary_dark</item>
- <item name="materialColorSurfaceDim">@color/system_surface_dim_dark</item>
- <item name="materialColorSurfaceBright">@color/system_surface_bright_dark</item>
<item name="materialColorOnError">@color/system_on_error_dark</item>
- <item name="materialColorSurface">@color/system_surface_dark</item>
- <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_dark</item>
- <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_dark</item>
+ <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
+ <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
+ <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_dark</item>
+ <item name="materialColorOnSecondary">@color/system_on_secondary_dark</item>
+ <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_dark</item>
+ <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
<item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_dark</item>
+ <item name="materialColorOnTertiary">@color/system_on_tertiary_dark</item>
+ <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_dark</item>
<item name="materialColorOutline">@color/system_outline_dark</item>
<item name="materialColorOutlineVariant">@color/system_outline_variant_dark</item>
- <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
- <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
+ <item name="materialColorPaletteKeyColorNeutral">@color/system_palette_key_color_neutral_dark</item>
+ <item name="materialColorPaletteKeyColorNeutralVariant">@color/system_palette_key_color_neutral_variant_dark</item>
+ <item name="materialColorPaletteKeyColorPrimary">@color/system_palette_key_color_primary_dark</item>
+ <item name="materialColorPaletteKeyColorSecondary">@color/system_palette_key_color_secondary_dark</item>
+ <item name="materialColorPaletteKeyColorTertiary">@color/system_palette_key_color_tertiary_dark</item>
<item name="materialColorPrimary">@color/system_primary_dark</item>
+ <item name="materialColorPrimaryContainer">@color/system_primary_container_dark</item>
+ <item name="materialColorScrim">@color/system_scrim_dark</item>
<item name="materialColorSecondary">@color/system_secondary_dark</item>
+ <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
+ <item name="materialColorShadow">@color/system_shadow_dark</item>
+ <item name="materialColorSurface">@color/system_surface_dark</item>
+ <item name="materialColorSurfaceBright">@color/system_surface_bright_dark</item>
+ <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
+ <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_dark</item>
+ <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_dark</item>
+ <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_dark</item>
+ <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_dark</item>
+ <item name="materialColorSurfaceDim">@color/system_surface_dim_dark</item>
+ <item name="materialColorSurfaceTint">@color/system_surface_tint_dark</item>
+ <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
<item name="materialColorTertiary">@color/system_tertiary_dark</item>
- <item name="materialColorError">@color/system_error_dark</item>
-
- <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
- <item name="customColorClockHour">@color/system_clock_hour_dark</item>
- <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
- <item name="customColorClockSecond">@color/system_clock_second_dark</item>
- <item name="customColorThemeApp">@color/system_theme_app_dark</item>
- <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
- <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
- <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
+ <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
+ <item name="materialColorTextHintInverse">@color/system_text_hint_inverse_dark</item>
+ <item name="materialColorTextPrimaryInverse">@color/system_text_primary_inverse_dark</item>
+ <item name="materialColorTextPrimaryInverseDisableOnly">@color/system_text_primary_inverse_disable_only_dark</item>
+ <item name="materialColorTextSecondaryAndTertiaryInverse">@color/system_text_secondary_and_tertiary_inverse_dark</item>
+ <item name="materialColorTextSecondaryAndTertiaryInverseDisabled">@color/system_text_secondary_and_tertiary_inverse_disabled_dark</item>
+ <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
+ <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
+ <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
+ <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
+ <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
+ <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
+ <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
+ <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
+ <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
+ <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
+ <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
+ <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="customColorBrandA">@color/system_brand_a_dark</item>
<item name="customColorBrandB">@color/system_brand_b_dark</item>
<item name="customColorBrandC">@color/system_brand_c_dark</item>
<item name="customColorBrandD">@color/system_brand_d_dark</item>
- <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
- <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+ <item name="customColorClockHour">@color/system_clock_hour_dark</item>
+ <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
+ <item name="customColorClockSecond">@color/system_clock_second_dark</item>
<item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
<item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
- <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
<item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
<item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
- <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+ <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
<item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
+ <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+ <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+ <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
+ <item name="customColorThemeApp">@color/system_theme_app_dark</item>
+ <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
+ <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
+ <item name="customColorWeatherTemp">@color/system_weather_temp_dark</item>
+ <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
</style>
<!-- DeviceDefault theme for a window without an action bar that will be displayed either
@@ -1438,73 +1608,90 @@
<!-- Toolbar attributes -->
<item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
- <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
- <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
- <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_dark</item>
- <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
- <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_dark</item>
- <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_dark</item>
- <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_dark</item>
- <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_dark</item>
- <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
- <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
- <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
- <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
- <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
- <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
- <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
+ <item name="materialColorBackground">@color/system_background_dark</item>
+ <item name="materialColorControlActivated">@color/system_control_activated_dark</item>
+ <item name="materialColorControlHighlight">@color/system_control_highlight_dark</item>
+ <item name="materialColorControlNormal">@color/system_control_normal_dark</item>
+ <item name="materialColorError">@color/system_error_dark</item>
<item name="materialColorErrorContainer">@color/system_error_container_dark</item>
- <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
- <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
- <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
- <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
- <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
- <item name="materialColorPrimaryContainer">@color/system_primary_container_dark</item>
+ <item name="materialColorInverseOnSurface">@color/system_inverse_on_surface_dark</item>
+ <item name="materialColorInversePrimary">@color/system_inverse_primary_dark</item>
+ <item name="materialColorInverseSurface">@color/system_inverse_surface_dark</item>
<item name="materialColorOnBackground">@color/system_on_background_dark</item>
- <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
- <item name="materialColorOnSecondary">@color/system_on_secondary_dark</item>
- <item name="materialColorOnTertiary">@color/system_on_tertiary_dark</item>
- <item name="materialColorSurfaceDim">@color/system_surface_dim_dark</item>
- <item name="materialColorSurfaceBright">@color/system_surface_bright_dark</item>
<item name="materialColorOnError">@color/system_on_error_dark</item>
- <item name="materialColorSurface">@color/system_surface_dark</item>
- <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_dark</item>
- <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_dark</item>
+ <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
+ <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
+ <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_dark</item>
+ <item name="materialColorOnSecondary">@color/system_on_secondary_dark</item>
+ <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_dark</item>
+ <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
<item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_dark</item>
+ <item name="materialColorOnTertiary">@color/system_on_tertiary_dark</item>
+ <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_dark</item>
<item name="materialColorOutline">@color/system_outline_dark</item>
<item name="materialColorOutlineVariant">@color/system_outline_variant_dark</item>
- <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
- <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
+ <item name="materialColorPaletteKeyColorNeutral">@color/system_palette_key_color_neutral_dark</item>
+ <item name="materialColorPaletteKeyColorNeutralVariant">@color/system_palette_key_color_neutral_variant_dark</item>
+ <item name="materialColorPaletteKeyColorPrimary">@color/system_palette_key_color_primary_dark</item>
+ <item name="materialColorPaletteKeyColorSecondary">@color/system_palette_key_color_secondary_dark</item>
+ <item name="materialColorPaletteKeyColorTertiary">@color/system_palette_key_color_tertiary_dark</item>
<item name="materialColorPrimary">@color/system_primary_dark</item>
+ <item name="materialColorPrimaryContainer">@color/system_primary_container_dark</item>
+ <item name="materialColorScrim">@color/system_scrim_dark</item>
<item name="materialColorSecondary">@color/system_secondary_dark</item>
+ <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
+ <item name="materialColorShadow">@color/system_shadow_dark</item>
+ <item name="materialColorSurface">@color/system_surface_dark</item>
+ <item name="materialColorSurfaceBright">@color/system_surface_bright_dark</item>
+ <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
+ <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_dark</item>
+ <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_dark</item>
+ <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_dark</item>
+ <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_dark</item>
+ <item name="materialColorSurfaceDim">@color/system_surface_dim_dark</item>
+ <item name="materialColorSurfaceTint">@color/system_surface_tint_dark</item>
+ <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
<item name="materialColorTertiary">@color/system_tertiary_dark</item>
- <item name="materialColorError">@color/system_error_dark</item>
-
- <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
- <item name="customColorClockHour">@color/system_clock_hour_dark</item>
- <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
- <item name="customColorClockSecond">@color/system_clock_second_dark</item>
- <item name="customColorThemeApp">@color/system_theme_app_dark</item>
- <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
- <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
- <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
+ <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
+ <item name="materialColorTextHintInverse">@color/system_text_hint_inverse_dark</item>
+ <item name="materialColorTextPrimaryInverse">@color/system_text_primary_inverse_dark</item>
+ <item name="materialColorTextPrimaryInverseDisableOnly">@color/system_text_primary_inverse_disable_only_dark</item>
+ <item name="materialColorTextSecondaryAndTertiaryInverse">@color/system_text_secondary_and_tertiary_inverse_dark</item>
+ <item name="materialColorTextSecondaryAndTertiaryInverseDisabled">@color/system_text_secondary_and_tertiary_inverse_disabled_dark</item>
+ <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
+ <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
+ <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
+ <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
+ <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
+ <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
+ <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
+ <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
+ <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
+ <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
+ <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
+ <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="customColorBrandA">@color/system_brand_a_dark</item>
<item name="customColorBrandB">@color/system_brand_b_dark</item>
<item name="customColorBrandC">@color/system_brand_c_dark</item>
<item name="customColorBrandD">@color/system_brand_d_dark</item>
- <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
- <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+ <item name="customColorClockHour">@color/system_clock_hour_dark</item>
+ <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
+ <item name="customColorClockSecond">@color/system_clock_second_dark</item>
<item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
<item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
- <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
<item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
<item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
- <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+ <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
<item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
+ <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+ <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+ <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
+ <item name="customColorThemeApp">@color/system_theme_app_dark</item>
+ <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
+ <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
+ <item name="customColorWeatherTemp">@color/system_weather_temp_dark</item>
+ <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
</style>
<!-- DeviceDefault theme for a presentation window on a secondary display. -->
@@ -1554,73 +1741,90 @@
<!-- Toolbar attributes -->
<item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
- <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
- <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
- <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_dark</item>
- <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
- <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_dark</item>
- <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_dark</item>
- <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_dark</item>
- <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_dark</item>
- <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
- <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
- <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
- <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
- <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
- <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
- <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
+ <item name="materialColorBackground">@color/system_background_dark</item>
+ <item name="materialColorControlActivated">@color/system_control_activated_dark</item>
+ <item name="materialColorControlHighlight">@color/system_control_highlight_dark</item>
+ <item name="materialColorControlNormal">@color/system_control_normal_dark</item>
+ <item name="materialColorError">@color/system_error_dark</item>
<item name="materialColorErrorContainer">@color/system_error_container_dark</item>
- <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
- <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
- <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
- <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
- <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
- <item name="materialColorPrimaryContainer">@color/system_primary_container_dark</item>
+ <item name="materialColorInverseOnSurface">@color/system_inverse_on_surface_dark</item>
+ <item name="materialColorInversePrimary">@color/system_inverse_primary_dark</item>
+ <item name="materialColorInverseSurface">@color/system_inverse_surface_dark</item>
<item name="materialColorOnBackground">@color/system_on_background_dark</item>
- <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
- <item name="materialColorOnSecondary">@color/system_on_secondary_dark</item>
- <item name="materialColorOnTertiary">@color/system_on_tertiary_dark</item>
- <item name="materialColorSurfaceDim">@color/system_surface_dim_dark</item>
- <item name="materialColorSurfaceBright">@color/system_surface_bright_dark</item>
<item name="materialColorOnError">@color/system_on_error_dark</item>
- <item name="materialColorSurface">@color/system_surface_dark</item>
- <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_dark</item>
- <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_dark</item>
+ <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
+ <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
+ <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_dark</item>
+ <item name="materialColorOnSecondary">@color/system_on_secondary_dark</item>
+ <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_dark</item>
+ <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
<item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_dark</item>
+ <item name="materialColorOnTertiary">@color/system_on_tertiary_dark</item>
+ <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_dark</item>
<item name="materialColorOutline">@color/system_outline_dark</item>
<item name="materialColorOutlineVariant">@color/system_outline_variant_dark</item>
- <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
- <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
+ <item name="materialColorPaletteKeyColorNeutral">@color/system_palette_key_color_neutral_dark</item>
+ <item name="materialColorPaletteKeyColorNeutralVariant">@color/system_palette_key_color_neutral_variant_dark</item>
+ <item name="materialColorPaletteKeyColorPrimary">@color/system_palette_key_color_primary_dark</item>
+ <item name="materialColorPaletteKeyColorSecondary">@color/system_palette_key_color_secondary_dark</item>
+ <item name="materialColorPaletteKeyColorTertiary">@color/system_palette_key_color_tertiary_dark</item>
<item name="materialColorPrimary">@color/system_primary_dark</item>
+ <item name="materialColorPrimaryContainer">@color/system_primary_container_dark</item>
+ <item name="materialColorScrim">@color/system_scrim_dark</item>
<item name="materialColorSecondary">@color/system_secondary_dark</item>
+ <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
+ <item name="materialColorShadow">@color/system_shadow_dark</item>
+ <item name="materialColorSurface">@color/system_surface_dark</item>
+ <item name="materialColorSurfaceBright">@color/system_surface_bright_dark</item>
+ <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
+ <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_dark</item>
+ <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_dark</item>
+ <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_dark</item>
+ <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_dark</item>
+ <item name="materialColorSurfaceDim">@color/system_surface_dim_dark</item>
+ <item name="materialColorSurfaceTint">@color/system_surface_tint_dark</item>
+ <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
<item name="materialColorTertiary">@color/system_tertiary_dark</item>
- <item name="materialColorError">@color/system_error_dark</item>
-
- <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
- <item name="customColorClockHour">@color/system_clock_hour_dark</item>
- <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
- <item name="customColorClockSecond">@color/system_clock_second_dark</item>
- <item name="customColorThemeApp">@color/system_theme_app_dark</item>
- <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
- <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
- <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
+ <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
+ <item name="materialColorTextHintInverse">@color/system_text_hint_inverse_dark</item>
+ <item name="materialColorTextPrimaryInverse">@color/system_text_primary_inverse_dark</item>
+ <item name="materialColorTextPrimaryInverseDisableOnly">@color/system_text_primary_inverse_disable_only_dark</item>
+ <item name="materialColorTextSecondaryAndTertiaryInverse">@color/system_text_secondary_and_tertiary_inverse_dark</item>
+ <item name="materialColorTextSecondaryAndTertiaryInverseDisabled">@color/system_text_secondary_and_tertiary_inverse_disabled_dark</item>
+ <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
+ <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
+ <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
+ <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
+ <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
+ <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
+ <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
+ <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
+ <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
+ <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
+ <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
+ <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="customColorBrandA">@color/system_brand_a_dark</item>
<item name="customColorBrandB">@color/system_brand_b_dark</item>
<item name="customColorBrandC">@color/system_brand_c_dark</item>
<item name="customColorBrandD">@color/system_brand_d_dark</item>
- <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
- <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+ <item name="customColorClockHour">@color/system_clock_hour_dark</item>
+ <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
+ <item name="customColorClockSecond">@color/system_clock_second_dark</item>
<item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
<item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
- <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
<item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
<item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
- <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+ <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
<item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
+ <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+ <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+ <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
+ <item name="customColorThemeApp">@color/system_theme_app_dark</item>
+ <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
+ <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
+ <item name="customColorWeatherTemp">@color/system_weather_temp_dark</item>
+ <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
</style>
<!-- DeviceDefault theme for panel windows. This removes all extraneous window
@@ -1672,73 +1876,90 @@
<!-- Toolbar attributes -->
<item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
- <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
- <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
- <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_dark</item>
- <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
- <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_dark</item>
- <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_dark</item>
- <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_dark</item>
- <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_dark</item>
- <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
- <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
- <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
- <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
- <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
- <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
- <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
+ <item name="materialColorBackground">@color/system_background_dark</item>
+ <item name="materialColorControlActivated">@color/system_control_activated_dark</item>
+ <item name="materialColorControlHighlight">@color/system_control_highlight_dark</item>
+ <item name="materialColorControlNormal">@color/system_control_normal_dark</item>
+ <item name="materialColorError">@color/system_error_dark</item>
<item name="materialColorErrorContainer">@color/system_error_container_dark</item>
- <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
- <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
- <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
- <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
- <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
- <item name="materialColorPrimaryContainer">@color/system_primary_container_dark</item>
+ <item name="materialColorInverseOnSurface">@color/system_inverse_on_surface_dark</item>
+ <item name="materialColorInversePrimary">@color/system_inverse_primary_dark</item>
+ <item name="materialColorInverseSurface">@color/system_inverse_surface_dark</item>
<item name="materialColorOnBackground">@color/system_on_background_dark</item>
- <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
- <item name="materialColorOnSecondary">@color/system_on_secondary_dark</item>
- <item name="materialColorOnTertiary">@color/system_on_tertiary_dark</item>
- <item name="materialColorSurfaceDim">@color/system_surface_dim_dark</item>
- <item name="materialColorSurfaceBright">@color/system_surface_bright_dark</item>
<item name="materialColorOnError">@color/system_on_error_dark</item>
- <item name="materialColorSurface">@color/system_surface_dark</item>
- <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_dark</item>
- <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_dark</item>
+ <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
+ <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
+ <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_dark</item>
+ <item name="materialColorOnSecondary">@color/system_on_secondary_dark</item>
+ <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_dark</item>
+ <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
<item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_dark</item>
+ <item name="materialColorOnTertiary">@color/system_on_tertiary_dark</item>
+ <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_dark</item>
<item name="materialColorOutline">@color/system_outline_dark</item>
<item name="materialColorOutlineVariant">@color/system_outline_variant_dark</item>
- <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
- <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
+ <item name="materialColorPaletteKeyColorNeutral">@color/system_palette_key_color_neutral_dark</item>
+ <item name="materialColorPaletteKeyColorNeutralVariant">@color/system_palette_key_color_neutral_variant_dark</item>
+ <item name="materialColorPaletteKeyColorPrimary">@color/system_palette_key_color_primary_dark</item>
+ <item name="materialColorPaletteKeyColorSecondary">@color/system_palette_key_color_secondary_dark</item>
+ <item name="materialColorPaletteKeyColorTertiary">@color/system_palette_key_color_tertiary_dark</item>
<item name="materialColorPrimary">@color/system_primary_dark</item>
+ <item name="materialColorPrimaryContainer">@color/system_primary_container_dark</item>
+ <item name="materialColorScrim">@color/system_scrim_dark</item>
<item name="materialColorSecondary">@color/system_secondary_dark</item>
+ <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
+ <item name="materialColorShadow">@color/system_shadow_dark</item>
+ <item name="materialColorSurface">@color/system_surface_dark</item>
+ <item name="materialColorSurfaceBright">@color/system_surface_bright_dark</item>
+ <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
+ <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_dark</item>
+ <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_dark</item>
+ <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_dark</item>
+ <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_dark</item>
+ <item name="materialColorSurfaceDim">@color/system_surface_dim_dark</item>
+ <item name="materialColorSurfaceTint">@color/system_surface_tint_dark</item>
+ <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
<item name="materialColorTertiary">@color/system_tertiary_dark</item>
- <item name="materialColorError">@color/system_error_dark</item>
-
- <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
- <item name="customColorClockHour">@color/system_clock_hour_dark</item>
- <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
- <item name="customColorClockSecond">@color/system_clock_second_dark</item>
- <item name="customColorThemeApp">@color/system_theme_app_dark</item>
- <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
- <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
- <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
+ <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
+ <item name="materialColorTextHintInverse">@color/system_text_hint_inverse_dark</item>
+ <item name="materialColorTextPrimaryInverse">@color/system_text_primary_inverse_dark</item>
+ <item name="materialColorTextPrimaryInverseDisableOnly">@color/system_text_primary_inverse_disable_only_dark</item>
+ <item name="materialColorTextSecondaryAndTertiaryInverse">@color/system_text_secondary_and_tertiary_inverse_dark</item>
+ <item name="materialColorTextSecondaryAndTertiaryInverseDisabled">@color/system_text_secondary_and_tertiary_inverse_disabled_dark</item>
+ <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
+ <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
+ <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
+ <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
+ <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
+ <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
+ <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
+ <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
+ <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
+ <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
+ <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
+ <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="customColorBrandA">@color/system_brand_a_dark</item>
<item name="customColorBrandB">@color/system_brand_b_dark</item>
<item name="customColorBrandC">@color/system_brand_c_dark</item>
<item name="customColorBrandD">@color/system_brand_d_dark</item>
- <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
- <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+ <item name="customColorClockHour">@color/system_clock_hour_dark</item>
+ <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
+ <item name="customColorClockSecond">@color/system_clock_second_dark</item>
<item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
<item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
- <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
<item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
<item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
- <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+ <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
<item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
+ <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+ <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+ <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
+ <item name="customColorThemeApp">@color/system_theme_app_dark</item>
+ <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
+ <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
+ <item name="customColorWeatherTemp">@color/system_weather_temp_dark</item>
+ <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
</style>
<!-- DeviceDefault theme for windows that want to have the user's selected wallpaper appear
@@ -1789,73 +2010,90 @@
<!-- Toolbar attributes -->
<item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
- <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
- <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
- <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_dark</item>
- <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
- <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_dark</item>
- <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_dark</item>
- <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_dark</item>
- <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_dark</item>
- <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
- <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
- <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
- <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
- <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
- <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
- <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
+ <item name="materialColorBackground">@color/system_background_dark</item>
+ <item name="materialColorControlActivated">@color/system_control_activated_dark</item>
+ <item name="materialColorControlHighlight">@color/system_control_highlight_dark</item>
+ <item name="materialColorControlNormal">@color/system_control_normal_dark</item>
+ <item name="materialColorError">@color/system_error_dark</item>
<item name="materialColorErrorContainer">@color/system_error_container_dark</item>
- <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
- <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
- <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
- <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
- <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
- <item name="materialColorPrimaryContainer">@color/system_primary_container_dark</item>
+ <item name="materialColorInverseOnSurface">@color/system_inverse_on_surface_dark</item>
+ <item name="materialColorInversePrimary">@color/system_inverse_primary_dark</item>
+ <item name="materialColorInverseSurface">@color/system_inverse_surface_dark</item>
<item name="materialColorOnBackground">@color/system_on_background_dark</item>
- <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
- <item name="materialColorOnSecondary">@color/system_on_secondary_dark</item>
- <item name="materialColorOnTertiary">@color/system_on_tertiary_dark</item>
- <item name="materialColorSurfaceDim">@color/system_surface_dim_dark</item>
- <item name="materialColorSurfaceBright">@color/system_surface_bright_dark</item>
<item name="materialColorOnError">@color/system_on_error_dark</item>
- <item name="materialColorSurface">@color/system_surface_dark</item>
- <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_dark</item>
- <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_dark</item>
+ <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
+ <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
+ <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_dark</item>
+ <item name="materialColorOnSecondary">@color/system_on_secondary_dark</item>
+ <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_dark</item>
+ <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
<item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_dark</item>
+ <item name="materialColorOnTertiary">@color/system_on_tertiary_dark</item>
+ <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_dark</item>
<item name="materialColorOutline">@color/system_outline_dark</item>
<item name="materialColorOutlineVariant">@color/system_outline_variant_dark</item>
- <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
- <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
+ <item name="materialColorPaletteKeyColorNeutral">@color/system_palette_key_color_neutral_dark</item>
+ <item name="materialColorPaletteKeyColorNeutralVariant">@color/system_palette_key_color_neutral_variant_dark</item>
+ <item name="materialColorPaletteKeyColorPrimary">@color/system_palette_key_color_primary_dark</item>
+ <item name="materialColorPaletteKeyColorSecondary">@color/system_palette_key_color_secondary_dark</item>
+ <item name="materialColorPaletteKeyColorTertiary">@color/system_palette_key_color_tertiary_dark</item>
<item name="materialColorPrimary">@color/system_primary_dark</item>
+ <item name="materialColorPrimaryContainer">@color/system_primary_container_dark</item>
+ <item name="materialColorScrim">@color/system_scrim_dark</item>
<item name="materialColorSecondary">@color/system_secondary_dark</item>
+ <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
+ <item name="materialColorShadow">@color/system_shadow_dark</item>
+ <item name="materialColorSurface">@color/system_surface_dark</item>
+ <item name="materialColorSurfaceBright">@color/system_surface_bright_dark</item>
+ <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
+ <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_dark</item>
+ <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_dark</item>
+ <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_dark</item>
+ <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_dark</item>
+ <item name="materialColorSurfaceDim">@color/system_surface_dim_dark</item>
+ <item name="materialColorSurfaceTint">@color/system_surface_tint_dark</item>
+ <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
<item name="materialColorTertiary">@color/system_tertiary_dark</item>
- <item name="materialColorError">@color/system_error_dark</item>
-
- <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
- <item name="customColorClockHour">@color/system_clock_hour_dark</item>
- <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
- <item name="customColorClockSecond">@color/system_clock_second_dark</item>
- <item name="customColorThemeApp">@color/system_theme_app_dark</item>
- <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
- <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
- <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
+ <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
+ <item name="materialColorTextHintInverse">@color/system_text_hint_inverse_dark</item>
+ <item name="materialColorTextPrimaryInverse">@color/system_text_primary_inverse_dark</item>
+ <item name="materialColorTextPrimaryInverseDisableOnly">@color/system_text_primary_inverse_disable_only_dark</item>
+ <item name="materialColorTextSecondaryAndTertiaryInverse">@color/system_text_secondary_and_tertiary_inverse_dark</item>
+ <item name="materialColorTextSecondaryAndTertiaryInverseDisabled">@color/system_text_secondary_and_tertiary_inverse_disabled_dark</item>
+ <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
+ <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
+ <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
+ <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
+ <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
+ <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
+ <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
+ <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
+ <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
+ <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
+ <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
+ <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="customColorBrandA">@color/system_brand_a_dark</item>
<item name="customColorBrandB">@color/system_brand_b_dark</item>
<item name="customColorBrandC">@color/system_brand_c_dark</item>
<item name="customColorBrandD">@color/system_brand_d_dark</item>
- <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
- <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+ <item name="customColorClockHour">@color/system_clock_hour_dark</item>
+ <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
+ <item name="customColorClockSecond">@color/system_clock_second_dark</item>
<item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
<item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
- <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
<item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
<item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
- <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+ <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
<item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
+ <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+ <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+ <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
+ <item name="customColorThemeApp">@color/system_theme_app_dark</item>
+ <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
+ <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
+ <item name="customColorWeatherTemp">@color/system_weather_temp_dark</item>
+ <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
</style>
<!-- DeviceDefault theme for windows that want to have the user's selected wallpaper appear
@@ -1906,73 +2144,90 @@
<!-- Toolbar attributes -->
<item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
- <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
- <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
- <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_dark</item>
- <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
- <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_dark</item>
- <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_dark</item>
- <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_dark</item>
- <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_dark</item>
- <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
- <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
- <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
- <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
- <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
- <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
- <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
+ <item name="materialColorBackground">@color/system_background_dark</item>
+ <item name="materialColorControlActivated">@color/system_control_activated_dark</item>
+ <item name="materialColorControlHighlight">@color/system_control_highlight_dark</item>
+ <item name="materialColorControlNormal">@color/system_control_normal_dark</item>
+ <item name="materialColorError">@color/system_error_dark</item>
<item name="materialColorErrorContainer">@color/system_error_container_dark</item>
- <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
- <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
- <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
- <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
- <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
- <item name="materialColorPrimaryContainer">@color/system_primary_container_dark</item>
+ <item name="materialColorInverseOnSurface">@color/system_inverse_on_surface_dark</item>
+ <item name="materialColorInversePrimary">@color/system_inverse_primary_dark</item>
+ <item name="materialColorInverseSurface">@color/system_inverse_surface_dark</item>
<item name="materialColorOnBackground">@color/system_on_background_dark</item>
- <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
- <item name="materialColorOnSecondary">@color/system_on_secondary_dark</item>
- <item name="materialColorOnTertiary">@color/system_on_tertiary_dark</item>
- <item name="materialColorSurfaceDim">@color/system_surface_dim_dark</item>
- <item name="materialColorSurfaceBright">@color/system_surface_bright_dark</item>
<item name="materialColorOnError">@color/system_on_error_dark</item>
- <item name="materialColorSurface">@color/system_surface_dark</item>
- <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_dark</item>
- <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_dark</item>
+ <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
+ <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
+ <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_dark</item>
+ <item name="materialColorOnSecondary">@color/system_on_secondary_dark</item>
+ <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_dark</item>
+ <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
<item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_dark</item>
+ <item name="materialColorOnTertiary">@color/system_on_tertiary_dark</item>
+ <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_dark</item>
<item name="materialColorOutline">@color/system_outline_dark</item>
<item name="materialColorOutlineVariant">@color/system_outline_variant_dark</item>
- <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
- <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
+ <item name="materialColorPaletteKeyColorNeutral">@color/system_palette_key_color_neutral_dark</item>
+ <item name="materialColorPaletteKeyColorNeutralVariant">@color/system_palette_key_color_neutral_variant_dark</item>
+ <item name="materialColorPaletteKeyColorPrimary">@color/system_palette_key_color_primary_dark</item>
+ <item name="materialColorPaletteKeyColorSecondary">@color/system_palette_key_color_secondary_dark</item>
+ <item name="materialColorPaletteKeyColorTertiary">@color/system_palette_key_color_tertiary_dark</item>
<item name="materialColorPrimary">@color/system_primary_dark</item>
+ <item name="materialColorPrimaryContainer">@color/system_primary_container_dark</item>
+ <item name="materialColorScrim">@color/system_scrim_dark</item>
<item name="materialColorSecondary">@color/system_secondary_dark</item>
+ <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
+ <item name="materialColorShadow">@color/system_shadow_dark</item>
+ <item name="materialColorSurface">@color/system_surface_dark</item>
+ <item name="materialColorSurfaceBright">@color/system_surface_bright_dark</item>
+ <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
+ <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_dark</item>
+ <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_dark</item>
+ <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_dark</item>
+ <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_dark</item>
+ <item name="materialColorSurfaceDim">@color/system_surface_dim_dark</item>
+ <item name="materialColorSurfaceTint">@color/system_surface_tint_dark</item>
+ <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
<item name="materialColorTertiary">@color/system_tertiary_dark</item>
- <item name="materialColorError">@color/system_error_dark</item>
-
- <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
- <item name="customColorClockHour">@color/system_clock_hour_dark</item>
- <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
- <item name="customColorClockSecond">@color/system_clock_second_dark</item>
- <item name="customColorThemeApp">@color/system_theme_app_dark</item>
- <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
- <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
- <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
+ <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
+ <item name="materialColorTextHintInverse">@color/system_text_hint_inverse_dark</item>
+ <item name="materialColorTextPrimaryInverse">@color/system_text_primary_inverse_dark</item>
+ <item name="materialColorTextPrimaryInverseDisableOnly">@color/system_text_primary_inverse_disable_only_dark</item>
+ <item name="materialColorTextSecondaryAndTertiaryInverse">@color/system_text_secondary_and_tertiary_inverse_dark</item>
+ <item name="materialColorTextSecondaryAndTertiaryInverseDisabled">@color/system_text_secondary_and_tertiary_inverse_disabled_dark</item>
+ <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
+ <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
+ <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
+ <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
+ <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
+ <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
+ <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
+ <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
+ <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
+ <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
+ <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
+ <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="customColorBrandA">@color/system_brand_a_dark</item>
<item name="customColorBrandB">@color/system_brand_b_dark</item>
<item name="customColorBrandC">@color/system_brand_c_dark</item>
<item name="customColorBrandD">@color/system_brand_d_dark</item>
- <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
- <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+ <item name="customColorClockHour">@color/system_clock_hour_dark</item>
+ <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
+ <item name="customColorClockSecond">@color/system_clock_second_dark</item>
<item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
<item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
- <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
<item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
<item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
- <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+ <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
<item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
+ <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+ <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+ <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
+ <item name="customColorThemeApp">@color/system_theme_app_dark</item>
+ <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
+ <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
+ <item name="customColorWeatherTemp">@color/system_weather_temp_dark</item>
+ <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
</style>
<!-- DeviceDefault style for input methods, which is used by the
@@ -2023,73 +2278,90 @@
<!-- Toolbar attributes -->
<item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
- <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
- <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
- <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_light</item>
- <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
- <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_light</item>
- <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_light</item>
- <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_light</item>
- <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_light</item>
- <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
- <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
- <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
- <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
- <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
- <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
- <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
+ <item name="materialColorBackground">@color/system_background_light</item>
+ <item name="materialColorControlActivated">@color/system_control_activated_light</item>
+ <item name="materialColorControlHighlight">@color/system_control_highlight_light</item>
+ <item name="materialColorControlNormal">@color/system_control_normal_light</item>
+ <item name="materialColorError">@color/system_error_light</item>
<item name="materialColorErrorContainer">@color/system_error_container_light</item>
- <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
- <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
- <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
- <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
- <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
- <item name="materialColorPrimaryContainer">@color/system_primary_container_light</item>
+ <item name="materialColorInverseOnSurface">@color/system_inverse_on_surface_light</item>
+ <item name="materialColorInversePrimary">@color/system_inverse_primary_light</item>
+ <item name="materialColorInverseSurface">@color/system_inverse_surface_light</item>
<item name="materialColorOnBackground">@color/system_on_background_light</item>
- <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
- <item name="materialColorOnSecondary">@color/system_on_secondary_light</item>
- <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item>
- <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item>
- <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item>
<item name="materialColorOnError">@color/system_on_error_light</item>
- <item name="materialColorSurface">@color/system_surface_light</item>
- <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item>
- <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item>
+ <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
+ <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
+ <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_light</item>
+ <item name="materialColorOnSecondary">@color/system_on_secondary_light</item>
+ <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_light</item>
+ <item name="materialColorOnSurface">@color/system_on_surface_light</item>
<item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_light</item>
+ <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item>
+ <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_light</item>
<item name="materialColorOutline">@color/system_outline_light</item>
<item name="materialColorOutlineVariant">@color/system_outline_variant_light</item>
- <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
- <item name="materialColorOnSurface">@color/system_on_surface_light</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
+ <item name="materialColorPaletteKeyColorNeutral">@color/system_palette_key_color_neutral_light</item>
+ <item name="materialColorPaletteKeyColorNeutralVariant">@color/system_palette_key_color_neutral_variant_light</item>
+ <item name="materialColorPaletteKeyColorPrimary">@color/system_palette_key_color_primary_light</item>
+ <item name="materialColorPaletteKeyColorSecondary">@color/system_palette_key_color_secondary_light</item>
+ <item name="materialColorPaletteKeyColorTertiary">@color/system_palette_key_color_tertiary_light</item>
<item name="materialColorPrimary">@color/system_primary_light</item>
+ <item name="materialColorPrimaryContainer">@color/system_primary_container_light</item>
+ <item name="materialColorScrim">@color/system_scrim_light</item>
<item name="materialColorSecondary">@color/system_secondary_light</item>
+ <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
+ <item name="materialColorShadow">@color/system_shadow_light</item>
+ <item name="materialColorSurface">@color/system_surface_light</item>
+ <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item>
+ <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
+ <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item>
+ <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item>
+ <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_light</item>
+ <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_light</item>
+ <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item>
+ <item name="materialColorSurfaceTint">@color/system_surface_tint_light</item>
+ <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
<item name="materialColorTertiary">@color/system_tertiary_light</item>
- <item name="materialColorError">@color/system_error_light</item>
-
- <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
- <item name="customColorClockHour">@color/system_clock_hour_light</item>
- <item name="customColorClockMinute">@color/system_clock_minute_light</item>
- <item name="customColorClockSecond">@color/system_clock_second_light</item>
- <item name="customColorThemeApp">@color/system_theme_app_light</item>
- <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
- <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
+ <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
+ <item name="materialColorTextHintInverse">@color/system_text_hint_inverse_light</item>
+ <item name="materialColorTextPrimaryInverse">@color/system_text_primary_inverse_light</item>
+ <item name="materialColorTextPrimaryInverseDisableOnly">@color/system_text_primary_inverse_disable_only_light</item>
+ <item name="materialColorTextSecondaryAndTertiaryInverse">@color/system_text_secondary_and_tertiary_inverse_light</item>
+ <item name="materialColorTextSecondaryAndTertiaryInverseDisabled">@color/system_text_secondary_and_tertiary_inverse_disabled_light</item>
+ <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
+ <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
+ <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
+ <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
+ <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
+ <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
+ <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
+ <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
+ <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
+ <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
+ <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
+ <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="customColorBrandA">@color/system_brand_a_light</item>
<item name="customColorBrandB">@color/system_brand_b_light</item>
<item name="customColorBrandC">@color/system_brand_c_light</item>
<item name="customColorBrandD">@color/system_brand_d_light</item>
- <item name="customColorUnderSurface">@color/system_under_surface_light</item>
- <item name="customColorShadeActive">@color/system_shade_active_light</item>
+ <item name="customColorClockHour">@color/system_clock_hour_light</item>
+ <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+ <item name="customColorClockSecond">@color/system_clock_second_light</item>
<item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
<item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
- <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
<item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
<item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
- <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+ <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
<item name="customColorOverviewBackground">@color/system_overview_background_light</item>
+ <item name="customColorShadeActive">@color/system_shade_active_light</item>
+ <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+ <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+ <item name="customColorThemeApp">@color/system_theme_app_light</item>
+ <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
+ <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+ <item name="customColorWeatherTemp">@color/system_weather_temp_light</item>
+ <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
</style>
<!-- DeviceDefault style for input methods, which is used by the
@@ -2140,73 +2412,90 @@
<!-- Toolbar attributes -->
<item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
- <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
- <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
- <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_light</item>
- <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
- <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_light</item>
- <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_light</item>
- <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_light</item>
- <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_light</item>
- <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
- <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
- <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
- <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
- <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
- <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
- <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
+ <item name="materialColorBackground">@color/system_background_light</item>
+ <item name="materialColorControlActivated">@color/system_control_activated_light</item>
+ <item name="materialColorControlHighlight">@color/system_control_highlight_light</item>
+ <item name="materialColorControlNormal">@color/system_control_normal_light</item>
+ <item name="materialColorError">@color/system_error_light</item>
<item name="materialColorErrorContainer">@color/system_error_container_light</item>
- <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
- <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
- <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
- <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
- <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
- <item name="materialColorPrimaryContainer">@color/system_primary_container_light</item>
+ <item name="materialColorInverseOnSurface">@color/system_inverse_on_surface_light</item>
+ <item name="materialColorInversePrimary">@color/system_inverse_primary_light</item>
+ <item name="materialColorInverseSurface">@color/system_inverse_surface_light</item>
<item name="materialColorOnBackground">@color/system_on_background_light</item>
- <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
- <item name="materialColorOnSecondary">@color/system_on_secondary_light</item>
- <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item>
- <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item>
- <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item>
<item name="materialColorOnError">@color/system_on_error_light</item>
- <item name="materialColorSurface">@color/system_surface_light</item>
- <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item>
- <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item>
+ <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
+ <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
+ <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_light</item>
+ <item name="materialColorOnSecondary">@color/system_on_secondary_light</item>
+ <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_light</item>
+ <item name="materialColorOnSurface">@color/system_on_surface_light</item>
<item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_light</item>
+ <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item>
+ <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_light</item>
<item name="materialColorOutline">@color/system_outline_light</item>
<item name="materialColorOutlineVariant">@color/system_outline_variant_light</item>
- <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
- <item name="materialColorOnSurface">@color/system_on_surface_light</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
+ <item name="materialColorPaletteKeyColorNeutral">@color/system_palette_key_color_neutral_light</item>
+ <item name="materialColorPaletteKeyColorNeutralVariant">@color/system_palette_key_color_neutral_variant_light</item>
+ <item name="materialColorPaletteKeyColorPrimary">@color/system_palette_key_color_primary_light</item>
+ <item name="materialColorPaletteKeyColorSecondary">@color/system_palette_key_color_secondary_light</item>
+ <item name="materialColorPaletteKeyColorTertiary">@color/system_palette_key_color_tertiary_light</item>
<item name="materialColorPrimary">@color/system_primary_light</item>
+ <item name="materialColorPrimaryContainer">@color/system_primary_container_light</item>
+ <item name="materialColorScrim">@color/system_scrim_light</item>
<item name="materialColorSecondary">@color/system_secondary_light</item>
+ <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
+ <item name="materialColorShadow">@color/system_shadow_light</item>
+ <item name="materialColorSurface">@color/system_surface_light</item>
+ <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item>
+ <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
+ <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item>
+ <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item>
+ <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_light</item>
+ <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_light</item>
+ <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item>
+ <item name="materialColorSurfaceTint">@color/system_surface_tint_light</item>
+ <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
<item name="materialColorTertiary">@color/system_tertiary_light</item>
- <item name="materialColorError">@color/system_error_light</item>
-
- <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
- <item name="customColorClockHour">@color/system_clock_hour_light</item>
- <item name="customColorClockMinute">@color/system_clock_minute_light</item>
- <item name="customColorClockSecond">@color/system_clock_second_light</item>
- <item name="customColorThemeApp">@color/system_theme_app_light</item>
- <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
- <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
+ <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
+ <item name="materialColorTextHintInverse">@color/system_text_hint_inverse_light</item>
+ <item name="materialColorTextPrimaryInverse">@color/system_text_primary_inverse_light</item>
+ <item name="materialColorTextPrimaryInverseDisableOnly">@color/system_text_primary_inverse_disable_only_light</item>
+ <item name="materialColorTextSecondaryAndTertiaryInverse">@color/system_text_secondary_and_tertiary_inverse_light</item>
+ <item name="materialColorTextSecondaryAndTertiaryInverseDisabled">@color/system_text_secondary_and_tertiary_inverse_disabled_light</item>
+ <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
+ <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
+ <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
+ <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
+ <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
+ <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
+ <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
+ <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
+ <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
+ <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
+ <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
+ <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="customColorBrandA">@color/system_brand_a_light</item>
<item name="customColorBrandB">@color/system_brand_b_light</item>
<item name="customColorBrandC">@color/system_brand_c_light</item>
<item name="customColorBrandD">@color/system_brand_d_light</item>
- <item name="customColorUnderSurface">@color/system_under_surface_light</item>
- <item name="customColorShadeActive">@color/system_shade_active_light</item>
+ <item name="customColorClockHour">@color/system_clock_hour_light</item>
+ <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+ <item name="customColorClockSecond">@color/system_clock_second_light</item>
<item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
<item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
- <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
<item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
<item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
- <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+ <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
<item name="customColorOverviewBackground">@color/system_overview_background_light</item>
+ <item name="customColorShadeActive">@color/system_shade_active_light</item>
+ <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+ <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+ <item name="customColorThemeApp">@color/system_theme_app_light</item>
+ <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
+ <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+ <item name="customColorWeatherTemp">@color/system_weather_temp_light</item>
+ <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
</style>
<style name="Theme.DeviceDefault.Dialog.Alert" parent="Theme.Material.Dialog.Alert">
@@ -2257,73 +2546,90 @@
<!-- Toolbar attributes -->
<item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
- <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
- <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
- <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_dark</item>
- <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
- <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_dark</item>
- <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_dark</item>
- <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_dark</item>
- <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_dark</item>
- <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
- <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
- <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
- <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
- <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
- <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
- <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
+ <item name="materialColorBackground">@color/system_background_dark</item>
+ <item name="materialColorControlActivated">@color/system_control_activated_dark</item>
+ <item name="materialColorControlHighlight">@color/system_control_highlight_dark</item>
+ <item name="materialColorControlNormal">@color/system_control_normal_dark</item>
+ <item name="materialColorError">@color/system_error_dark</item>
<item name="materialColorErrorContainer">@color/system_error_container_dark</item>
- <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
- <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
- <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
- <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
- <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
- <item name="materialColorPrimaryContainer">@color/system_primary_container_dark</item>
+ <item name="materialColorInverseOnSurface">@color/system_inverse_on_surface_dark</item>
+ <item name="materialColorInversePrimary">@color/system_inverse_primary_dark</item>
+ <item name="materialColorInverseSurface">@color/system_inverse_surface_dark</item>
<item name="materialColorOnBackground">@color/system_on_background_dark</item>
- <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
- <item name="materialColorOnSecondary">@color/system_on_secondary_dark</item>
- <item name="materialColorOnTertiary">@color/system_on_tertiary_dark</item>
- <item name="materialColorSurfaceDim">@color/system_surface_dim_dark</item>
- <item name="materialColorSurfaceBright">@color/system_surface_bright_dark</item>
<item name="materialColorOnError">@color/system_on_error_dark</item>
- <item name="materialColorSurface">@color/system_surface_dark</item>
- <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_dark</item>
- <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_dark</item>
+ <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
+ <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
+ <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_dark</item>
+ <item name="materialColorOnSecondary">@color/system_on_secondary_dark</item>
+ <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_dark</item>
+ <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
<item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_dark</item>
+ <item name="materialColorOnTertiary">@color/system_on_tertiary_dark</item>
+ <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_dark</item>
<item name="materialColorOutline">@color/system_outline_dark</item>
<item name="materialColorOutlineVariant">@color/system_outline_variant_dark</item>
- <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
- <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
+ <item name="materialColorPaletteKeyColorNeutral">@color/system_palette_key_color_neutral_dark</item>
+ <item name="materialColorPaletteKeyColorNeutralVariant">@color/system_palette_key_color_neutral_variant_dark</item>
+ <item name="materialColorPaletteKeyColorPrimary">@color/system_palette_key_color_primary_dark</item>
+ <item name="materialColorPaletteKeyColorSecondary">@color/system_palette_key_color_secondary_dark</item>
+ <item name="materialColorPaletteKeyColorTertiary">@color/system_palette_key_color_tertiary_dark</item>
<item name="materialColorPrimary">@color/system_primary_dark</item>
+ <item name="materialColorPrimaryContainer">@color/system_primary_container_dark</item>
+ <item name="materialColorScrim">@color/system_scrim_dark</item>
<item name="materialColorSecondary">@color/system_secondary_dark</item>
+ <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
+ <item name="materialColorShadow">@color/system_shadow_dark</item>
+ <item name="materialColorSurface">@color/system_surface_dark</item>
+ <item name="materialColorSurfaceBright">@color/system_surface_bright_dark</item>
+ <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
+ <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_dark</item>
+ <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_dark</item>
+ <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_dark</item>
+ <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_dark</item>
+ <item name="materialColorSurfaceDim">@color/system_surface_dim_dark</item>
+ <item name="materialColorSurfaceTint">@color/system_surface_tint_dark</item>
+ <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
<item name="materialColorTertiary">@color/system_tertiary_dark</item>
- <item name="materialColorError">@color/system_error_dark</item>
-
- <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
- <item name="customColorClockHour">@color/system_clock_hour_dark</item>
- <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
- <item name="customColorClockSecond">@color/system_clock_second_dark</item>
- <item name="customColorThemeApp">@color/system_theme_app_dark</item>
- <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
- <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
- <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
+ <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
+ <item name="materialColorTextHintInverse">@color/system_text_hint_inverse_dark</item>
+ <item name="materialColorTextPrimaryInverse">@color/system_text_primary_inverse_dark</item>
+ <item name="materialColorTextPrimaryInverseDisableOnly">@color/system_text_primary_inverse_disable_only_dark</item>
+ <item name="materialColorTextSecondaryAndTertiaryInverse">@color/system_text_secondary_and_tertiary_inverse_dark</item>
+ <item name="materialColorTextSecondaryAndTertiaryInverseDisabled">@color/system_text_secondary_and_tertiary_inverse_disabled_dark</item>
+ <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
+ <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
+ <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
+ <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
+ <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
+ <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
+ <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
+ <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
+ <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
+ <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
+ <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
+ <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="customColorBrandA">@color/system_brand_a_dark</item>
<item name="customColorBrandB">@color/system_brand_b_dark</item>
<item name="customColorBrandC">@color/system_brand_c_dark</item>
<item name="customColorBrandD">@color/system_brand_d_dark</item>
- <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
- <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+ <item name="customColorClockHour">@color/system_clock_hour_dark</item>
+ <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
+ <item name="customColorClockSecond">@color/system_clock_second_dark</item>
<item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
<item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
- <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
<item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
<item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
- <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+ <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
<item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
+ <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+ <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+ <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
+ <item name="customColorThemeApp">@color/system_theme_app_dark</item>
+ <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
+ <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
+ <item name="customColorWeatherTemp">@color/system_weather_temp_dark</item>
+ <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
</style>
<!-- Theme for the dialog shown when an app crashes or ANRs. -->
@@ -2379,73 +2685,90 @@
<!-- Toolbar attributes -->
<item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
- <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
- <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
- <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_dark</item>
- <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
- <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_dark</item>
- <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_dark</item>
- <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_dark</item>
- <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_dark</item>
- <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
- <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
- <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
- <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
- <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
- <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
- <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
+ <item name="materialColorBackground">@color/system_background_dark</item>
+ <item name="materialColorControlActivated">@color/system_control_activated_dark</item>
+ <item name="materialColorControlHighlight">@color/system_control_highlight_dark</item>
+ <item name="materialColorControlNormal">@color/system_control_normal_dark</item>
+ <item name="materialColorError">@color/system_error_dark</item>
<item name="materialColorErrorContainer">@color/system_error_container_dark</item>
- <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
- <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
- <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
- <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
- <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
- <item name="materialColorPrimaryContainer">@color/system_primary_container_dark</item>
+ <item name="materialColorInverseOnSurface">@color/system_inverse_on_surface_dark</item>
+ <item name="materialColorInversePrimary">@color/system_inverse_primary_dark</item>
+ <item name="materialColorInverseSurface">@color/system_inverse_surface_dark</item>
<item name="materialColorOnBackground">@color/system_on_background_dark</item>
- <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
- <item name="materialColorOnSecondary">@color/system_on_secondary_dark</item>
- <item name="materialColorOnTertiary">@color/system_on_tertiary_dark</item>
- <item name="materialColorSurfaceDim">@color/system_surface_dim_dark</item>
- <item name="materialColorSurfaceBright">@color/system_surface_bright_dark</item>
<item name="materialColorOnError">@color/system_on_error_dark</item>
- <item name="materialColorSurface">@color/system_surface_dark</item>
- <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_dark</item>
- <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_dark</item>
+ <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
+ <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
+ <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_dark</item>
+ <item name="materialColorOnSecondary">@color/system_on_secondary_dark</item>
+ <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_dark</item>
+ <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
<item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_dark</item>
+ <item name="materialColorOnTertiary">@color/system_on_tertiary_dark</item>
+ <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_dark</item>
<item name="materialColorOutline">@color/system_outline_dark</item>
<item name="materialColorOutlineVariant">@color/system_outline_variant_dark</item>
- <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
- <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
+ <item name="materialColorPaletteKeyColorNeutral">@color/system_palette_key_color_neutral_dark</item>
+ <item name="materialColorPaletteKeyColorNeutralVariant">@color/system_palette_key_color_neutral_variant_dark</item>
+ <item name="materialColorPaletteKeyColorPrimary">@color/system_palette_key_color_primary_dark</item>
+ <item name="materialColorPaletteKeyColorSecondary">@color/system_palette_key_color_secondary_dark</item>
+ <item name="materialColorPaletteKeyColorTertiary">@color/system_palette_key_color_tertiary_dark</item>
<item name="materialColorPrimary">@color/system_primary_dark</item>
+ <item name="materialColorPrimaryContainer">@color/system_primary_container_dark</item>
+ <item name="materialColorScrim">@color/system_scrim_dark</item>
<item name="materialColorSecondary">@color/system_secondary_dark</item>
+ <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
+ <item name="materialColorShadow">@color/system_shadow_dark</item>
+ <item name="materialColorSurface">@color/system_surface_dark</item>
+ <item name="materialColorSurfaceBright">@color/system_surface_bright_dark</item>
+ <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
+ <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_dark</item>
+ <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_dark</item>
+ <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_dark</item>
+ <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_dark</item>
+ <item name="materialColorSurfaceDim">@color/system_surface_dim_dark</item>
+ <item name="materialColorSurfaceTint">@color/system_surface_tint_dark</item>
+ <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
<item name="materialColorTertiary">@color/system_tertiary_dark</item>
- <item name="materialColorError">@color/system_error_dark</item>
-
- <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
- <item name="customColorClockHour">@color/system_clock_hour_dark</item>
- <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
- <item name="customColorClockSecond">@color/system_clock_second_dark</item>
- <item name="customColorThemeApp">@color/system_theme_app_dark</item>
- <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
- <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
- <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
+ <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
+ <item name="materialColorTextHintInverse">@color/system_text_hint_inverse_dark</item>
+ <item name="materialColorTextPrimaryInverse">@color/system_text_primary_inverse_dark</item>
+ <item name="materialColorTextPrimaryInverseDisableOnly">@color/system_text_primary_inverse_disable_only_dark</item>
+ <item name="materialColorTextSecondaryAndTertiaryInverse">@color/system_text_secondary_and_tertiary_inverse_dark</item>
+ <item name="materialColorTextSecondaryAndTertiaryInverseDisabled">@color/system_text_secondary_and_tertiary_inverse_disabled_dark</item>
+ <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
+ <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
+ <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
+ <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
+ <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
+ <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
+ <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
+ <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
+ <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
+ <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
+ <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
+ <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="customColorBrandA">@color/system_brand_a_dark</item>
<item name="customColorBrandB">@color/system_brand_b_dark</item>
<item name="customColorBrandC">@color/system_brand_c_dark</item>
<item name="customColorBrandD">@color/system_brand_d_dark</item>
- <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
- <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+ <item name="customColorClockHour">@color/system_clock_hour_dark</item>
+ <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
+ <item name="customColorClockSecond">@color/system_clock_second_dark</item>
<item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
<item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
- <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
<item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
<item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
- <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+ <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
<item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
+ <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+ <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+ <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
+ <item name="customColorThemeApp">@color/system_theme_app_dark</item>
+ <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
+ <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
+ <item name="customColorWeatherTemp">@color/system_weather_temp_dark</item>
+ <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
</style>
<style name="Theme.DeviceDefault.Dialog.NoFrame" parent="Theme.Material.Dialog.NoFrame">
@@ -2494,73 +2817,90 @@
<!-- Toolbar attributes -->
<item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
- <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
- <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
- <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_dark</item>
- <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
- <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_dark</item>
- <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_dark</item>
- <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_dark</item>
- <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_dark</item>
- <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
- <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
- <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
- <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
- <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
- <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
- <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
+ <item name="materialColorBackground">@color/system_background_dark</item>
+ <item name="materialColorControlActivated">@color/system_control_activated_dark</item>
+ <item name="materialColorControlHighlight">@color/system_control_highlight_dark</item>
+ <item name="materialColorControlNormal">@color/system_control_normal_dark</item>
+ <item name="materialColorError">@color/system_error_dark</item>
<item name="materialColorErrorContainer">@color/system_error_container_dark</item>
- <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
- <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
- <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
- <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
- <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
- <item name="materialColorPrimaryContainer">@color/system_primary_container_dark</item>
+ <item name="materialColorInverseOnSurface">@color/system_inverse_on_surface_dark</item>
+ <item name="materialColorInversePrimary">@color/system_inverse_primary_dark</item>
+ <item name="materialColorInverseSurface">@color/system_inverse_surface_dark</item>
<item name="materialColorOnBackground">@color/system_on_background_dark</item>
- <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
- <item name="materialColorOnSecondary">@color/system_on_secondary_dark</item>
- <item name="materialColorOnTertiary">@color/system_on_tertiary_dark</item>
- <item name="materialColorSurfaceDim">@color/system_surface_dim_dark</item>
- <item name="materialColorSurfaceBright">@color/system_surface_bright_dark</item>
<item name="materialColorOnError">@color/system_on_error_dark</item>
- <item name="materialColorSurface">@color/system_surface_dark</item>
- <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_dark</item>
- <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_dark</item>
+ <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
+ <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
+ <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_dark</item>
+ <item name="materialColorOnSecondary">@color/system_on_secondary_dark</item>
+ <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_dark</item>
+ <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
<item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_dark</item>
+ <item name="materialColorOnTertiary">@color/system_on_tertiary_dark</item>
+ <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_dark</item>
<item name="materialColorOutline">@color/system_outline_dark</item>
<item name="materialColorOutlineVariant">@color/system_outline_variant_dark</item>
- <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
- <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
+ <item name="materialColorPaletteKeyColorNeutral">@color/system_palette_key_color_neutral_dark</item>
+ <item name="materialColorPaletteKeyColorNeutralVariant">@color/system_palette_key_color_neutral_variant_dark</item>
+ <item name="materialColorPaletteKeyColorPrimary">@color/system_palette_key_color_primary_dark</item>
+ <item name="materialColorPaletteKeyColorSecondary">@color/system_palette_key_color_secondary_dark</item>
+ <item name="materialColorPaletteKeyColorTertiary">@color/system_palette_key_color_tertiary_dark</item>
<item name="materialColorPrimary">@color/system_primary_dark</item>
+ <item name="materialColorPrimaryContainer">@color/system_primary_container_dark</item>
+ <item name="materialColorScrim">@color/system_scrim_dark</item>
<item name="materialColorSecondary">@color/system_secondary_dark</item>
+ <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
+ <item name="materialColorShadow">@color/system_shadow_dark</item>
+ <item name="materialColorSurface">@color/system_surface_dark</item>
+ <item name="materialColorSurfaceBright">@color/system_surface_bright_dark</item>
+ <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
+ <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_dark</item>
+ <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_dark</item>
+ <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_dark</item>
+ <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_dark</item>
+ <item name="materialColorSurfaceDim">@color/system_surface_dim_dark</item>
+ <item name="materialColorSurfaceTint">@color/system_surface_tint_dark</item>
+ <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
<item name="materialColorTertiary">@color/system_tertiary_dark</item>
- <item name="materialColorError">@color/system_error_dark</item>
-
- <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
- <item name="customColorClockHour">@color/system_clock_hour_dark</item>
- <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
- <item name="customColorClockSecond">@color/system_clock_second_dark</item>
- <item name="customColorThemeApp">@color/system_theme_app_dark</item>
- <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
- <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
- <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
+ <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
+ <item name="materialColorTextHintInverse">@color/system_text_hint_inverse_dark</item>
+ <item name="materialColorTextPrimaryInverse">@color/system_text_primary_inverse_dark</item>
+ <item name="materialColorTextPrimaryInverseDisableOnly">@color/system_text_primary_inverse_disable_only_dark</item>
+ <item name="materialColorTextSecondaryAndTertiaryInverse">@color/system_text_secondary_and_tertiary_inverse_dark</item>
+ <item name="materialColorTextSecondaryAndTertiaryInverseDisabled">@color/system_text_secondary_and_tertiary_inverse_disabled_dark</item>
+ <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
+ <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
+ <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
+ <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
+ <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
+ <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
+ <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
+ <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
+ <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
+ <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
+ <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
+ <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="customColorBrandA">@color/system_brand_a_dark</item>
<item name="customColorBrandB">@color/system_brand_b_dark</item>
<item name="customColorBrandC">@color/system_brand_c_dark</item>
<item name="customColorBrandD">@color/system_brand_d_dark</item>
- <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
- <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+ <item name="customColorClockHour">@color/system_clock_hour_dark</item>
+ <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
+ <item name="customColorClockSecond">@color/system_clock_second_dark</item>
<item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
<item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
- <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
<item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
<item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
- <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+ <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
<item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
+ <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+ <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+ <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
+ <item name="customColorThemeApp">@color/system_theme_app_dark</item>
+ <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
+ <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
+ <item name="customColorWeatherTemp">@color/system_weather_temp_dark</item>
+ <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault} with a light-colored style -->
@@ -2747,73 +3087,90 @@
<item name="colorPopupBackground">?attr/colorBackgroundFloating</item>
<item name="panelColorBackground">?attr/colorBackgroundFloating</item>
- <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
- <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
- <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_light</item>
- <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
- <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_light</item>
- <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_light</item>
- <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_light</item>
- <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_light</item>
- <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
- <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
- <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
- <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
- <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
- <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
- <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
+ <item name="materialColorBackground">@color/system_background_light</item>
+ <item name="materialColorControlActivated">@color/system_control_activated_light</item>
+ <item name="materialColorControlHighlight">@color/system_control_highlight_light</item>
+ <item name="materialColorControlNormal">@color/system_control_normal_light</item>
+ <item name="materialColorError">@color/system_error_light</item>
<item name="materialColorErrorContainer">@color/system_error_container_light</item>
- <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
- <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
- <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
- <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
- <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
- <item name="materialColorPrimaryContainer">@color/system_primary_container_light</item>
+ <item name="materialColorInverseOnSurface">@color/system_inverse_on_surface_light</item>
+ <item name="materialColorInversePrimary">@color/system_inverse_primary_light</item>
+ <item name="materialColorInverseSurface">@color/system_inverse_surface_light</item>
<item name="materialColorOnBackground">@color/system_on_background_light</item>
- <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
- <item name="materialColorOnSecondary">@color/system_on_secondary_light</item>
- <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item>
- <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item>
- <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item>
<item name="materialColorOnError">@color/system_on_error_light</item>
- <item name="materialColorSurface">@color/system_surface_light</item>
- <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item>
- <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item>
+ <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
+ <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
+ <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_light</item>
+ <item name="materialColorOnSecondary">@color/system_on_secondary_light</item>
+ <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_light</item>
+ <item name="materialColorOnSurface">@color/system_on_surface_light</item>
<item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_light</item>
+ <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item>
+ <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_light</item>
<item name="materialColorOutline">@color/system_outline_light</item>
<item name="materialColorOutlineVariant">@color/system_outline_variant_light</item>
- <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
- <item name="materialColorOnSurface">@color/system_on_surface_light</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
+ <item name="materialColorPaletteKeyColorNeutral">@color/system_palette_key_color_neutral_light</item>
+ <item name="materialColorPaletteKeyColorNeutralVariant">@color/system_palette_key_color_neutral_variant_light</item>
+ <item name="materialColorPaletteKeyColorPrimary">@color/system_palette_key_color_primary_light</item>
+ <item name="materialColorPaletteKeyColorSecondary">@color/system_palette_key_color_secondary_light</item>
+ <item name="materialColorPaletteKeyColorTertiary">@color/system_palette_key_color_tertiary_light</item>
<item name="materialColorPrimary">@color/system_primary_light</item>
+ <item name="materialColorPrimaryContainer">@color/system_primary_container_light</item>
+ <item name="materialColorScrim">@color/system_scrim_light</item>
<item name="materialColorSecondary">@color/system_secondary_light</item>
+ <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
+ <item name="materialColorShadow">@color/system_shadow_light</item>
+ <item name="materialColorSurface">@color/system_surface_light</item>
+ <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item>
+ <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
+ <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item>
+ <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item>
+ <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_light</item>
+ <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_light</item>
+ <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item>
+ <item name="materialColorSurfaceTint">@color/system_surface_tint_light</item>
+ <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
<item name="materialColorTertiary">@color/system_tertiary_light</item>
- <item name="materialColorError">@color/system_error_light</item>
-
- <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
- <item name="customColorClockHour">@color/system_clock_hour_light</item>
- <item name="customColorClockMinute">@color/system_clock_minute_light</item>
- <item name="customColorClockSecond">@color/system_clock_second_light</item>
- <item name="customColorThemeApp">@color/system_theme_app_light</item>
- <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
- <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
+ <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
+ <item name="materialColorTextHintInverse">@color/system_text_hint_inverse_light</item>
+ <item name="materialColorTextPrimaryInverse">@color/system_text_primary_inverse_light</item>
+ <item name="materialColorTextPrimaryInverseDisableOnly">@color/system_text_primary_inverse_disable_only_light</item>
+ <item name="materialColorTextSecondaryAndTertiaryInverse">@color/system_text_secondary_and_tertiary_inverse_light</item>
+ <item name="materialColorTextSecondaryAndTertiaryInverseDisabled">@color/system_text_secondary_and_tertiary_inverse_disabled_light</item>
+ <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
+ <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
+ <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
+ <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
+ <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
+ <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
+ <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
+ <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
+ <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
+ <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
+ <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
+ <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="customColorBrandA">@color/system_brand_a_light</item>
<item name="customColorBrandB">@color/system_brand_b_light</item>
<item name="customColorBrandC">@color/system_brand_c_light</item>
<item name="customColorBrandD">@color/system_brand_d_light</item>
- <item name="customColorUnderSurface">@color/system_under_surface_light</item>
- <item name="customColorShadeActive">@color/system_shade_active_light</item>
+ <item name="customColorClockHour">@color/system_clock_hour_light</item>
+ <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+ <item name="customColorClockSecond">@color/system_clock_second_light</item>
<item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
<item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
- <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
<item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
<item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
- <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+ <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
<item name="customColorOverviewBackground">@color/system_overview_background_light</item>
+ <item name="customColorShadeActive">@color/system_shade_active_light</item>
+ <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+ <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+ <item name="customColorThemeApp">@color/system_theme_app_light</item>
+ <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
+ <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+ <item name="customColorWeatherTemp">@color/system_weather_temp_light</item>
+ <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
</style>
<!-- Variant of the DeviceDefault (light) theme that has a solid (opaque) action bar with an
@@ -2864,73 +3221,90 @@
<!-- Toolbar attributes -->
<item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
- <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
- <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
- <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_light</item>
- <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
- <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_light</item>
- <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_light</item>
- <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_light</item>
- <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_light</item>
- <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
- <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
- <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
- <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
- <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
- <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
- <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
+ <item name="materialColorBackground">@color/system_background_light</item>
+ <item name="materialColorControlActivated">@color/system_control_activated_light</item>
+ <item name="materialColorControlHighlight">@color/system_control_highlight_light</item>
+ <item name="materialColorControlNormal">@color/system_control_normal_light</item>
+ <item name="materialColorError">@color/system_error_light</item>
<item name="materialColorErrorContainer">@color/system_error_container_light</item>
- <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
- <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
- <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
- <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
- <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
- <item name="materialColorPrimaryContainer">@color/system_primary_container_light</item>
+ <item name="materialColorInverseOnSurface">@color/system_inverse_on_surface_light</item>
+ <item name="materialColorInversePrimary">@color/system_inverse_primary_light</item>
+ <item name="materialColorInverseSurface">@color/system_inverse_surface_light</item>
<item name="materialColorOnBackground">@color/system_on_background_light</item>
- <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
- <item name="materialColorOnSecondary">@color/system_on_secondary_light</item>
- <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item>
- <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item>
- <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item>
<item name="materialColorOnError">@color/system_on_error_light</item>
- <item name="materialColorSurface">@color/system_surface_light</item>
- <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item>
- <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item>
+ <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
+ <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
+ <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_light</item>
+ <item name="materialColorOnSecondary">@color/system_on_secondary_light</item>
+ <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_light</item>
+ <item name="materialColorOnSurface">@color/system_on_surface_light</item>
<item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_light</item>
+ <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item>
+ <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_light</item>
<item name="materialColorOutline">@color/system_outline_light</item>
<item name="materialColorOutlineVariant">@color/system_outline_variant_light</item>
- <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
- <item name="materialColorOnSurface">@color/system_on_surface_light</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
+ <item name="materialColorPaletteKeyColorNeutral">@color/system_palette_key_color_neutral_light</item>
+ <item name="materialColorPaletteKeyColorNeutralVariant">@color/system_palette_key_color_neutral_variant_light</item>
+ <item name="materialColorPaletteKeyColorPrimary">@color/system_palette_key_color_primary_light</item>
+ <item name="materialColorPaletteKeyColorSecondary">@color/system_palette_key_color_secondary_light</item>
+ <item name="materialColorPaletteKeyColorTertiary">@color/system_palette_key_color_tertiary_light</item>
<item name="materialColorPrimary">@color/system_primary_light</item>
+ <item name="materialColorPrimaryContainer">@color/system_primary_container_light</item>
+ <item name="materialColorScrim">@color/system_scrim_light</item>
<item name="materialColorSecondary">@color/system_secondary_light</item>
+ <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
+ <item name="materialColorShadow">@color/system_shadow_light</item>
+ <item name="materialColorSurface">@color/system_surface_light</item>
+ <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item>
+ <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
+ <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item>
+ <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item>
+ <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_light</item>
+ <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_light</item>
+ <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item>
+ <item name="materialColorSurfaceTint">@color/system_surface_tint_light</item>
+ <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
<item name="materialColorTertiary">@color/system_tertiary_light</item>
- <item name="materialColorError">@color/system_error_light</item>
-
- <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
- <item name="customColorClockHour">@color/system_clock_hour_light</item>
- <item name="customColorClockMinute">@color/system_clock_minute_light</item>
- <item name="customColorClockSecond">@color/system_clock_second_light</item>
- <item name="customColorThemeApp">@color/system_theme_app_light</item>
- <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
- <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
+ <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
+ <item name="materialColorTextHintInverse">@color/system_text_hint_inverse_light</item>
+ <item name="materialColorTextPrimaryInverse">@color/system_text_primary_inverse_light</item>
+ <item name="materialColorTextPrimaryInverseDisableOnly">@color/system_text_primary_inverse_disable_only_light</item>
+ <item name="materialColorTextSecondaryAndTertiaryInverse">@color/system_text_secondary_and_tertiary_inverse_light</item>
+ <item name="materialColorTextSecondaryAndTertiaryInverseDisabled">@color/system_text_secondary_and_tertiary_inverse_disabled_light</item>
+ <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
+ <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
+ <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
+ <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
+ <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
+ <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
+ <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
+ <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
+ <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
+ <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
+ <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
+ <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="customColorBrandA">@color/system_brand_a_light</item>
<item name="customColorBrandB">@color/system_brand_b_light</item>
<item name="customColorBrandC">@color/system_brand_c_light</item>
<item name="customColorBrandD">@color/system_brand_d_light</item>
- <item name="customColorUnderSurface">@color/system_under_surface_light</item>
- <item name="customColorShadeActive">@color/system_shade_active_light</item>
+ <item name="customColorClockHour">@color/system_clock_hour_light</item>
+ <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+ <item name="customColorClockSecond">@color/system_clock_second_light</item>
<item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
<item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
- <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
<item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
<item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
- <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+ <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
<item name="customColorOverviewBackground">@color/system_overview_background_light</item>
+ <item name="customColorShadeActive">@color/system_shade_active_light</item>
+ <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+ <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+ <item name="customColorThemeApp">@color/system_theme_app_light</item>
+ <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
+ <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+ <item name="customColorWeatherTemp">@color/system_weather_temp_light</item>
+ <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault_Light} with no action bar -->
@@ -2980,73 +3354,90 @@
<!-- Toolbar attributes -->
<item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
- <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
- <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
- <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_light</item>
- <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
- <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_light</item>
- <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_light</item>
- <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_light</item>
- <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_light</item>
- <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
- <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
- <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
- <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
- <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
- <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
- <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
+ <item name="materialColorBackground">@color/system_background_light</item>
+ <item name="materialColorControlActivated">@color/system_control_activated_light</item>
+ <item name="materialColorControlHighlight">@color/system_control_highlight_light</item>
+ <item name="materialColorControlNormal">@color/system_control_normal_light</item>
+ <item name="materialColorError">@color/system_error_light</item>
<item name="materialColorErrorContainer">@color/system_error_container_light</item>
- <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
- <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
- <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
- <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
- <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
- <item name="materialColorPrimaryContainer">@color/system_primary_container_light</item>
+ <item name="materialColorInverseOnSurface">@color/system_inverse_on_surface_light</item>
+ <item name="materialColorInversePrimary">@color/system_inverse_primary_light</item>
+ <item name="materialColorInverseSurface">@color/system_inverse_surface_light</item>
<item name="materialColorOnBackground">@color/system_on_background_light</item>
- <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
- <item name="materialColorOnSecondary">@color/system_on_secondary_light</item>
- <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item>
- <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item>
- <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item>
<item name="materialColorOnError">@color/system_on_error_light</item>
- <item name="materialColorSurface">@color/system_surface_light</item>
- <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item>
- <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item>
+ <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
+ <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
+ <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_light</item>
+ <item name="materialColorOnSecondary">@color/system_on_secondary_light</item>
+ <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_light</item>
+ <item name="materialColorOnSurface">@color/system_on_surface_light</item>
<item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_light</item>
+ <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item>
+ <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_light</item>
<item name="materialColorOutline">@color/system_outline_light</item>
<item name="materialColorOutlineVariant">@color/system_outline_variant_light</item>
- <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
- <item name="materialColorOnSurface">@color/system_on_surface_light</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
+ <item name="materialColorPaletteKeyColorNeutral">@color/system_palette_key_color_neutral_light</item>
+ <item name="materialColorPaletteKeyColorNeutralVariant">@color/system_palette_key_color_neutral_variant_light</item>
+ <item name="materialColorPaletteKeyColorPrimary">@color/system_palette_key_color_primary_light</item>
+ <item name="materialColorPaletteKeyColorSecondary">@color/system_palette_key_color_secondary_light</item>
+ <item name="materialColorPaletteKeyColorTertiary">@color/system_palette_key_color_tertiary_light</item>
<item name="materialColorPrimary">@color/system_primary_light</item>
+ <item name="materialColorPrimaryContainer">@color/system_primary_container_light</item>
+ <item name="materialColorScrim">@color/system_scrim_light</item>
<item name="materialColorSecondary">@color/system_secondary_light</item>
+ <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
+ <item name="materialColorShadow">@color/system_shadow_light</item>
+ <item name="materialColorSurface">@color/system_surface_light</item>
+ <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item>
+ <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
+ <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item>
+ <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item>
+ <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_light</item>
+ <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_light</item>
+ <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item>
+ <item name="materialColorSurfaceTint">@color/system_surface_tint_light</item>
+ <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
<item name="materialColorTertiary">@color/system_tertiary_light</item>
- <item name="materialColorError">@color/system_error_light</item>
-
- <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
- <item name="customColorClockHour">@color/system_clock_hour_light</item>
- <item name="customColorClockMinute">@color/system_clock_minute_light</item>
- <item name="customColorClockSecond">@color/system_clock_second_light</item>
- <item name="customColorThemeApp">@color/system_theme_app_light</item>
- <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
- <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
+ <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
+ <item name="materialColorTextHintInverse">@color/system_text_hint_inverse_light</item>
+ <item name="materialColorTextPrimaryInverse">@color/system_text_primary_inverse_light</item>
+ <item name="materialColorTextPrimaryInverseDisableOnly">@color/system_text_primary_inverse_disable_only_light</item>
+ <item name="materialColorTextSecondaryAndTertiaryInverse">@color/system_text_secondary_and_tertiary_inverse_light</item>
+ <item name="materialColorTextSecondaryAndTertiaryInverseDisabled">@color/system_text_secondary_and_tertiary_inverse_disabled_light</item>
+ <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
+ <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
+ <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
+ <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
+ <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
+ <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
+ <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
+ <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
+ <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
+ <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
+ <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
+ <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="customColorBrandA">@color/system_brand_a_light</item>
<item name="customColorBrandB">@color/system_brand_b_light</item>
<item name="customColorBrandC">@color/system_brand_c_light</item>
<item name="customColorBrandD">@color/system_brand_d_light</item>
- <item name="customColorUnderSurface">@color/system_under_surface_light</item>
- <item name="customColorShadeActive">@color/system_shade_active_light</item>
+ <item name="customColorClockHour">@color/system_clock_hour_light</item>
+ <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+ <item name="customColorClockSecond">@color/system_clock_second_light</item>
<item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
<item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
- <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
<item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
<item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
- <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+ <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
<item name="customColorOverviewBackground">@color/system_overview_background_light</item>
+ <item name="customColorShadeActive">@color/system_shade_active_light</item>
+ <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+ <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+ <item name="customColorThemeApp">@color/system_theme_app_light</item>
+ <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
+ <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+ <item name="customColorWeatherTemp">@color/system_weather_temp_light</item>
+ <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault_Light} with no action bar and no status bar.
@@ -3097,73 +3488,90 @@
<!-- Toolbar attributes -->
<item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
- <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
- <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
- <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_light</item>
- <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
- <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_light</item>
- <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_light</item>
- <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_light</item>
- <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_light</item>
- <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
- <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
- <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
- <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
- <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
- <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
- <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
+ <item name="materialColorBackground">@color/system_background_light</item>
+ <item name="materialColorControlActivated">@color/system_control_activated_light</item>
+ <item name="materialColorControlHighlight">@color/system_control_highlight_light</item>
+ <item name="materialColorControlNormal">@color/system_control_normal_light</item>
+ <item name="materialColorError">@color/system_error_light</item>
<item name="materialColorErrorContainer">@color/system_error_container_light</item>
- <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
- <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
- <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
- <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
- <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
- <item name="materialColorPrimaryContainer">@color/system_primary_container_light</item>
+ <item name="materialColorInverseOnSurface">@color/system_inverse_on_surface_light</item>
+ <item name="materialColorInversePrimary">@color/system_inverse_primary_light</item>
+ <item name="materialColorInverseSurface">@color/system_inverse_surface_light</item>
<item name="materialColorOnBackground">@color/system_on_background_light</item>
- <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
- <item name="materialColorOnSecondary">@color/system_on_secondary_light</item>
- <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item>
- <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item>
- <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item>
<item name="materialColorOnError">@color/system_on_error_light</item>
- <item name="materialColorSurface">@color/system_surface_light</item>
- <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item>
- <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item>
+ <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
+ <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
+ <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_light</item>
+ <item name="materialColorOnSecondary">@color/system_on_secondary_light</item>
+ <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_light</item>
+ <item name="materialColorOnSurface">@color/system_on_surface_light</item>
<item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_light</item>
+ <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item>
+ <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_light</item>
<item name="materialColorOutline">@color/system_outline_light</item>
<item name="materialColorOutlineVariant">@color/system_outline_variant_light</item>
- <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
- <item name="materialColorOnSurface">@color/system_on_surface_light</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
+ <item name="materialColorPaletteKeyColorNeutral">@color/system_palette_key_color_neutral_light</item>
+ <item name="materialColorPaletteKeyColorNeutralVariant">@color/system_palette_key_color_neutral_variant_light</item>
+ <item name="materialColorPaletteKeyColorPrimary">@color/system_palette_key_color_primary_light</item>
+ <item name="materialColorPaletteKeyColorSecondary">@color/system_palette_key_color_secondary_light</item>
+ <item name="materialColorPaletteKeyColorTertiary">@color/system_palette_key_color_tertiary_light</item>
<item name="materialColorPrimary">@color/system_primary_light</item>
+ <item name="materialColorPrimaryContainer">@color/system_primary_container_light</item>
+ <item name="materialColorScrim">@color/system_scrim_light</item>
<item name="materialColorSecondary">@color/system_secondary_light</item>
+ <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
+ <item name="materialColorShadow">@color/system_shadow_light</item>
+ <item name="materialColorSurface">@color/system_surface_light</item>
+ <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item>
+ <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
+ <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item>
+ <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item>
+ <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_light</item>
+ <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_light</item>
+ <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item>
+ <item name="materialColorSurfaceTint">@color/system_surface_tint_light</item>
+ <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
<item name="materialColorTertiary">@color/system_tertiary_light</item>
- <item name="materialColorError">@color/system_error_light</item>
-
- <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
- <item name="customColorClockHour">@color/system_clock_hour_light</item>
- <item name="customColorClockMinute">@color/system_clock_minute_light</item>
- <item name="customColorClockSecond">@color/system_clock_second_light</item>
- <item name="customColorThemeApp">@color/system_theme_app_light</item>
- <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
- <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
+ <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
+ <item name="materialColorTextHintInverse">@color/system_text_hint_inverse_light</item>
+ <item name="materialColorTextPrimaryInverse">@color/system_text_primary_inverse_light</item>
+ <item name="materialColorTextPrimaryInverseDisableOnly">@color/system_text_primary_inverse_disable_only_light</item>
+ <item name="materialColorTextSecondaryAndTertiaryInverse">@color/system_text_secondary_and_tertiary_inverse_light</item>
+ <item name="materialColorTextSecondaryAndTertiaryInverseDisabled">@color/system_text_secondary_and_tertiary_inverse_disabled_light</item>
+ <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
+ <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
+ <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
+ <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
+ <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
+ <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
+ <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
+ <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
+ <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
+ <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
+ <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
+ <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="customColorBrandA">@color/system_brand_a_light</item>
<item name="customColorBrandB">@color/system_brand_b_light</item>
<item name="customColorBrandC">@color/system_brand_c_light</item>
<item name="customColorBrandD">@color/system_brand_d_light</item>
- <item name="customColorUnderSurface">@color/system_under_surface_light</item>
- <item name="customColorShadeActive">@color/system_shade_active_light</item>
+ <item name="customColorClockHour">@color/system_clock_hour_light</item>
+ <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+ <item name="customColorClockSecond">@color/system_clock_second_light</item>
<item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
<item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
- <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
<item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
<item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
- <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+ <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
<item name="customColorOverviewBackground">@color/system_overview_background_light</item>
+ <item name="customColorShadeActive">@color/system_shade_active_light</item>
+ <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+ <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+ <item name="customColorThemeApp">@color/system_theme_app_light</item>
+ <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
+ <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+ <item name="customColorWeatherTemp">@color/system_weather_temp_light</item>
+ <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault_Light} with no action bar and no status bar
@@ -3216,73 +3624,90 @@
<!-- Toolbar attributes -->
<item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
- <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
- <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
- <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_light</item>
- <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
- <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_light</item>
- <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_light</item>
- <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_light</item>
- <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_light</item>
- <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
- <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
- <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
- <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
- <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
- <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
- <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
+ <item name="materialColorBackground">@color/system_background_light</item>
+ <item name="materialColorControlActivated">@color/system_control_activated_light</item>
+ <item name="materialColorControlHighlight">@color/system_control_highlight_light</item>
+ <item name="materialColorControlNormal">@color/system_control_normal_light</item>
+ <item name="materialColorError">@color/system_error_light</item>
<item name="materialColorErrorContainer">@color/system_error_container_light</item>
- <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
- <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
- <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
- <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
- <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
- <item name="materialColorPrimaryContainer">@color/system_primary_container_light</item>
+ <item name="materialColorInverseOnSurface">@color/system_inverse_on_surface_light</item>
+ <item name="materialColorInversePrimary">@color/system_inverse_primary_light</item>
+ <item name="materialColorInverseSurface">@color/system_inverse_surface_light</item>
<item name="materialColorOnBackground">@color/system_on_background_light</item>
- <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
- <item name="materialColorOnSecondary">@color/system_on_secondary_light</item>
- <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item>
- <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item>
- <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item>
<item name="materialColorOnError">@color/system_on_error_light</item>
- <item name="materialColorSurface">@color/system_surface_light</item>
- <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item>
- <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item>
+ <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
+ <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
+ <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_light</item>
+ <item name="materialColorOnSecondary">@color/system_on_secondary_light</item>
+ <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_light</item>
+ <item name="materialColorOnSurface">@color/system_on_surface_light</item>
<item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_light</item>
+ <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item>
+ <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_light</item>
<item name="materialColorOutline">@color/system_outline_light</item>
<item name="materialColorOutlineVariant">@color/system_outline_variant_light</item>
- <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
- <item name="materialColorOnSurface">@color/system_on_surface_light</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
+ <item name="materialColorPaletteKeyColorNeutral">@color/system_palette_key_color_neutral_light</item>
+ <item name="materialColorPaletteKeyColorNeutralVariant">@color/system_palette_key_color_neutral_variant_light</item>
+ <item name="materialColorPaletteKeyColorPrimary">@color/system_palette_key_color_primary_light</item>
+ <item name="materialColorPaletteKeyColorSecondary">@color/system_palette_key_color_secondary_light</item>
+ <item name="materialColorPaletteKeyColorTertiary">@color/system_palette_key_color_tertiary_light</item>
<item name="materialColorPrimary">@color/system_primary_light</item>
+ <item name="materialColorPrimaryContainer">@color/system_primary_container_light</item>
+ <item name="materialColorScrim">@color/system_scrim_light</item>
<item name="materialColorSecondary">@color/system_secondary_light</item>
+ <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
+ <item name="materialColorShadow">@color/system_shadow_light</item>
+ <item name="materialColorSurface">@color/system_surface_light</item>
+ <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item>
+ <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
+ <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item>
+ <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item>
+ <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_light</item>
+ <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_light</item>
+ <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item>
+ <item name="materialColorSurfaceTint">@color/system_surface_tint_light</item>
+ <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
<item name="materialColorTertiary">@color/system_tertiary_light</item>
- <item name="materialColorError">@color/system_error_light</item>
-
- <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
- <item name="customColorClockHour">@color/system_clock_hour_light</item>
- <item name="customColorClockMinute">@color/system_clock_minute_light</item>
- <item name="customColorClockSecond">@color/system_clock_second_light</item>
- <item name="customColorThemeApp">@color/system_theme_app_light</item>
- <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
- <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
+ <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
+ <item name="materialColorTextHintInverse">@color/system_text_hint_inverse_light</item>
+ <item name="materialColorTextPrimaryInverse">@color/system_text_primary_inverse_light</item>
+ <item name="materialColorTextPrimaryInverseDisableOnly">@color/system_text_primary_inverse_disable_only_light</item>
+ <item name="materialColorTextSecondaryAndTertiaryInverse">@color/system_text_secondary_and_tertiary_inverse_light</item>
+ <item name="materialColorTextSecondaryAndTertiaryInverseDisabled">@color/system_text_secondary_and_tertiary_inverse_disabled_light</item>
+ <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
+ <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
+ <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
+ <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
+ <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
+ <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
+ <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
+ <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
+ <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
+ <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
+ <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
+ <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="customColorBrandA">@color/system_brand_a_light</item>
<item name="customColorBrandB">@color/system_brand_b_light</item>
<item name="customColorBrandC">@color/system_brand_c_light</item>
<item name="customColorBrandD">@color/system_brand_d_light</item>
- <item name="customColorUnderSurface">@color/system_under_surface_light</item>
- <item name="customColorShadeActive">@color/system_shade_active_light</item>
+ <item name="customColorClockHour">@color/system_clock_hour_light</item>
+ <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+ <item name="customColorClockSecond">@color/system_clock_second_light</item>
<item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
<item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
- <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
<item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
<item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
- <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+ <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
<item name="customColorOverviewBackground">@color/system_overview_background_light</item>
+ <item name="customColorShadeActive">@color/system_shade_active_light</item>
+ <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+ <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+ <item name="customColorThemeApp">@color/system_theme_app_light</item>
+ <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
+ <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+ <item name="customColorWeatherTemp">@color/system_weather_temp_light</item>
+ <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault_Light} that has no title bar and translucent
@@ -3334,73 +3759,90 @@
<!-- Toolbar attributes -->
<item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
- <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
- <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
- <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_light</item>
- <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
- <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_light</item>
- <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_light</item>
- <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_light</item>
- <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_light</item>
- <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
- <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
- <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
- <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
- <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
- <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
- <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
+ <item name="materialColorBackground">@color/system_background_light</item>
+ <item name="materialColorControlActivated">@color/system_control_activated_light</item>
+ <item name="materialColorControlHighlight">@color/system_control_highlight_light</item>
+ <item name="materialColorControlNormal">@color/system_control_normal_light</item>
+ <item name="materialColorError">@color/system_error_light</item>
<item name="materialColorErrorContainer">@color/system_error_container_light</item>
- <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
- <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
- <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
- <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
- <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
- <item name="materialColorPrimaryContainer">@color/system_primary_container_light</item>
+ <item name="materialColorInverseOnSurface">@color/system_inverse_on_surface_light</item>
+ <item name="materialColorInversePrimary">@color/system_inverse_primary_light</item>
+ <item name="materialColorInverseSurface">@color/system_inverse_surface_light</item>
<item name="materialColorOnBackground">@color/system_on_background_light</item>
- <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
- <item name="materialColorOnSecondary">@color/system_on_secondary_light</item>
- <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item>
- <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item>
- <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item>
<item name="materialColorOnError">@color/system_on_error_light</item>
- <item name="materialColorSurface">@color/system_surface_light</item>
- <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item>
- <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item>
+ <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
+ <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
+ <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_light</item>
+ <item name="materialColorOnSecondary">@color/system_on_secondary_light</item>
+ <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_light</item>
+ <item name="materialColorOnSurface">@color/system_on_surface_light</item>
<item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_light</item>
+ <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item>
+ <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_light</item>
<item name="materialColorOutline">@color/system_outline_light</item>
<item name="materialColorOutlineVariant">@color/system_outline_variant_light</item>
- <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
- <item name="materialColorOnSurface">@color/system_on_surface_light</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
+ <item name="materialColorPaletteKeyColorNeutral">@color/system_palette_key_color_neutral_light</item>
+ <item name="materialColorPaletteKeyColorNeutralVariant">@color/system_palette_key_color_neutral_variant_light</item>
+ <item name="materialColorPaletteKeyColorPrimary">@color/system_palette_key_color_primary_light</item>
+ <item name="materialColorPaletteKeyColorSecondary">@color/system_palette_key_color_secondary_light</item>
+ <item name="materialColorPaletteKeyColorTertiary">@color/system_palette_key_color_tertiary_light</item>
<item name="materialColorPrimary">@color/system_primary_light</item>
+ <item name="materialColorPrimaryContainer">@color/system_primary_container_light</item>
+ <item name="materialColorScrim">@color/system_scrim_light</item>
<item name="materialColorSecondary">@color/system_secondary_light</item>
+ <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
+ <item name="materialColorShadow">@color/system_shadow_light</item>
+ <item name="materialColorSurface">@color/system_surface_light</item>
+ <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item>
+ <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
+ <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item>
+ <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item>
+ <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_light</item>
+ <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_light</item>
+ <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item>
+ <item name="materialColorSurfaceTint">@color/system_surface_tint_light</item>
+ <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
<item name="materialColorTertiary">@color/system_tertiary_light</item>
- <item name="materialColorError">@color/system_error_light</item>
-
- <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
- <item name="customColorClockHour">@color/system_clock_hour_light</item>
- <item name="customColorClockMinute">@color/system_clock_minute_light</item>
- <item name="customColorClockSecond">@color/system_clock_second_light</item>
- <item name="customColorThemeApp">@color/system_theme_app_light</item>
- <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
- <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
+ <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
+ <item name="materialColorTextHintInverse">@color/system_text_hint_inverse_light</item>
+ <item name="materialColorTextPrimaryInverse">@color/system_text_primary_inverse_light</item>
+ <item name="materialColorTextPrimaryInverseDisableOnly">@color/system_text_primary_inverse_disable_only_light</item>
+ <item name="materialColorTextSecondaryAndTertiaryInverse">@color/system_text_secondary_and_tertiary_inverse_light</item>
+ <item name="materialColorTextSecondaryAndTertiaryInverseDisabled">@color/system_text_secondary_and_tertiary_inverse_disabled_light</item>
+ <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
+ <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
+ <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
+ <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
+ <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
+ <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
+ <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
+ <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
+ <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
+ <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
+ <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
+ <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="customColorBrandA">@color/system_brand_a_light</item>
<item name="customColorBrandB">@color/system_brand_b_light</item>
<item name="customColorBrandC">@color/system_brand_c_light</item>
<item name="customColorBrandD">@color/system_brand_d_light</item>
- <item name="customColorUnderSurface">@color/system_under_surface_light</item>
- <item name="customColorShadeActive">@color/system_shade_active_light</item>
+ <item name="customColorClockHour">@color/system_clock_hour_light</item>
+ <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+ <item name="customColorClockSecond">@color/system_clock_second_light</item>
<item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
<item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
- <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
<item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
<item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
- <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+ <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
<item name="customColorOverviewBackground">@color/system_overview_background_light</item>
+ <item name="customColorShadeActive">@color/system_shade_active_light</item>
+ <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+ <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+ <item name="customColorThemeApp">@color/system_theme_app_light</item>
+ <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
+ <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+ <item name="customColorWeatherTemp">@color/system_weather_temp_light</item>
+ <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
</style>
<!-- DeviceDefault light theme for dialog windows and activities. This changes the window to be
@@ -3458,73 +3900,90 @@
<!-- Toolbar attributes -->
<item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
- <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
- <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
- <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_light</item>
- <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
- <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_light</item>
- <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_light</item>
- <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_light</item>
- <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_light</item>
- <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
- <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
- <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
- <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
- <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
- <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
- <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
+ <item name="materialColorBackground">@color/system_background_light</item>
+ <item name="materialColorControlActivated">@color/system_control_activated_light</item>
+ <item name="materialColorControlHighlight">@color/system_control_highlight_light</item>
+ <item name="materialColorControlNormal">@color/system_control_normal_light</item>
+ <item name="materialColorError">@color/system_error_light</item>
<item name="materialColorErrorContainer">@color/system_error_container_light</item>
- <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
- <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
- <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
- <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
- <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
- <item name="materialColorPrimaryContainer">@color/system_primary_container_light</item>
+ <item name="materialColorInverseOnSurface">@color/system_inverse_on_surface_light</item>
+ <item name="materialColorInversePrimary">@color/system_inverse_primary_light</item>
+ <item name="materialColorInverseSurface">@color/system_inverse_surface_light</item>
<item name="materialColorOnBackground">@color/system_on_background_light</item>
- <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
- <item name="materialColorOnSecondary">@color/system_on_secondary_light</item>
- <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item>
- <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item>
- <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item>
<item name="materialColorOnError">@color/system_on_error_light</item>
- <item name="materialColorSurface">@color/system_surface_light</item>
- <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item>
- <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item>
+ <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
+ <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
+ <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_light</item>
+ <item name="materialColorOnSecondary">@color/system_on_secondary_light</item>
+ <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_light</item>
+ <item name="materialColorOnSurface">@color/system_on_surface_light</item>
<item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_light</item>
+ <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item>
+ <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_light</item>
<item name="materialColorOutline">@color/system_outline_light</item>
<item name="materialColorOutlineVariant">@color/system_outline_variant_light</item>
- <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
- <item name="materialColorOnSurface">@color/system_on_surface_light</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
+ <item name="materialColorPaletteKeyColorNeutral">@color/system_palette_key_color_neutral_light</item>
+ <item name="materialColorPaletteKeyColorNeutralVariant">@color/system_palette_key_color_neutral_variant_light</item>
+ <item name="materialColorPaletteKeyColorPrimary">@color/system_palette_key_color_primary_light</item>
+ <item name="materialColorPaletteKeyColorSecondary">@color/system_palette_key_color_secondary_light</item>
+ <item name="materialColorPaletteKeyColorTertiary">@color/system_palette_key_color_tertiary_light</item>
<item name="materialColorPrimary">@color/system_primary_light</item>
+ <item name="materialColorPrimaryContainer">@color/system_primary_container_light</item>
+ <item name="materialColorScrim">@color/system_scrim_light</item>
<item name="materialColorSecondary">@color/system_secondary_light</item>
+ <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
+ <item name="materialColorShadow">@color/system_shadow_light</item>
+ <item name="materialColorSurface">@color/system_surface_light</item>
+ <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item>
+ <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
+ <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item>
+ <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item>
+ <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_light</item>
+ <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_light</item>
+ <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item>
+ <item name="materialColorSurfaceTint">@color/system_surface_tint_light</item>
+ <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
<item name="materialColorTertiary">@color/system_tertiary_light</item>
- <item name="materialColorError">@color/system_error_light</item>
-
- <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
- <item name="customColorClockHour">@color/system_clock_hour_light</item>
- <item name="customColorClockMinute">@color/system_clock_minute_light</item>
- <item name="customColorClockSecond">@color/system_clock_second_light</item>
- <item name="customColorThemeApp">@color/system_theme_app_light</item>
- <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
- <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
+ <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
+ <item name="materialColorTextHintInverse">@color/system_text_hint_inverse_light</item>
+ <item name="materialColorTextPrimaryInverse">@color/system_text_primary_inverse_light</item>
+ <item name="materialColorTextPrimaryInverseDisableOnly">@color/system_text_primary_inverse_disable_only_light</item>
+ <item name="materialColorTextSecondaryAndTertiaryInverse">@color/system_text_secondary_and_tertiary_inverse_light</item>
+ <item name="materialColorTextSecondaryAndTertiaryInverseDisabled">@color/system_text_secondary_and_tertiary_inverse_disabled_light</item>
+ <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
+ <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
+ <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
+ <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
+ <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
+ <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
+ <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
+ <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
+ <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
+ <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
+ <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
+ <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="customColorBrandA">@color/system_brand_a_light</item>
<item name="customColorBrandB">@color/system_brand_b_light</item>
<item name="customColorBrandC">@color/system_brand_c_light</item>
<item name="customColorBrandD">@color/system_brand_d_light</item>
- <item name="customColorUnderSurface">@color/system_under_surface_light</item>
- <item name="customColorShadeActive">@color/system_shade_active_light</item>
+ <item name="customColorClockHour">@color/system_clock_hour_light</item>
+ <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+ <item name="customColorClockSecond">@color/system_clock_second_light</item>
<item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
<item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
- <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
<item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
<item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
- <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+ <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
<item name="customColorOverviewBackground">@color/system_overview_background_light</item>
+ <item name="customColorShadeActive">@color/system_shade_active_light</item>
+ <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+ <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+ <item name="customColorThemeApp">@color/system_theme_app_light</item>
+ <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
+ <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+ <item name="customColorWeatherTemp">@color/system_weather_temp_light</item>
+ <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault_Light_Dialog} that has a nice minimum width for a
@@ -3578,73 +4037,90 @@
<!-- Toolbar attributes -->
<item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
- <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
- <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
- <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_light</item>
- <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
- <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_light</item>
- <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_light</item>
- <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_light</item>
- <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_light</item>
- <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
- <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
- <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
- <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
- <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
- <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
- <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
+ <item name="materialColorBackground">@color/system_background_light</item>
+ <item name="materialColorControlActivated">@color/system_control_activated_light</item>
+ <item name="materialColorControlHighlight">@color/system_control_highlight_light</item>
+ <item name="materialColorControlNormal">@color/system_control_normal_light</item>
+ <item name="materialColorError">@color/system_error_light</item>
<item name="materialColorErrorContainer">@color/system_error_container_light</item>
- <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
- <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
- <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
- <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
- <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
- <item name="materialColorPrimaryContainer">@color/system_primary_container_light</item>
+ <item name="materialColorInverseOnSurface">@color/system_inverse_on_surface_light</item>
+ <item name="materialColorInversePrimary">@color/system_inverse_primary_light</item>
+ <item name="materialColorInverseSurface">@color/system_inverse_surface_light</item>
<item name="materialColorOnBackground">@color/system_on_background_light</item>
- <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
- <item name="materialColorOnSecondary">@color/system_on_secondary_light</item>
- <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item>
- <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item>
- <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item>
<item name="materialColorOnError">@color/system_on_error_light</item>
- <item name="materialColorSurface">@color/system_surface_light</item>
- <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item>
- <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item>
+ <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
+ <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
+ <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_light</item>
+ <item name="materialColorOnSecondary">@color/system_on_secondary_light</item>
+ <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_light</item>
+ <item name="materialColorOnSurface">@color/system_on_surface_light</item>
<item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_light</item>
+ <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item>
+ <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_light</item>
<item name="materialColorOutline">@color/system_outline_light</item>
<item name="materialColorOutlineVariant">@color/system_outline_variant_light</item>
- <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
- <item name="materialColorOnSurface">@color/system_on_surface_light</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
+ <item name="materialColorPaletteKeyColorNeutral">@color/system_palette_key_color_neutral_light</item>
+ <item name="materialColorPaletteKeyColorNeutralVariant">@color/system_palette_key_color_neutral_variant_light</item>
+ <item name="materialColorPaletteKeyColorPrimary">@color/system_palette_key_color_primary_light</item>
+ <item name="materialColorPaletteKeyColorSecondary">@color/system_palette_key_color_secondary_light</item>
+ <item name="materialColorPaletteKeyColorTertiary">@color/system_palette_key_color_tertiary_light</item>
<item name="materialColorPrimary">@color/system_primary_light</item>
+ <item name="materialColorPrimaryContainer">@color/system_primary_container_light</item>
+ <item name="materialColorScrim">@color/system_scrim_light</item>
<item name="materialColorSecondary">@color/system_secondary_light</item>
+ <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
+ <item name="materialColorShadow">@color/system_shadow_light</item>
+ <item name="materialColorSurface">@color/system_surface_light</item>
+ <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item>
+ <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
+ <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item>
+ <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item>
+ <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_light</item>
+ <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_light</item>
+ <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item>
+ <item name="materialColorSurfaceTint">@color/system_surface_tint_light</item>
+ <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
<item name="materialColorTertiary">@color/system_tertiary_light</item>
- <item name="materialColorError">@color/system_error_light</item>
-
- <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
- <item name="customColorClockHour">@color/system_clock_hour_light</item>
- <item name="customColorClockMinute">@color/system_clock_minute_light</item>
- <item name="customColorClockSecond">@color/system_clock_second_light</item>
- <item name="customColorThemeApp">@color/system_theme_app_light</item>
- <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
- <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
+ <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
+ <item name="materialColorTextHintInverse">@color/system_text_hint_inverse_light</item>
+ <item name="materialColorTextPrimaryInverse">@color/system_text_primary_inverse_light</item>
+ <item name="materialColorTextPrimaryInverseDisableOnly">@color/system_text_primary_inverse_disable_only_light</item>
+ <item name="materialColorTextSecondaryAndTertiaryInverse">@color/system_text_secondary_and_tertiary_inverse_light</item>
+ <item name="materialColorTextSecondaryAndTertiaryInverseDisabled">@color/system_text_secondary_and_tertiary_inverse_disabled_light</item>
+ <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
+ <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
+ <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
+ <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
+ <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
+ <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
+ <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
+ <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
+ <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
+ <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
+ <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
+ <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="customColorBrandA">@color/system_brand_a_light</item>
<item name="customColorBrandB">@color/system_brand_b_light</item>
<item name="customColorBrandC">@color/system_brand_c_light</item>
<item name="customColorBrandD">@color/system_brand_d_light</item>
- <item name="customColorUnderSurface">@color/system_under_surface_light</item>
- <item name="customColorShadeActive">@color/system_shade_active_light</item>
+ <item name="customColorClockHour">@color/system_clock_hour_light</item>
+ <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+ <item name="customColorClockSecond">@color/system_clock_second_light</item>
<item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
<item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
- <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
<item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
<item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
- <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+ <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
<item name="customColorOverviewBackground">@color/system_overview_background_light</item>
+ <item name="customColorShadeActive">@color/system_shade_active_light</item>
+ <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+ <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+ <item name="customColorThemeApp">@color/system_theme_app_light</item>
+ <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
+ <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+ <item name="customColorWeatherTemp">@color/system_weather_temp_light</item>
+ <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault_Light_Dialog} without an action bar -->
@@ -3697,73 +4173,90 @@
<!-- Toolbar attributes -->
<item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
- <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
- <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
- <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_light</item>
- <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
- <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_light</item>
- <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_light</item>
- <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_light</item>
- <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_light</item>
- <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
- <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
- <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
- <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
- <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
- <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
- <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
+ <item name="materialColorBackground">@color/system_background_light</item>
+ <item name="materialColorControlActivated">@color/system_control_activated_light</item>
+ <item name="materialColorControlHighlight">@color/system_control_highlight_light</item>
+ <item name="materialColorControlNormal">@color/system_control_normal_light</item>
+ <item name="materialColorError">@color/system_error_light</item>
<item name="materialColorErrorContainer">@color/system_error_container_light</item>
- <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
- <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
- <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
- <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
- <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
- <item name="materialColorPrimaryContainer">@color/system_primary_container_light</item>
+ <item name="materialColorInverseOnSurface">@color/system_inverse_on_surface_light</item>
+ <item name="materialColorInversePrimary">@color/system_inverse_primary_light</item>
+ <item name="materialColorInverseSurface">@color/system_inverse_surface_light</item>
<item name="materialColorOnBackground">@color/system_on_background_light</item>
- <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
- <item name="materialColorOnSecondary">@color/system_on_secondary_light</item>
- <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item>
- <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item>
- <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item>
<item name="materialColorOnError">@color/system_on_error_light</item>
- <item name="materialColorSurface">@color/system_surface_light</item>
- <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item>
- <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item>
+ <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
+ <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
+ <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_light</item>
+ <item name="materialColorOnSecondary">@color/system_on_secondary_light</item>
+ <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_light</item>
+ <item name="materialColorOnSurface">@color/system_on_surface_light</item>
<item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_light</item>
+ <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item>
+ <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_light</item>
<item name="materialColorOutline">@color/system_outline_light</item>
<item name="materialColorOutlineVariant">@color/system_outline_variant_light</item>
- <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
- <item name="materialColorOnSurface">@color/system_on_surface_light</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
+ <item name="materialColorPaletteKeyColorNeutral">@color/system_palette_key_color_neutral_light</item>
+ <item name="materialColorPaletteKeyColorNeutralVariant">@color/system_palette_key_color_neutral_variant_light</item>
+ <item name="materialColorPaletteKeyColorPrimary">@color/system_palette_key_color_primary_light</item>
+ <item name="materialColorPaletteKeyColorSecondary">@color/system_palette_key_color_secondary_light</item>
+ <item name="materialColorPaletteKeyColorTertiary">@color/system_palette_key_color_tertiary_light</item>
<item name="materialColorPrimary">@color/system_primary_light</item>
+ <item name="materialColorPrimaryContainer">@color/system_primary_container_light</item>
+ <item name="materialColorScrim">@color/system_scrim_light</item>
<item name="materialColorSecondary">@color/system_secondary_light</item>
+ <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
+ <item name="materialColorShadow">@color/system_shadow_light</item>
+ <item name="materialColorSurface">@color/system_surface_light</item>
+ <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item>
+ <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
+ <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item>
+ <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item>
+ <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_light</item>
+ <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_light</item>
+ <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item>
+ <item name="materialColorSurfaceTint">@color/system_surface_tint_light</item>
+ <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
<item name="materialColorTertiary">@color/system_tertiary_light</item>
- <item name="materialColorError">@color/system_error_light</item>
-
- <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
- <item name="customColorClockHour">@color/system_clock_hour_light</item>
- <item name="customColorClockMinute">@color/system_clock_minute_light</item>
- <item name="customColorClockSecond">@color/system_clock_second_light</item>
- <item name="customColorThemeApp">@color/system_theme_app_light</item>
- <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
- <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
+ <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
+ <item name="materialColorTextHintInverse">@color/system_text_hint_inverse_light</item>
+ <item name="materialColorTextPrimaryInverse">@color/system_text_primary_inverse_light</item>
+ <item name="materialColorTextPrimaryInverseDisableOnly">@color/system_text_primary_inverse_disable_only_light</item>
+ <item name="materialColorTextSecondaryAndTertiaryInverse">@color/system_text_secondary_and_tertiary_inverse_light</item>
+ <item name="materialColorTextSecondaryAndTertiaryInverseDisabled">@color/system_text_secondary_and_tertiary_inverse_disabled_light</item>
+ <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
+ <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
+ <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
+ <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
+ <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
+ <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
+ <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
+ <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
+ <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
+ <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
+ <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
+ <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="customColorBrandA">@color/system_brand_a_light</item>
<item name="customColorBrandB">@color/system_brand_b_light</item>
<item name="customColorBrandC">@color/system_brand_c_light</item>
<item name="customColorBrandD">@color/system_brand_d_light</item>
- <item name="customColorUnderSurface">@color/system_under_surface_light</item>
- <item name="customColorShadeActive">@color/system_shade_active_light</item>
+ <item name="customColorClockHour">@color/system_clock_hour_light</item>
+ <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+ <item name="customColorClockSecond">@color/system_clock_second_light</item>
<item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
<item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
- <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
<item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
<item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
- <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+ <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
<item name="customColorOverviewBackground">@color/system_overview_background_light</item>
+ <item name="customColorShadeActive">@color/system_shade_active_light</item>
+ <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+ <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+ <item name="customColorThemeApp">@color/system_theme_app_light</item>
+ <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
+ <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+ <item name="customColorWeatherTemp">@color/system_weather_temp_light</item>
+ <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault_Light_Dialog_NoActionBar} that has a nice minimum
@@ -3817,73 +4310,90 @@
<!-- Toolbar attributes -->
<item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
- <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
- <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
- <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_light</item>
- <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
- <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_light</item>
- <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_light</item>
- <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_light</item>
- <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_light</item>
- <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
- <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
- <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
- <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
- <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
- <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
- <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
+ <item name="materialColorBackground">@color/system_background_light</item>
+ <item name="materialColorControlActivated">@color/system_control_activated_light</item>
+ <item name="materialColorControlHighlight">@color/system_control_highlight_light</item>
+ <item name="materialColorControlNormal">@color/system_control_normal_light</item>
+ <item name="materialColorError">@color/system_error_light</item>
<item name="materialColorErrorContainer">@color/system_error_container_light</item>
- <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
- <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
- <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
- <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
- <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
- <item name="materialColorPrimaryContainer">@color/system_primary_container_light</item>
+ <item name="materialColorInverseOnSurface">@color/system_inverse_on_surface_light</item>
+ <item name="materialColorInversePrimary">@color/system_inverse_primary_light</item>
+ <item name="materialColorInverseSurface">@color/system_inverse_surface_light</item>
<item name="materialColorOnBackground">@color/system_on_background_light</item>
- <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
- <item name="materialColorOnSecondary">@color/system_on_secondary_light</item>
- <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item>
- <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item>
- <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item>
<item name="materialColorOnError">@color/system_on_error_light</item>
- <item name="materialColorSurface">@color/system_surface_light</item>
- <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item>
- <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item>
+ <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
+ <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
+ <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_light</item>
+ <item name="materialColorOnSecondary">@color/system_on_secondary_light</item>
+ <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_light</item>
+ <item name="materialColorOnSurface">@color/system_on_surface_light</item>
<item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_light</item>
+ <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item>
+ <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_light</item>
<item name="materialColorOutline">@color/system_outline_light</item>
<item name="materialColorOutlineVariant">@color/system_outline_variant_light</item>
- <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
- <item name="materialColorOnSurface">@color/system_on_surface_light</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
+ <item name="materialColorPaletteKeyColorNeutral">@color/system_palette_key_color_neutral_light</item>
+ <item name="materialColorPaletteKeyColorNeutralVariant">@color/system_palette_key_color_neutral_variant_light</item>
+ <item name="materialColorPaletteKeyColorPrimary">@color/system_palette_key_color_primary_light</item>
+ <item name="materialColorPaletteKeyColorSecondary">@color/system_palette_key_color_secondary_light</item>
+ <item name="materialColorPaletteKeyColorTertiary">@color/system_palette_key_color_tertiary_light</item>
<item name="materialColorPrimary">@color/system_primary_light</item>
+ <item name="materialColorPrimaryContainer">@color/system_primary_container_light</item>
+ <item name="materialColorScrim">@color/system_scrim_light</item>
<item name="materialColorSecondary">@color/system_secondary_light</item>
+ <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
+ <item name="materialColorShadow">@color/system_shadow_light</item>
+ <item name="materialColorSurface">@color/system_surface_light</item>
+ <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item>
+ <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
+ <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item>
+ <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item>
+ <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_light</item>
+ <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_light</item>
+ <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item>
+ <item name="materialColorSurfaceTint">@color/system_surface_tint_light</item>
+ <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
<item name="materialColorTertiary">@color/system_tertiary_light</item>
- <item name="materialColorError">@color/system_error_light</item>
-
- <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
- <item name="customColorClockHour">@color/system_clock_hour_light</item>
- <item name="customColorClockMinute">@color/system_clock_minute_light</item>
- <item name="customColorClockSecond">@color/system_clock_second_light</item>
- <item name="customColorThemeApp">@color/system_theme_app_light</item>
- <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
- <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
+ <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
+ <item name="materialColorTextHintInverse">@color/system_text_hint_inverse_light</item>
+ <item name="materialColorTextPrimaryInverse">@color/system_text_primary_inverse_light</item>
+ <item name="materialColorTextPrimaryInverseDisableOnly">@color/system_text_primary_inverse_disable_only_light</item>
+ <item name="materialColorTextSecondaryAndTertiaryInverse">@color/system_text_secondary_and_tertiary_inverse_light</item>
+ <item name="materialColorTextSecondaryAndTertiaryInverseDisabled">@color/system_text_secondary_and_tertiary_inverse_disabled_light</item>
+ <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
+ <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
+ <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
+ <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
+ <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
+ <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
+ <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
+ <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
+ <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
+ <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
+ <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
+ <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="customColorBrandA">@color/system_brand_a_light</item>
<item name="customColorBrandB">@color/system_brand_b_light</item>
<item name="customColorBrandC">@color/system_brand_c_light</item>
<item name="customColorBrandD">@color/system_brand_d_light</item>
- <item name="customColorUnderSurface">@color/system_under_surface_light</item>
- <item name="customColorShadeActive">@color/system_shade_active_light</item>
+ <item name="customColorClockHour">@color/system_clock_hour_light</item>
+ <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+ <item name="customColorClockSecond">@color/system_clock_second_light</item>
<item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
<item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
- <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
<item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
<item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
- <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+ <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
<item name="customColorOverviewBackground">@color/system_overview_background_light</item>
+ <item name="customColorShadeActive">@color/system_shade_active_light</item>
+ <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+ <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+ <item name="customColorThemeApp">@color/system_theme_app_light</item>
+ <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
+ <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+ <item name="customColorWeatherTemp">@color/system_weather_temp_light</item>
+ <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
</style>
<!-- Variant of Theme.DeviceDefault.Dialog that has a fixed size. -->
@@ -3918,73 +4428,90 @@
<item name="colorForeground">@color/foreground_device_default_light</item>
<item name="colorForegroundInverse">@color/foreground_device_default_dark</item>
- <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
- <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
- <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_light</item>
- <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
- <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_light</item>
- <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_light</item>
- <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_light</item>
- <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_light</item>
- <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
- <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
- <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
- <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
- <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
- <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
- <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
+ <item name="materialColorBackground">@color/system_background_light</item>
+ <item name="materialColorControlActivated">@color/system_control_activated_light</item>
+ <item name="materialColorControlHighlight">@color/system_control_highlight_light</item>
+ <item name="materialColorControlNormal">@color/system_control_normal_light</item>
+ <item name="materialColorError">@color/system_error_light</item>
<item name="materialColorErrorContainer">@color/system_error_container_light</item>
- <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
- <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
- <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
- <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
- <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
- <item name="materialColorPrimaryContainer">@color/system_primary_container_light</item>
+ <item name="materialColorInverseOnSurface">@color/system_inverse_on_surface_light</item>
+ <item name="materialColorInversePrimary">@color/system_inverse_primary_light</item>
+ <item name="materialColorInverseSurface">@color/system_inverse_surface_light</item>
<item name="materialColorOnBackground">@color/system_on_background_light</item>
- <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
- <item name="materialColorOnSecondary">@color/system_on_secondary_light</item>
- <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item>
- <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item>
- <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item>
<item name="materialColorOnError">@color/system_on_error_light</item>
- <item name="materialColorSurface">@color/system_surface_light</item>
- <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item>
- <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item>
+ <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
+ <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
+ <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_light</item>
+ <item name="materialColorOnSecondary">@color/system_on_secondary_light</item>
+ <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_light</item>
+ <item name="materialColorOnSurface">@color/system_on_surface_light</item>
<item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_light</item>
+ <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item>
+ <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_light</item>
<item name="materialColorOutline">@color/system_outline_light</item>
<item name="materialColorOutlineVariant">@color/system_outline_variant_light</item>
- <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
- <item name="materialColorOnSurface">@color/system_on_surface_light</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
+ <item name="materialColorPaletteKeyColorNeutral">@color/system_palette_key_color_neutral_light</item>
+ <item name="materialColorPaletteKeyColorNeutralVariant">@color/system_palette_key_color_neutral_variant_light</item>
+ <item name="materialColorPaletteKeyColorPrimary">@color/system_palette_key_color_primary_light</item>
+ <item name="materialColorPaletteKeyColorSecondary">@color/system_palette_key_color_secondary_light</item>
+ <item name="materialColorPaletteKeyColorTertiary">@color/system_palette_key_color_tertiary_light</item>
<item name="materialColorPrimary">@color/system_primary_light</item>
+ <item name="materialColorPrimaryContainer">@color/system_primary_container_light</item>
+ <item name="materialColorScrim">@color/system_scrim_light</item>
<item name="materialColorSecondary">@color/system_secondary_light</item>
+ <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
+ <item name="materialColorShadow">@color/system_shadow_light</item>
+ <item name="materialColorSurface">@color/system_surface_light</item>
+ <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item>
+ <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
+ <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item>
+ <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item>
+ <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_light</item>
+ <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_light</item>
+ <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item>
+ <item name="materialColorSurfaceTint">@color/system_surface_tint_light</item>
+ <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
<item name="materialColorTertiary">@color/system_tertiary_light</item>
- <item name="materialColorError">@color/system_error_light</item>
-
- <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
- <item name="customColorClockHour">@color/system_clock_hour_light</item>
- <item name="customColorClockMinute">@color/system_clock_minute_light</item>
- <item name="customColorClockSecond">@color/system_clock_second_light</item>
- <item name="customColorThemeApp">@color/system_theme_app_light</item>
- <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
- <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
+ <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
+ <item name="materialColorTextHintInverse">@color/system_text_hint_inverse_light</item>
+ <item name="materialColorTextPrimaryInverse">@color/system_text_primary_inverse_light</item>
+ <item name="materialColorTextPrimaryInverseDisableOnly">@color/system_text_primary_inverse_disable_only_light</item>
+ <item name="materialColorTextSecondaryAndTertiaryInverse">@color/system_text_secondary_and_tertiary_inverse_light</item>
+ <item name="materialColorTextSecondaryAndTertiaryInverseDisabled">@color/system_text_secondary_and_tertiary_inverse_disabled_light</item>
+ <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
+ <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
+ <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
+ <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
+ <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
+ <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
+ <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
+ <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
+ <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
+ <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
+ <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
+ <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="customColorBrandA">@color/system_brand_a_light</item>
<item name="customColorBrandB">@color/system_brand_b_light</item>
<item name="customColorBrandC">@color/system_brand_c_light</item>
<item name="customColorBrandD">@color/system_brand_d_light</item>
- <item name="customColorUnderSurface">@color/system_under_surface_light</item>
- <item name="customColorShadeActive">@color/system_shade_active_light</item>
+ <item name="customColorClockHour">@color/system_clock_hour_light</item>
+ <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+ <item name="customColorClockSecond">@color/system_clock_second_light</item>
<item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
<item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
- <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
<item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
<item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
- <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+ <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
<item name="customColorOverviewBackground">@color/system_overview_background_light</item>
+ <item name="customColorShadeActive">@color/system_shade_active_light</item>
+ <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+ <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+ <item name="customColorThemeApp">@color/system_theme_app_light</item>
+ <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
+ <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+ <item name="customColorWeatherTemp">@color/system_weather_temp_light</item>
+ <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
</style>
<!-- Variant of Theme.DeviceDefault.Dialog.NoActionBar that has a fixed size. -->
@@ -4019,73 +4546,90 @@
<item name="colorForeground">@color/foreground_device_default_light</item>
<item name="colorForegroundInverse">@color/foreground_device_default_dark</item>
- <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
- <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
- <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_light</item>
- <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
- <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_light</item>
- <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_light</item>
- <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_light</item>
- <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_light</item>
- <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
- <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
- <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
- <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
- <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
- <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
- <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
+ <item name="materialColorBackground">@color/system_background_light</item>
+ <item name="materialColorControlActivated">@color/system_control_activated_light</item>
+ <item name="materialColorControlHighlight">@color/system_control_highlight_light</item>
+ <item name="materialColorControlNormal">@color/system_control_normal_light</item>
+ <item name="materialColorError">@color/system_error_light</item>
<item name="materialColorErrorContainer">@color/system_error_container_light</item>
- <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
- <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
- <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
- <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
- <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
- <item name="materialColorPrimaryContainer">@color/system_primary_container_light</item>
+ <item name="materialColorInverseOnSurface">@color/system_inverse_on_surface_light</item>
+ <item name="materialColorInversePrimary">@color/system_inverse_primary_light</item>
+ <item name="materialColorInverseSurface">@color/system_inverse_surface_light</item>
<item name="materialColorOnBackground">@color/system_on_background_light</item>
- <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
- <item name="materialColorOnSecondary">@color/system_on_secondary_light</item>
- <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item>
- <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item>
- <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item>
<item name="materialColorOnError">@color/system_on_error_light</item>
- <item name="materialColorSurface">@color/system_surface_light</item>
- <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item>
- <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item>
+ <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
+ <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
+ <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_light</item>
+ <item name="materialColorOnSecondary">@color/system_on_secondary_light</item>
+ <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_light</item>
+ <item name="materialColorOnSurface">@color/system_on_surface_light</item>
<item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_light</item>
+ <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item>
+ <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_light</item>
<item name="materialColorOutline">@color/system_outline_light</item>
<item name="materialColorOutlineVariant">@color/system_outline_variant_light</item>
- <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
- <item name="materialColorOnSurface">@color/system_on_surface_light</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
+ <item name="materialColorPaletteKeyColorNeutral">@color/system_palette_key_color_neutral_light</item>
+ <item name="materialColorPaletteKeyColorNeutralVariant">@color/system_palette_key_color_neutral_variant_light</item>
+ <item name="materialColorPaletteKeyColorPrimary">@color/system_palette_key_color_primary_light</item>
+ <item name="materialColorPaletteKeyColorSecondary">@color/system_palette_key_color_secondary_light</item>
+ <item name="materialColorPaletteKeyColorTertiary">@color/system_palette_key_color_tertiary_light</item>
<item name="materialColorPrimary">@color/system_primary_light</item>
+ <item name="materialColorPrimaryContainer">@color/system_primary_container_light</item>
+ <item name="materialColorScrim">@color/system_scrim_light</item>
<item name="materialColorSecondary">@color/system_secondary_light</item>
+ <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
+ <item name="materialColorShadow">@color/system_shadow_light</item>
+ <item name="materialColorSurface">@color/system_surface_light</item>
+ <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item>
+ <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
+ <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item>
+ <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item>
+ <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_light</item>
+ <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_light</item>
+ <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item>
+ <item name="materialColorSurfaceTint">@color/system_surface_tint_light</item>
+ <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
<item name="materialColorTertiary">@color/system_tertiary_light</item>
- <item name="materialColorError">@color/system_error_light</item>
-
- <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
- <item name="customColorClockHour">@color/system_clock_hour_light</item>
- <item name="customColorClockMinute">@color/system_clock_minute_light</item>
- <item name="customColorClockSecond">@color/system_clock_second_light</item>
- <item name="customColorThemeApp">@color/system_theme_app_light</item>
- <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
- <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
+ <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
+ <item name="materialColorTextHintInverse">@color/system_text_hint_inverse_light</item>
+ <item name="materialColorTextPrimaryInverse">@color/system_text_primary_inverse_light</item>
+ <item name="materialColorTextPrimaryInverseDisableOnly">@color/system_text_primary_inverse_disable_only_light</item>
+ <item name="materialColorTextSecondaryAndTertiaryInverse">@color/system_text_secondary_and_tertiary_inverse_light</item>
+ <item name="materialColorTextSecondaryAndTertiaryInverseDisabled">@color/system_text_secondary_and_tertiary_inverse_disabled_light</item>
+ <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
+ <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
+ <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
+ <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
+ <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
+ <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
+ <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
+ <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
+ <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
+ <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
+ <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
+ <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="customColorBrandA">@color/system_brand_a_light</item>
<item name="customColorBrandB">@color/system_brand_b_light</item>
<item name="customColorBrandC">@color/system_brand_c_light</item>
<item name="customColorBrandD">@color/system_brand_d_light</item>
- <item name="customColorUnderSurface">@color/system_under_surface_light</item>
- <item name="customColorShadeActive">@color/system_shade_active_light</item>
+ <item name="customColorClockHour">@color/system_clock_hour_light</item>
+ <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+ <item name="customColorClockSecond">@color/system_clock_second_light</item>
<item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
<item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
- <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
<item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
<item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
- <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+ <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
<item name="customColorOverviewBackground">@color/system_overview_background_light</item>
+ <item name="customColorShadeActive">@color/system_shade_active_light</item>
+ <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+ <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+ <item name="customColorThemeApp">@color/system_theme_app_light</item>
+ <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
+ <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+ <item name="customColorWeatherTemp">@color/system_weather_temp_light</item>
+ <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
</style>
<!-- DeviceDefault light theme for a window that will be displayed either full-screen on smaller
@@ -4139,73 +4683,90 @@
<!-- Toolbar attributes -->
<item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
- <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
- <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
- <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_light</item>
- <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
- <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_light</item>
- <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_light</item>
- <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_light</item>
- <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_light</item>
- <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
- <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
- <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
- <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
- <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
- <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
- <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
+ <item name="materialColorBackground">@color/system_background_light</item>
+ <item name="materialColorControlActivated">@color/system_control_activated_light</item>
+ <item name="materialColorControlHighlight">@color/system_control_highlight_light</item>
+ <item name="materialColorControlNormal">@color/system_control_normal_light</item>
+ <item name="materialColorError">@color/system_error_light</item>
<item name="materialColorErrorContainer">@color/system_error_container_light</item>
- <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
- <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
- <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
- <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
- <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
- <item name="materialColorPrimaryContainer">@color/system_primary_container_light</item>
+ <item name="materialColorInverseOnSurface">@color/system_inverse_on_surface_light</item>
+ <item name="materialColorInversePrimary">@color/system_inverse_primary_light</item>
+ <item name="materialColorInverseSurface">@color/system_inverse_surface_light</item>
<item name="materialColorOnBackground">@color/system_on_background_light</item>
- <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
- <item name="materialColorOnSecondary">@color/system_on_secondary_light</item>
- <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item>
- <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item>
- <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item>
<item name="materialColorOnError">@color/system_on_error_light</item>
- <item name="materialColorSurface">@color/system_surface_light</item>
- <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item>
- <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item>
+ <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
+ <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
+ <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_light</item>
+ <item name="materialColorOnSecondary">@color/system_on_secondary_light</item>
+ <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_light</item>
+ <item name="materialColorOnSurface">@color/system_on_surface_light</item>
<item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_light</item>
+ <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item>
+ <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_light</item>
<item name="materialColorOutline">@color/system_outline_light</item>
<item name="materialColorOutlineVariant">@color/system_outline_variant_light</item>
- <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
- <item name="materialColorOnSurface">@color/system_on_surface_light</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
+ <item name="materialColorPaletteKeyColorNeutral">@color/system_palette_key_color_neutral_light</item>
+ <item name="materialColorPaletteKeyColorNeutralVariant">@color/system_palette_key_color_neutral_variant_light</item>
+ <item name="materialColorPaletteKeyColorPrimary">@color/system_palette_key_color_primary_light</item>
+ <item name="materialColorPaletteKeyColorSecondary">@color/system_palette_key_color_secondary_light</item>
+ <item name="materialColorPaletteKeyColorTertiary">@color/system_palette_key_color_tertiary_light</item>
<item name="materialColorPrimary">@color/system_primary_light</item>
+ <item name="materialColorPrimaryContainer">@color/system_primary_container_light</item>
+ <item name="materialColorScrim">@color/system_scrim_light</item>
<item name="materialColorSecondary">@color/system_secondary_light</item>
+ <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
+ <item name="materialColorShadow">@color/system_shadow_light</item>
+ <item name="materialColorSurface">@color/system_surface_light</item>
+ <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item>
+ <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
+ <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item>
+ <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item>
+ <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_light</item>
+ <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_light</item>
+ <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item>
+ <item name="materialColorSurfaceTint">@color/system_surface_tint_light</item>
+ <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
<item name="materialColorTertiary">@color/system_tertiary_light</item>
- <item name="materialColorError">@color/system_error_light</item>
-
- <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
- <item name="customColorClockHour">@color/system_clock_hour_light</item>
- <item name="customColorClockMinute">@color/system_clock_minute_light</item>
- <item name="customColorClockSecond">@color/system_clock_second_light</item>
- <item name="customColorThemeApp">@color/system_theme_app_light</item>
- <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
- <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
+ <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
+ <item name="materialColorTextHintInverse">@color/system_text_hint_inverse_light</item>
+ <item name="materialColorTextPrimaryInverse">@color/system_text_primary_inverse_light</item>
+ <item name="materialColorTextPrimaryInverseDisableOnly">@color/system_text_primary_inverse_disable_only_light</item>
+ <item name="materialColorTextSecondaryAndTertiaryInverse">@color/system_text_secondary_and_tertiary_inverse_light</item>
+ <item name="materialColorTextSecondaryAndTertiaryInverseDisabled">@color/system_text_secondary_and_tertiary_inverse_disabled_light</item>
+ <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
+ <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
+ <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
+ <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
+ <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
+ <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
+ <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
+ <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
+ <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
+ <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
+ <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
+ <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="customColorBrandA">@color/system_brand_a_light</item>
<item name="customColorBrandB">@color/system_brand_b_light</item>
<item name="customColorBrandC">@color/system_brand_c_light</item>
<item name="customColorBrandD">@color/system_brand_d_light</item>
- <item name="customColorUnderSurface">@color/system_under_surface_light</item>
- <item name="customColorShadeActive">@color/system_shade_active_light</item>
+ <item name="customColorClockHour">@color/system_clock_hour_light</item>
+ <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+ <item name="customColorClockSecond">@color/system_clock_second_light</item>
<item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
<item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
- <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
<item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
<item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
- <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+ <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
<item name="customColorOverviewBackground">@color/system_overview_background_light</item>
+ <item name="customColorShadeActive">@color/system_shade_active_light</item>
+ <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+ <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+ <item name="customColorThemeApp">@color/system_theme_app_light</item>
+ <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
+ <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+ <item name="customColorWeatherTemp">@color/system_weather_temp_light</item>
+ <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
</style>
<!-- DeviceDefault light theme for a window without an action bar that will be displayed either
@@ -4260,73 +4821,90 @@
<!-- Toolbar attributes -->
<item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
- <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
- <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
- <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_light</item>
- <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
- <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_light</item>
- <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_light</item>
- <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_light</item>
- <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_light</item>
- <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
- <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
- <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
- <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
- <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
- <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
- <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
+ <item name="materialColorBackground">@color/system_background_light</item>
+ <item name="materialColorControlActivated">@color/system_control_activated_light</item>
+ <item name="materialColorControlHighlight">@color/system_control_highlight_light</item>
+ <item name="materialColorControlNormal">@color/system_control_normal_light</item>
+ <item name="materialColorError">@color/system_error_light</item>
<item name="materialColorErrorContainer">@color/system_error_container_light</item>
- <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
- <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
- <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
- <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
- <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
- <item name="materialColorPrimaryContainer">@color/system_primary_container_light</item>
+ <item name="materialColorInverseOnSurface">@color/system_inverse_on_surface_light</item>
+ <item name="materialColorInversePrimary">@color/system_inverse_primary_light</item>
+ <item name="materialColorInverseSurface">@color/system_inverse_surface_light</item>
<item name="materialColorOnBackground">@color/system_on_background_light</item>
- <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
- <item name="materialColorOnSecondary">@color/system_on_secondary_light</item>
- <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item>
- <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item>
- <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item>
<item name="materialColorOnError">@color/system_on_error_light</item>
- <item name="materialColorSurface">@color/system_surface_light</item>
- <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item>
- <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item>
+ <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
+ <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
+ <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_light</item>
+ <item name="materialColorOnSecondary">@color/system_on_secondary_light</item>
+ <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_light</item>
+ <item name="materialColorOnSurface">@color/system_on_surface_light</item>
<item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_light</item>
+ <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item>
+ <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_light</item>
<item name="materialColorOutline">@color/system_outline_light</item>
<item name="materialColorOutlineVariant">@color/system_outline_variant_light</item>
- <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
- <item name="materialColorOnSurface">@color/system_on_surface_light</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
+ <item name="materialColorPaletteKeyColorNeutral">@color/system_palette_key_color_neutral_light</item>
+ <item name="materialColorPaletteKeyColorNeutralVariant">@color/system_palette_key_color_neutral_variant_light</item>
+ <item name="materialColorPaletteKeyColorPrimary">@color/system_palette_key_color_primary_light</item>
+ <item name="materialColorPaletteKeyColorSecondary">@color/system_palette_key_color_secondary_light</item>
+ <item name="materialColorPaletteKeyColorTertiary">@color/system_palette_key_color_tertiary_light</item>
<item name="materialColorPrimary">@color/system_primary_light</item>
+ <item name="materialColorPrimaryContainer">@color/system_primary_container_light</item>
+ <item name="materialColorScrim">@color/system_scrim_light</item>
<item name="materialColorSecondary">@color/system_secondary_light</item>
+ <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
+ <item name="materialColorShadow">@color/system_shadow_light</item>
+ <item name="materialColorSurface">@color/system_surface_light</item>
+ <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item>
+ <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
+ <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item>
+ <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item>
+ <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_light</item>
+ <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_light</item>
+ <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item>
+ <item name="materialColorSurfaceTint">@color/system_surface_tint_light</item>
+ <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
<item name="materialColorTertiary">@color/system_tertiary_light</item>
- <item name="materialColorError">@color/system_error_light</item>
-
- <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
- <item name="customColorClockHour">@color/system_clock_hour_light</item>
- <item name="customColorClockMinute">@color/system_clock_minute_light</item>
- <item name="customColorClockSecond">@color/system_clock_second_light</item>
- <item name="customColorThemeApp">@color/system_theme_app_light</item>
- <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
- <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
+ <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
+ <item name="materialColorTextHintInverse">@color/system_text_hint_inverse_light</item>
+ <item name="materialColorTextPrimaryInverse">@color/system_text_primary_inverse_light</item>
+ <item name="materialColorTextPrimaryInverseDisableOnly">@color/system_text_primary_inverse_disable_only_light</item>
+ <item name="materialColorTextSecondaryAndTertiaryInverse">@color/system_text_secondary_and_tertiary_inverse_light</item>
+ <item name="materialColorTextSecondaryAndTertiaryInverseDisabled">@color/system_text_secondary_and_tertiary_inverse_disabled_light</item>
+ <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
+ <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
+ <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
+ <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
+ <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
+ <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
+ <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
+ <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
+ <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
+ <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
+ <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
+ <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="customColorBrandA">@color/system_brand_a_light</item>
<item name="customColorBrandB">@color/system_brand_b_light</item>
<item name="customColorBrandC">@color/system_brand_c_light</item>
<item name="customColorBrandD">@color/system_brand_d_light</item>
- <item name="customColorUnderSurface">@color/system_under_surface_light</item>
- <item name="customColorShadeActive">@color/system_shade_active_light</item>
+ <item name="customColorClockHour">@color/system_clock_hour_light</item>
+ <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+ <item name="customColorClockSecond">@color/system_clock_second_light</item>
<item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
<item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
- <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
<item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
<item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
- <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+ <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
<item name="customColorOverviewBackground">@color/system_overview_background_light</item>
+ <item name="customColorShadeActive">@color/system_shade_active_light</item>
+ <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+ <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+ <item name="customColorThemeApp">@color/system_theme_app_light</item>
+ <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
+ <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+ <item name="customColorWeatherTemp">@color/system_weather_temp_light</item>
+ <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
</style>
<!-- DeviceDefault light theme for a presentation window on a secondary display. -->
@@ -4379,73 +4957,90 @@
<!-- Toolbar attributes -->
<item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
- <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
- <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
- <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_light</item>
- <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
- <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_light</item>
- <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_light</item>
- <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_light</item>
- <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_light</item>
- <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
- <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
- <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
- <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
- <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
- <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
- <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
+ <item name="materialColorBackground">@color/system_background_light</item>
+ <item name="materialColorControlActivated">@color/system_control_activated_light</item>
+ <item name="materialColorControlHighlight">@color/system_control_highlight_light</item>
+ <item name="materialColorControlNormal">@color/system_control_normal_light</item>
+ <item name="materialColorError">@color/system_error_light</item>
<item name="materialColorErrorContainer">@color/system_error_container_light</item>
- <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
- <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
- <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
- <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
- <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
- <item name="materialColorPrimaryContainer">@color/system_primary_container_light</item>
+ <item name="materialColorInverseOnSurface">@color/system_inverse_on_surface_light</item>
+ <item name="materialColorInversePrimary">@color/system_inverse_primary_light</item>
+ <item name="materialColorInverseSurface">@color/system_inverse_surface_light</item>
<item name="materialColorOnBackground">@color/system_on_background_light</item>
- <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
- <item name="materialColorOnSecondary">@color/system_on_secondary_light</item>
- <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item>
- <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item>
- <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item>
<item name="materialColorOnError">@color/system_on_error_light</item>
- <item name="materialColorSurface">@color/system_surface_light</item>
- <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item>
- <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item>
+ <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
+ <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
+ <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_light</item>
+ <item name="materialColorOnSecondary">@color/system_on_secondary_light</item>
+ <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_light</item>
+ <item name="materialColorOnSurface">@color/system_on_surface_light</item>
<item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_light</item>
+ <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item>
+ <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_light</item>
<item name="materialColorOutline">@color/system_outline_light</item>
<item name="materialColorOutlineVariant">@color/system_outline_variant_light</item>
- <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
- <item name="materialColorOnSurface">@color/system_on_surface_light</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
+ <item name="materialColorPaletteKeyColorNeutral">@color/system_palette_key_color_neutral_light</item>
+ <item name="materialColorPaletteKeyColorNeutralVariant">@color/system_palette_key_color_neutral_variant_light</item>
+ <item name="materialColorPaletteKeyColorPrimary">@color/system_palette_key_color_primary_light</item>
+ <item name="materialColorPaletteKeyColorSecondary">@color/system_palette_key_color_secondary_light</item>
+ <item name="materialColorPaletteKeyColorTertiary">@color/system_palette_key_color_tertiary_light</item>
<item name="materialColorPrimary">@color/system_primary_light</item>
+ <item name="materialColorPrimaryContainer">@color/system_primary_container_light</item>
+ <item name="materialColorScrim">@color/system_scrim_light</item>
<item name="materialColorSecondary">@color/system_secondary_light</item>
+ <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
+ <item name="materialColorShadow">@color/system_shadow_light</item>
+ <item name="materialColorSurface">@color/system_surface_light</item>
+ <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item>
+ <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
+ <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item>
+ <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item>
+ <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_light</item>
+ <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_light</item>
+ <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item>
+ <item name="materialColorSurfaceTint">@color/system_surface_tint_light</item>
+ <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
<item name="materialColorTertiary">@color/system_tertiary_light</item>
- <item name="materialColorError">@color/system_error_light</item>
-
- <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
- <item name="customColorClockHour">@color/system_clock_hour_light</item>
- <item name="customColorClockMinute">@color/system_clock_minute_light</item>
- <item name="customColorClockSecond">@color/system_clock_second_light</item>
- <item name="customColorThemeApp">@color/system_theme_app_light</item>
- <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
- <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
+ <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
+ <item name="materialColorTextHintInverse">@color/system_text_hint_inverse_light</item>
+ <item name="materialColorTextPrimaryInverse">@color/system_text_primary_inverse_light</item>
+ <item name="materialColorTextPrimaryInverseDisableOnly">@color/system_text_primary_inverse_disable_only_light</item>
+ <item name="materialColorTextSecondaryAndTertiaryInverse">@color/system_text_secondary_and_tertiary_inverse_light</item>
+ <item name="materialColorTextSecondaryAndTertiaryInverseDisabled">@color/system_text_secondary_and_tertiary_inverse_disabled_light</item>
+ <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
+ <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
+ <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
+ <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
+ <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
+ <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
+ <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
+ <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
+ <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
+ <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
+ <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
+ <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="customColorBrandA">@color/system_brand_a_light</item>
<item name="customColorBrandB">@color/system_brand_b_light</item>
<item name="customColorBrandC">@color/system_brand_c_light</item>
<item name="customColorBrandD">@color/system_brand_d_light</item>
- <item name="customColorUnderSurface">@color/system_under_surface_light</item>
- <item name="customColorShadeActive">@color/system_shade_active_light</item>
+ <item name="customColorClockHour">@color/system_clock_hour_light</item>
+ <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+ <item name="customColorClockSecond">@color/system_clock_second_light</item>
<item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
<item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
- <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
<item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
<item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
- <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+ <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
<item name="customColorOverviewBackground">@color/system_overview_background_light</item>
+ <item name="customColorShadeActive">@color/system_shade_active_light</item>
+ <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+ <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+ <item name="customColorThemeApp">@color/system_theme_app_light</item>
+ <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
+ <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+ <item name="customColorWeatherTemp">@color/system_weather_temp_light</item>
+ <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
</style>
<!-- DeviceDefault light theme for panel windows. This removes all extraneous window
@@ -4497,73 +5092,90 @@
<!-- Toolbar attributes -->
<item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
- <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
- <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
- <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_light</item>
- <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
- <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_light</item>
- <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_light</item>
- <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_light</item>
- <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_light</item>
- <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
- <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
- <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
- <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
- <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
- <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
- <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
+ <item name="materialColorBackground">@color/system_background_light</item>
+ <item name="materialColorControlActivated">@color/system_control_activated_light</item>
+ <item name="materialColorControlHighlight">@color/system_control_highlight_light</item>
+ <item name="materialColorControlNormal">@color/system_control_normal_light</item>
+ <item name="materialColorError">@color/system_error_light</item>
<item name="materialColorErrorContainer">@color/system_error_container_light</item>
- <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
- <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
- <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
- <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
- <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
- <item name="materialColorPrimaryContainer">@color/system_primary_container_light</item>
+ <item name="materialColorInverseOnSurface">@color/system_inverse_on_surface_light</item>
+ <item name="materialColorInversePrimary">@color/system_inverse_primary_light</item>
+ <item name="materialColorInverseSurface">@color/system_inverse_surface_light</item>
<item name="materialColorOnBackground">@color/system_on_background_light</item>
- <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
- <item name="materialColorOnSecondary">@color/system_on_secondary_light</item>
- <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item>
- <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item>
- <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item>
<item name="materialColorOnError">@color/system_on_error_light</item>
- <item name="materialColorSurface">@color/system_surface_light</item>
- <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item>
- <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item>
+ <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
+ <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
+ <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_light</item>
+ <item name="materialColorOnSecondary">@color/system_on_secondary_light</item>
+ <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_light</item>
+ <item name="materialColorOnSurface">@color/system_on_surface_light</item>
<item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_light</item>
+ <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item>
+ <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_light</item>
<item name="materialColorOutline">@color/system_outline_light</item>
<item name="materialColorOutlineVariant">@color/system_outline_variant_light</item>
- <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
- <item name="materialColorOnSurface">@color/system_on_surface_light</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
+ <item name="materialColorPaletteKeyColorNeutral">@color/system_palette_key_color_neutral_light</item>
+ <item name="materialColorPaletteKeyColorNeutralVariant">@color/system_palette_key_color_neutral_variant_light</item>
+ <item name="materialColorPaletteKeyColorPrimary">@color/system_palette_key_color_primary_light</item>
+ <item name="materialColorPaletteKeyColorSecondary">@color/system_palette_key_color_secondary_light</item>
+ <item name="materialColorPaletteKeyColorTertiary">@color/system_palette_key_color_tertiary_light</item>
<item name="materialColorPrimary">@color/system_primary_light</item>
+ <item name="materialColorPrimaryContainer">@color/system_primary_container_light</item>
+ <item name="materialColorScrim">@color/system_scrim_light</item>
<item name="materialColorSecondary">@color/system_secondary_light</item>
+ <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
+ <item name="materialColorShadow">@color/system_shadow_light</item>
+ <item name="materialColorSurface">@color/system_surface_light</item>
+ <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item>
+ <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
+ <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item>
+ <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item>
+ <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_light</item>
+ <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_light</item>
+ <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item>
+ <item name="materialColorSurfaceTint">@color/system_surface_tint_light</item>
+ <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
<item name="materialColorTertiary">@color/system_tertiary_light</item>
- <item name="materialColorError">@color/system_error_light</item>
-
- <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
- <item name="customColorClockHour">@color/system_clock_hour_light</item>
- <item name="customColorClockMinute">@color/system_clock_minute_light</item>
- <item name="customColorClockSecond">@color/system_clock_second_light</item>
- <item name="customColorThemeApp">@color/system_theme_app_light</item>
- <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
- <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
+ <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
+ <item name="materialColorTextHintInverse">@color/system_text_hint_inverse_light</item>
+ <item name="materialColorTextPrimaryInverse">@color/system_text_primary_inverse_light</item>
+ <item name="materialColorTextPrimaryInverseDisableOnly">@color/system_text_primary_inverse_disable_only_light</item>
+ <item name="materialColorTextSecondaryAndTertiaryInverse">@color/system_text_secondary_and_tertiary_inverse_light</item>
+ <item name="materialColorTextSecondaryAndTertiaryInverseDisabled">@color/system_text_secondary_and_tertiary_inverse_disabled_light</item>
+ <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
+ <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
+ <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
+ <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
+ <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
+ <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
+ <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
+ <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
+ <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
+ <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
+ <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
+ <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="customColorBrandA">@color/system_brand_a_light</item>
<item name="customColorBrandB">@color/system_brand_b_light</item>
<item name="customColorBrandC">@color/system_brand_c_light</item>
<item name="customColorBrandD">@color/system_brand_d_light</item>
- <item name="customColorUnderSurface">@color/system_under_surface_light</item>
- <item name="customColorShadeActive">@color/system_shade_active_light</item>
+ <item name="customColorClockHour">@color/system_clock_hour_light</item>
+ <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+ <item name="customColorClockSecond">@color/system_clock_second_light</item>
<item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
<item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
- <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
<item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
<item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
- <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+ <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
<item name="customColorOverviewBackground">@color/system_overview_background_light</item>
+ <item name="customColorShadeActive">@color/system_shade_active_light</item>
+ <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+ <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+ <item name="customColorThemeApp">@color/system_theme_app_light</item>
+ <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
+ <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+ <item name="customColorWeatherTemp">@color/system_weather_temp_light</item>
+ <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
</style>
<style name="Theme.DeviceDefault.Light.Dialog.Alert" parent="Theme.Material.Light.Dialog.Alert">
@@ -4614,73 +5226,90 @@
<!-- Toolbar attributes -->
<item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
- <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
- <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
- <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_light</item>
- <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
- <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_light</item>
- <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_light</item>
- <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_light</item>
- <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_light</item>
- <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
- <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
- <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
- <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
- <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
- <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
- <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
+ <item name="materialColorBackground">@color/system_background_light</item>
+ <item name="materialColorControlActivated">@color/system_control_activated_light</item>
+ <item name="materialColorControlHighlight">@color/system_control_highlight_light</item>
+ <item name="materialColorControlNormal">@color/system_control_normal_light</item>
+ <item name="materialColorError">@color/system_error_light</item>
<item name="materialColorErrorContainer">@color/system_error_container_light</item>
- <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
- <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
- <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
- <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
- <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
- <item name="materialColorPrimaryContainer">@color/system_primary_container_light</item>
+ <item name="materialColorInverseOnSurface">@color/system_inverse_on_surface_light</item>
+ <item name="materialColorInversePrimary">@color/system_inverse_primary_light</item>
+ <item name="materialColorInverseSurface">@color/system_inverse_surface_light</item>
<item name="materialColorOnBackground">@color/system_on_background_light</item>
- <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
- <item name="materialColorOnSecondary">@color/system_on_secondary_light</item>
- <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item>
- <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item>
- <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item>
<item name="materialColorOnError">@color/system_on_error_light</item>
- <item name="materialColorSurface">@color/system_surface_light</item>
- <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item>
- <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item>
+ <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
+ <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
+ <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_light</item>
+ <item name="materialColorOnSecondary">@color/system_on_secondary_light</item>
+ <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_light</item>
+ <item name="materialColorOnSurface">@color/system_on_surface_light</item>
<item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_light</item>
+ <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item>
+ <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_light</item>
<item name="materialColorOutline">@color/system_outline_light</item>
<item name="materialColorOutlineVariant">@color/system_outline_variant_light</item>
- <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
- <item name="materialColorOnSurface">@color/system_on_surface_light</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
+ <item name="materialColorPaletteKeyColorNeutral">@color/system_palette_key_color_neutral_light</item>
+ <item name="materialColorPaletteKeyColorNeutralVariant">@color/system_palette_key_color_neutral_variant_light</item>
+ <item name="materialColorPaletteKeyColorPrimary">@color/system_palette_key_color_primary_light</item>
+ <item name="materialColorPaletteKeyColorSecondary">@color/system_palette_key_color_secondary_light</item>
+ <item name="materialColorPaletteKeyColorTertiary">@color/system_palette_key_color_tertiary_light</item>
<item name="materialColorPrimary">@color/system_primary_light</item>
+ <item name="materialColorPrimaryContainer">@color/system_primary_container_light</item>
+ <item name="materialColorScrim">@color/system_scrim_light</item>
<item name="materialColorSecondary">@color/system_secondary_light</item>
+ <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
+ <item name="materialColorShadow">@color/system_shadow_light</item>
+ <item name="materialColorSurface">@color/system_surface_light</item>
+ <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item>
+ <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
+ <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item>
+ <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item>
+ <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_light</item>
+ <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_light</item>
+ <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item>
+ <item name="materialColorSurfaceTint">@color/system_surface_tint_light</item>
+ <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
<item name="materialColorTertiary">@color/system_tertiary_light</item>
- <item name="materialColorError">@color/system_error_light</item>
-
- <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
- <item name="customColorClockHour">@color/system_clock_hour_light</item>
- <item name="customColorClockMinute">@color/system_clock_minute_light</item>
- <item name="customColorClockSecond">@color/system_clock_second_light</item>
- <item name="customColorThemeApp">@color/system_theme_app_light</item>
- <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
- <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
+ <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
+ <item name="materialColorTextHintInverse">@color/system_text_hint_inverse_light</item>
+ <item name="materialColorTextPrimaryInverse">@color/system_text_primary_inverse_light</item>
+ <item name="materialColorTextPrimaryInverseDisableOnly">@color/system_text_primary_inverse_disable_only_light</item>
+ <item name="materialColorTextSecondaryAndTertiaryInverse">@color/system_text_secondary_and_tertiary_inverse_light</item>
+ <item name="materialColorTextSecondaryAndTertiaryInverseDisabled">@color/system_text_secondary_and_tertiary_inverse_disabled_light</item>
+ <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
+ <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
+ <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
+ <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
+ <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
+ <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
+ <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
+ <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
+ <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
+ <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
+ <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
+ <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="customColorBrandA">@color/system_brand_a_light</item>
<item name="customColorBrandB">@color/system_brand_b_light</item>
<item name="customColorBrandC">@color/system_brand_c_light</item>
<item name="customColorBrandD">@color/system_brand_d_light</item>
- <item name="customColorUnderSurface">@color/system_under_surface_light</item>
- <item name="customColorShadeActive">@color/system_shade_active_light</item>
+ <item name="customColorClockHour">@color/system_clock_hour_light</item>
+ <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+ <item name="customColorClockSecond">@color/system_clock_second_light</item>
<item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
<item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
- <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
<item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
<item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
- <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+ <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
<item name="customColorOverviewBackground">@color/system_overview_background_light</item>
+ <item name="customColorShadeActive">@color/system_shade_active_light</item>
+ <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+ <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+ <item name="customColorThemeApp">@color/system_theme_app_light</item>
+ <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
+ <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+ <item name="customColorWeatherTemp">@color/system_weather_temp_light</item>
+ <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
</style>
<style name="Theme.DeviceDefault.Dialog.Alert.DayNight" parent="Theme.DeviceDefault.Light.Dialog.Alert" />
@@ -4731,73 +5360,90 @@
<!-- Toolbar attributes -->
<item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
- <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
- <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
- <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_light</item>
- <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
- <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_light</item>
- <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_light</item>
- <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_light</item>
- <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_light</item>
- <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
- <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
- <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
- <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
- <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
- <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
- <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
+ <item name="materialColorBackground">@color/system_background_light</item>
+ <item name="materialColorControlActivated">@color/system_control_activated_light</item>
+ <item name="materialColorControlHighlight">@color/system_control_highlight_light</item>
+ <item name="materialColorControlNormal">@color/system_control_normal_light</item>
+ <item name="materialColorError">@color/system_error_light</item>
<item name="materialColorErrorContainer">@color/system_error_container_light</item>
- <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
- <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
- <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
- <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
- <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
- <item name="materialColorPrimaryContainer">@color/system_primary_container_light</item>
+ <item name="materialColorInverseOnSurface">@color/system_inverse_on_surface_light</item>
+ <item name="materialColorInversePrimary">@color/system_inverse_primary_light</item>
+ <item name="materialColorInverseSurface">@color/system_inverse_surface_light</item>
<item name="materialColorOnBackground">@color/system_on_background_light</item>
- <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
- <item name="materialColorOnSecondary">@color/system_on_secondary_light</item>
- <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item>
- <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item>
- <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item>
<item name="materialColorOnError">@color/system_on_error_light</item>
- <item name="materialColorSurface">@color/system_surface_light</item>
- <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item>
- <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item>
+ <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
+ <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
+ <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_light</item>
+ <item name="materialColorOnSecondary">@color/system_on_secondary_light</item>
+ <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_light</item>
+ <item name="materialColorOnSurface">@color/system_on_surface_light</item>
<item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_light</item>
+ <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item>
+ <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_light</item>
<item name="materialColorOutline">@color/system_outline_light</item>
<item name="materialColorOutlineVariant">@color/system_outline_variant_light</item>
- <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
- <item name="materialColorOnSurface">@color/system_on_surface_light</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
+ <item name="materialColorPaletteKeyColorNeutral">@color/system_palette_key_color_neutral_light</item>
+ <item name="materialColorPaletteKeyColorNeutralVariant">@color/system_palette_key_color_neutral_variant_light</item>
+ <item name="materialColorPaletteKeyColorPrimary">@color/system_palette_key_color_primary_light</item>
+ <item name="materialColorPaletteKeyColorSecondary">@color/system_palette_key_color_secondary_light</item>
+ <item name="materialColorPaletteKeyColorTertiary">@color/system_palette_key_color_tertiary_light</item>
<item name="materialColorPrimary">@color/system_primary_light</item>
+ <item name="materialColorPrimaryContainer">@color/system_primary_container_light</item>
+ <item name="materialColorScrim">@color/system_scrim_light</item>
<item name="materialColorSecondary">@color/system_secondary_light</item>
+ <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
+ <item name="materialColorShadow">@color/system_shadow_light</item>
+ <item name="materialColorSurface">@color/system_surface_light</item>
+ <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item>
+ <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
+ <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item>
+ <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item>
+ <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_light</item>
+ <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_light</item>
+ <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item>
+ <item name="materialColorSurfaceTint">@color/system_surface_tint_light</item>
+ <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
<item name="materialColorTertiary">@color/system_tertiary_light</item>
- <item name="materialColorError">@color/system_error_light</item>
-
- <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
- <item name="customColorClockHour">@color/system_clock_hour_light</item>
- <item name="customColorClockMinute">@color/system_clock_minute_light</item>
- <item name="customColorClockSecond">@color/system_clock_second_light</item>
- <item name="customColorThemeApp">@color/system_theme_app_light</item>
- <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
- <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
+ <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
+ <item name="materialColorTextHintInverse">@color/system_text_hint_inverse_light</item>
+ <item name="materialColorTextPrimaryInverse">@color/system_text_primary_inverse_light</item>
+ <item name="materialColorTextPrimaryInverseDisableOnly">@color/system_text_primary_inverse_disable_only_light</item>
+ <item name="materialColorTextSecondaryAndTertiaryInverse">@color/system_text_secondary_and_tertiary_inverse_light</item>
+ <item name="materialColorTextSecondaryAndTertiaryInverseDisabled">@color/system_text_secondary_and_tertiary_inverse_disabled_light</item>
+ <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
+ <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
+ <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
+ <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
+ <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
+ <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
+ <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
+ <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
+ <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
+ <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
+ <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
+ <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="customColorBrandA">@color/system_brand_a_light</item>
<item name="customColorBrandB">@color/system_brand_b_light</item>
<item name="customColorBrandC">@color/system_brand_c_light</item>
<item name="customColorBrandD">@color/system_brand_d_light</item>
- <item name="customColorUnderSurface">@color/system_under_surface_light</item>
- <item name="customColorShadeActive">@color/system_shade_active_light</item>
+ <item name="customColorClockHour">@color/system_clock_hour_light</item>
+ <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+ <item name="customColorClockSecond">@color/system_clock_second_light</item>
<item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
<item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
- <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
<item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
<item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
- <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+ <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
<item name="customColorOverviewBackground">@color/system_overview_background_light</item>
+ <item name="customColorShadeActive">@color/system_shade_active_light</item>
+ <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+ <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+ <item name="customColorThemeApp">@color/system_theme_app_light</item>
+ <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
+ <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+ <item name="customColorWeatherTemp">@color/system_weather_temp_light</item>
+ <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
</style>
<style name="Theme.DeviceDefault.Light.Voice" parent="Theme.Material.Light.Voice">
@@ -4846,73 +5492,90 @@
<!-- Toolbar attributes -->
<item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
- <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
- <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
- <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_light</item>
- <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
- <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_light</item>
- <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_light</item>
- <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_light</item>
- <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_light</item>
- <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
- <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
- <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
- <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
- <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
- <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
- <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
+ <item name="materialColorBackground">@color/system_background_light</item>
+ <item name="materialColorControlActivated">@color/system_control_activated_light</item>
+ <item name="materialColorControlHighlight">@color/system_control_highlight_light</item>
+ <item name="materialColorControlNormal">@color/system_control_normal_light</item>
+ <item name="materialColorError">@color/system_error_light</item>
<item name="materialColorErrorContainer">@color/system_error_container_light</item>
- <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
- <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
- <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
- <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
- <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
- <item name="materialColorPrimaryContainer">@color/system_primary_container_light</item>
+ <item name="materialColorInverseOnSurface">@color/system_inverse_on_surface_light</item>
+ <item name="materialColorInversePrimary">@color/system_inverse_primary_light</item>
+ <item name="materialColorInverseSurface">@color/system_inverse_surface_light</item>
<item name="materialColorOnBackground">@color/system_on_background_light</item>
- <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
- <item name="materialColorOnSecondary">@color/system_on_secondary_light</item>
- <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item>
- <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item>
- <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item>
<item name="materialColorOnError">@color/system_on_error_light</item>
- <item name="materialColorSurface">@color/system_surface_light</item>
- <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item>
- <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item>
+ <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
+ <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
+ <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_light</item>
+ <item name="materialColorOnSecondary">@color/system_on_secondary_light</item>
+ <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_light</item>
+ <item name="materialColorOnSurface">@color/system_on_surface_light</item>
<item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_light</item>
+ <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item>
+ <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_light</item>
<item name="materialColorOutline">@color/system_outline_light</item>
<item name="materialColorOutlineVariant">@color/system_outline_variant_light</item>
- <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
- <item name="materialColorOnSurface">@color/system_on_surface_light</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
+ <item name="materialColorPaletteKeyColorNeutral">@color/system_palette_key_color_neutral_light</item>
+ <item name="materialColorPaletteKeyColorNeutralVariant">@color/system_palette_key_color_neutral_variant_light</item>
+ <item name="materialColorPaletteKeyColorPrimary">@color/system_palette_key_color_primary_light</item>
+ <item name="materialColorPaletteKeyColorSecondary">@color/system_palette_key_color_secondary_light</item>
+ <item name="materialColorPaletteKeyColorTertiary">@color/system_palette_key_color_tertiary_light</item>
<item name="materialColorPrimary">@color/system_primary_light</item>
+ <item name="materialColorPrimaryContainer">@color/system_primary_container_light</item>
+ <item name="materialColorScrim">@color/system_scrim_light</item>
<item name="materialColorSecondary">@color/system_secondary_light</item>
+ <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
+ <item name="materialColorShadow">@color/system_shadow_light</item>
+ <item name="materialColorSurface">@color/system_surface_light</item>
+ <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item>
+ <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
+ <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item>
+ <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item>
+ <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_light</item>
+ <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_light</item>
+ <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item>
+ <item name="materialColorSurfaceTint">@color/system_surface_tint_light</item>
+ <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
<item name="materialColorTertiary">@color/system_tertiary_light</item>
- <item name="materialColorError">@color/system_error_light</item>
-
- <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
- <item name="customColorClockHour">@color/system_clock_hour_light</item>
- <item name="customColorClockMinute">@color/system_clock_minute_light</item>
- <item name="customColorClockSecond">@color/system_clock_second_light</item>
- <item name="customColorThemeApp">@color/system_theme_app_light</item>
- <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
- <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
+ <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
+ <item name="materialColorTextHintInverse">@color/system_text_hint_inverse_light</item>
+ <item name="materialColorTextPrimaryInverse">@color/system_text_primary_inverse_light</item>
+ <item name="materialColorTextPrimaryInverseDisableOnly">@color/system_text_primary_inverse_disable_only_light</item>
+ <item name="materialColorTextSecondaryAndTertiaryInverse">@color/system_text_secondary_and_tertiary_inverse_light</item>
+ <item name="materialColorTextSecondaryAndTertiaryInverseDisabled">@color/system_text_secondary_and_tertiary_inverse_disabled_light</item>
+ <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
+ <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
+ <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
+ <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
+ <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
+ <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
+ <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
+ <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
+ <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
+ <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
+ <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
+ <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="customColorBrandA">@color/system_brand_a_light</item>
<item name="customColorBrandB">@color/system_brand_b_light</item>
<item name="customColorBrandC">@color/system_brand_c_light</item>
<item name="customColorBrandD">@color/system_brand_d_light</item>
- <item name="customColorUnderSurface">@color/system_under_surface_light</item>
- <item name="customColorShadeActive">@color/system_shade_active_light</item>
+ <item name="customColorClockHour">@color/system_clock_hour_light</item>
+ <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+ <item name="customColorClockSecond">@color/system_clock_second_light</item>
<item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
<item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
- <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
<item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
<item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
- <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+ <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
<item name="customColorOverviewBackground">@color/system_overview_background_light</item>
+ <item name="customColorShadeActive">@color/system_shade_active_light</item>
+ <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+ <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+ <item name="customColorThemeApp">@color/system_theme_app_light</item>
+ <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
+ <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+ <item name="customColorWeatherTemp">@color/system_weather_temp_light</item>
+ <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
</style>
<!-- DeviceDefault theme for a window that should look like the Settings app. -->
@@ -4968,74 +5631,90 @@
<item name="colorListDivider">@color/list_divider_color_light</item>
<item name="opacityListDivider">@color/list_divider_opacity_device_default_light</item>
- <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
- <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
- <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_light</item>
- <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
- <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_light</item>
- <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_light</item>
- <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_light</item>
- <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_light</item>
- <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
- <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
- <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
- <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
- <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
- <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
- <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
+ <item name="materialColorBackground">@color/system_background_light</item>
+ <item name="materialColorControlActivated">@color/system_control_activated_light</item>
+ <item name="materialColorControlHighlight">@color/system_control_highlight_light</item>
+ <item name="materialColorControlNormal">@color/system_control_normal_light</item>
+ <item name="materialColorError">@color/system_error_light</item>
<item name="materialColorErrorContainer">@color/system_error_container_light</item>
- <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
- <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
- <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
- <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
- <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
- <item name="materialColorPrimaryContainer">@color/system_primary_container_light</item>
+ <item name="materialColorInverseOnSurface">@color/system_inverse_on_surface_light</item>
+ <item name="materialColorInversePrimary">@color/system_inverse_primary_light</item>
+ <item name="materialColorInverseSurface">@color/system_inverse_surface_light</item>
<item name="materialColorOnBackground">@color/system_on_background_light</item>
- <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
-
- <item name="materialColorOnSecondary">@color/system_on_secondary_light</item>
- <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item>
- <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item>
- <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item>
<item name="materialColorOnError">@color/system_on_error_light</item>
- <item name="materialColorSurface">@color/system_surface_light</item>
- <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item>
- <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item>
+ <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
+ <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
+ <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_light</item>
+ <item name="materialColorOnSecondary">@color/system_on_secondary_light</item>
+ <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_light</item>
+ <item name="materialColorOnSurface">@color/system_on_surface_light</item>
<item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_light</item>
+ <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item>
+ <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_light</item>
<item name="materialColorOutline">@color/system_outline_light</item>
<item name="materialColorOutlineVariant">@color/system_outline_variant_light</item>
- <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
- <item name="materialColorOnSurface">@color/system_on_surface_light</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
+ <item name="materialColorPaletteKeyColorNeutral">@color/system_palette_key_color_neutral_light</item>
+ <item name="materialColorPaletteKeyColorNeutralVariant">@color/system_palette_key_color_neutral_variant_light</item>
+ <item name="materialColorPaletteKeyColorPrimary">@color/system_palette_key_color_primary_light</item>
+ <item name="materialColorPaletteKeyColorSecondary">@color/system_palette_key_color_secondary_light</item>
+ <item name="materialColorPaletteKeyColorTertiary">@color/system_palette_key_color_tertiary_light</item>
<item name="materialColorPrimary">@color/system_primary_light</item>
+ <item name="materialColorPrimaryContainer">@color/system_primary_container_light</item>
+ <item name="materialColorScrim">@color/system_scrim_light</item>
<item name="materialColorSecondary">@color/system_secondary_light</item>
+ <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
+ <item name="materialColorShadow">@color/system_shadow_light</item>
+ <item name="materialColorSurface">@color/system_surface_light</item>
+ <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item>
+ <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
+ <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item>
+ <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item>
+ <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_light</item>
+ <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_light</item>
+ <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item>
+ <item name="materialColorSurfaceTint">@color/system_surface_tint_light</item>
+ <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
<item name="materialColorTertiary">@color/system_tertiary_light</item>
- <item name="materialColorError">@color/system_error_light</item>
-
- <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
- <item name="customColorClockHour">@color/system_clock_hour_light</item>
- <item name="customColorClockMinute">@color/system_clock_minute_light</item>
- <item name="customColorClockSecond">@color/system_clock_second_light</item>
- <item name="customColorThemeApp">@color/system_theme_app_light</item>
- <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
- <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
+ <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
+ <item name="materialColorTextHintInverse">@color/system_text_hint_inverse_light</item>
+ <item name="materialColorTextPrimaryInverse">@color/system_text_primary_inverse_light</item>
+ <item name="materialColorTextPrimaryInverseDisableOnly">@color/system_text_primary_inverse_disable_only_light</item>
+ <item name="materialColorTextSecondaryAndTertiaryInverse">@color/system_text_secondary_and_tertiary_inverse_light</item>
+ <item name="materialColorTextSecondaryAndTertiaryInverseDisabled">@color/system_text_secondary_and_tertiary_inverse_disabled_light</item>
+ <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
+ <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
+ <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
+ <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
+ <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
+ <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
+ <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
+ <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
+ <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
+ <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
+ <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
+ <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="customColorBrandA">@color/system_brand_a_light</item>
<item name="customColorBrandB">@color/system_brand_b_light</item>
<item name="customColorBrandC">@color/system_brand_c_light</item>
<item name="customColorBrandD">@color/system_brand_d_light</item>
- <item name="customColorUnderSurface">@color/system_under_surface_light</item>
- <item name="customColorShadeActive">@color/system_shade_active_light</item>
+ <item name="customColorClockHour">@color/system_clock_hour_light</item>
+ <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+ <item name="customColorClockSecond">@color/system_clock_second_light</item>
<item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
<item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
- <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
<item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
<item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
- <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+ <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
<item name="customColorOverviewBackground">@color/system_overview_background_light</item>
+ <item name="customColorShadeActive">@color/system_shade_active_light</item>
+ <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+ <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+ <item name="customColorThemeApp">@color/system_theme_app_light</item>
+ <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
+ <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+ <item name="customColorWeatherTemp">@color/system_weather_temp_light</item>
+ <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
</style>
<style name="Theme.DeviceDefault.SystemUI" parent="Theme.DeviceDefault.Light">
@@ -5072,74 +5751,90 @@
<!-- Toolbar attributes -->
<item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
- <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
- <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
- <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_light</item>
- <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
- <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_light</item>
- <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_light</item>
- <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_light</item>
- <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_light</item>
- <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
- <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
- <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
- <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
- <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
- <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
- <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
+ <item name="materialColorBackground">@color/system_background_light</item>
+ <item name="materialColorControlActivated">@color/system_control_activated_light</item>
+ <item name="materialColorControlHighlight">@color/system_control_highlight_light</item>
+ <item name="materialColorControlNormal">@color/system_control_normal_light</item>
+ <item name="materialColorError">@color/system_error_light</item>
<item name="materialColorErrorContainer">@color/system_error_container_light</item>
- <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
- <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
- <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
- <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
- <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
- <item name="materialColorPrimaryContainer">@color/system_primary_container_light</item>
+ <item name="materialColorInverseOnSurface">@color/system_inverse_on_surface_light</item>
+ <item name="materialColorInversePrimary">@color/system_inverse_primary_light</item>
+ <item name="materialColorInverseSurface">@color/system_inverse_surface_light</item>
<item name="materialColorOnBackground">@color/system_on_background_light</item>
- <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
-
- <item name="materialColorOnSecondary">@color/system_on_secondary_light</item>
- <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item>
- <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item>
- <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item>
<item name="materialColorOnError">@color/system_on_error_light</item>
- <item name="materialColorSurface">@color/system_surface_light</item>
- <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item>
- <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item>
+ <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
+ <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
+ <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_light</item>
+ <item name="materialColorOnSecondary">@color/system_on_secondary_light</item>
+ <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_light</item>
+ <item name="materialColorOnSurface">@color/system_on_surface_light</item>
<item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_light</item>
+ <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item>
+ <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_light</item>
<item name="materialColorOutline">@color/system_outline_light</item>
<item name="materialColorOutlineVariant">@color/system_outline_variant_light</item>
- <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
- <item name="materialColorOnSurface">@color/system_on_surface_light</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
+ <item name="materialColorPaletteKeyColorNeutral">@color/system_palette_key_color_neutral_light</item>
+ <item name="materialColorPaletteKeyColorNeutralVariant">@color/system_palette_key_color_neutral_variant_light</item>
+ <item name="materialColorPaletteKeyColorPrimary">@color/system_palette_key_color_primary_light</item>
+ <item name="materialColorPaletteKeyColorSecondary">@color/system_palette_key_color_secondary_light</item>
+ <item name="materialColorPaletteKeyColorTertiary">@color/system_palette_key_color_tertiary_light</item>
<item name="materialColorPrimary">@color/system_primary_light</item>
+ <item name="materialColorPrimaryContainer">@color/system_primary_container_light</item>
+ <item name="materialColorScrim">@color/system_scrim_light</item>
<item name="materialColorSecondary">@color/system_secondary_light</item>
+ <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
+ <item name="materialColorShadow">@color/system_shadow_light</item>
+ <item name="materialColorSurface">@color/system_surface_light</item>
+ <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item>
+ <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
+ <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item>
+ <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item>
+ <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_light</item>
+ <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_light</item>
+ <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item>
+ <item name="materialColorSurfaceTint">@color/system_surface_tint_light</item>
+ <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
<item name="materialColorTertiary">@color/system_tertiary_light</item>
- <item name="materialColorError">@color/system_error_light</item>
-
- <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
- <item name="customColorClockHour">@color/system_clock_hour_light</item>
- <item name="customColorClockMinute">@color/system_clock_minute_light</item>
- <item name="customColorClockSecond">@color/system_clock_second_light</item>
- <item name="customColorThemeApp">@color/system_theme_app_light</item>
- <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
- <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
+ <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
+ <item name="materialColorTextHintInverse">@color/system_text_hint_inverse_light</item>
+ <item name="materialColorTextPrimaryInverse">@color/system_text_primary_inverse_light</item>
+ <item name="materialColorTextPrimaryInverseDisableOnly">@color/system_text_primary_inverse_disable_only_light</item>
+ <item name="materialColorTextSecondaryAndTertiaryInverse">@color/system_text_secondary_and_tertiary_inverse_light</item>
+ <item name="materialColorTextSecondaryAndTertiaryInverseDisabled">@color/system_text_secondary_and_tertiary_inverse_disabled_light</item>
+ <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
+ <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
+ <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
+ <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
+ <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
+ <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
+ <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
+ <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
+ <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
+ <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
+ <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
+ <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="customColorBrandA">@color/system_brand_a_light</item>
<item name="customColorBrandB">@color/system_brand_b_light</item>
<item name="customColorBrandC">@color/system_brand_c_light</item>
<item name="customColorBrandD">@color/system_brand_d_light</item>
- <item name="customColorUnderSurface">@color/system_under_surface_light</item>
- <item name="customColorShadeActive">@color/system_shade_active_light</item>
+ <item name="customColorClockHour">@color/system_clock_hour_light</item>
+ <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+ <item name="customColorClockSecond">@color/system_clock_second_light</item>
<item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
<item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
- <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
<item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
<item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
- <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+ <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
<item name="customColorOverviewBackground">@color/system_overview_background_light</item>
+ <item name="customColorShadeActive">@color/system_shade_active_light</item>
+ <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+ <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+ <item name="customColorThemeApp">@color/system_theme_app_light</item>
+ <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
+ <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+ <item name="customColorWeatherTemp">@color/system_weather_temp_light</item>
+ <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
</style>
<style name="Theme.DeviceDefault.SystemUI.Dialog" parent="Theme.DeviceDefault.Light.Dialog">
@@ -5168,74 +5863,90 @@
<!-- Toolbar attributes -->
<item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
- <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
- <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
- <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_light</item>
- <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
- <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_light</item>
- <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_light</item>
- <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_light</item>
- <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_light</item>
- <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
- <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
- <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
- <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
- <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
- <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
- <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
+ <item name="materialColorBackground">@color/system_background_light</item>
+ <item name="materialColorControlActivated">@color/system_control_activated_light</item>
+ <item name="materialColorControlHighlight">@color/system_control_highlight_light</item>
+ <item name="materialColorControlNormal">@color/system_control_normal_light</item>
+ <item name="materialColorError">@color/system_error_light</item>
<item name="materialColorErrorContainer">@color/system_error_container_light</item>
- <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
- <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
- <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
- <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
- <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
- <item name="materialColorPrimaryContainer">@color/system_primary_container_light</item>
+ <item name="materialColorInverseOnSurface">@color/system_inverse_on_surface_light</item>
+ <item name="materialColorInversePrimary">@color/system_inverse_primary_light</item>
+ <item name="materialColorInverseSurface">@color/system_inverse_surface_light</item>
<item name="materialColorOnBackground">@color/system_on_background_light</item>
- <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
-
- <item name="materialColorOnSecondary">@color/system_on_secondary_light</item>
- <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item>
- <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item>
- <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item>
<item name="materialColorOnError">@color/system_on_error_light</item>
- <item name="materialColorSurface">@color/system_surface_light</item>
- <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item>
- <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item>
+ <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
+ <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
+ <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_light</item>
+ <item name="materialColorOnSecondary">@color/system_on_secondary_light</item>
+ <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_light</item>
+ <item name="materialColorOnSurface">@color/system_on_surface_light</item>
<item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_light</item>
+ <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item>
+ <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_light</item>
<item name="materialColorOutline">@color/system_outline_light</item>
<item name="materialColorOutlineVariant">@color/system_outline_variant_light</item>
- <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
- <item name="materialColorOnSurface">@color/system_on_surface_light</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
+ <item name="materialColorPaletteKeyColorNeutral">@color/system_palette_key_color_neutral_light</item>
+ <item name="materialColorPaletteKeyColorNeutralVariant">@color/system_palette_key_color_neutral_variant_light</item>
+ <item name="materialColorPaletteKeyColorPrimary">@color/system_palette_key_color_primary_light</item>
+ <item name="materialColorPaletteKeyColorSecondary">@color/system_palette_key_color_secondary_light</item>
+ <item name="materialColorPaletteKeyColorTertiary">@color/system_palette_key_color_tertiary_light</item>
<item name="materialColorPrimary">@color/system_primary_light</item>
+ <item name="materialColorPrimaryContainer">@color/system_primary_container_light</item>
+ <item name="materialColorScrim">@color/system_scrim_light</item>
<item name="materialColorSecondary">@color/system_secondary_light</item>
+ <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
+ <item name="materialColorShadow">@color/system_shadow_light</item>
+ <item name="materialColorSurface">@color/system_surface_light</item>
+ <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item>
+ <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
+ <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item>
+ <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item>
+ <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_light</item>
+ <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_light</item>
+ <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item>
+ <item name="materialColorSurfaceTint">@color/system_surface_tint_light</item>
+ <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
<item name="materialColorTertiary">@color/system_tertiary_light</item>
- <item name="materialColorError">@color/system_error_light</item>
-
- <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
- <item name="customColorClockHour">@color/system_clock_hour_light</item>
- <item name="customColorClockMinute">@color/system_clock_minute_light</item>
- <item name="customColorClockSecond">@color/system_clock_second_light</item>
- <item name="customColorThemeApp">@color/system_theme_app_light</item>
- <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
- <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
+ <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
+ <item name="materialColorTextHintInverse">@color/system_text_hint_inverse_light</item>
+ <item name="materialColorTextPrimaryInverse">@color/system_text_primary_inverse_light</item>
+ <item name="materialColorTextPrimaryInverseDisableOnly">@color/system_text_primary_inverse_disable_only_light</item>
+ <item name="materialColorTextSecondaryAndTertiaryInverse">@color/system_text_secondary_and_tertiary_inverse_light</item>
+ <item name="materialColorTextSecondaryAndTertiaryInverseDisabled">@color/system_text_secondary_and_tertiary_inverse_disabled_light</item>
+ <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
+ <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
+ <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
+ <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
+ <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
+ <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
+ <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
+ <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
+ <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
+ <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
+ <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
+ <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="customColorBrandA">@color/system_brand_a_light</item>
<item name="customColorBrandB">@color/system_brand_b_light</item>
<item name="customColorBrandC">@color/system_brand_c_light</item>
<item name="customColorBrandD">@color/system_brand_d_light</item>
- <item name="customColorUnderSurface">@color/system_under_surface_light</item>
- <item name="customColorShadeActive">@color/system_shade_active_light</item>
+ <item name="customColorClockHour">@color/system_clock_hour_light</item>
+ <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+ <item name="customColorClockSecond">@color/system_clock_second_light</item>
<item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
<item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
- <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
<item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
<item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
- <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+ <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
<item name="customColorOverviewBackground">@color/system_overview_background_light</item>
+ <item name="customColorShadeActive">@color/system_shade_active_light</item>
+ <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+ <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+ <item name="customColorThemeApp">@color/system_theme_app_light</item>
+ <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
+ <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+ <item name="customColorWeatherTemp">@color/system_weather_temp_light</item>
+ <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault_Settings_Dark} with no action bar -->
@@ -5286,73 +5997,90 @@
<!-- Toolbar attributes -->
<item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
- <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
- <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
- <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_dark</item>
- <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
- <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_dark</item>
- <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_dark</item>
- <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_dark</item>
- <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_dark</item>
- <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
- <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
- <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
- <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
- <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
- <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
- <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
+ <item name="materialColorBackground">@color/system_background_dark</item>
+ <item name="materialColorControlActivated">@color/system_control_activated_dark</item>
+ <item name="materialColorControlHighlight">@color/system_control_highlight_dark</item>
+ <item name="materialColorControlNormal">@color/system_control_normal_dark</item>
+ <item name="materialColorError">@color/system_error_dark</item>
<item name="materialColorErrorContainer">@color/system_error_container_dark</item>
- <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
- <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
- <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
- <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
- <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
- <item name="materialColorPrimaryContainer">@color/system_primary_container_dark</item>
+ <item name="materialColorInverseOnSurface">@color/system_inverse_on_surface_dark</item>
+ <item name="materialColorInversePrimary">@color/system_inverse_primary_dark</item>
+ <item name="materialColorInverseSurface">@color/system_inverse_surface_dark</item>
<item name="materialColorOnBackground">@color/system_on_background_dark</item>
- <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
- <item name="materialColorOnSecondary">@color/system_on_secondary_dark</item>
- <item name="materialColorOnTertiary">@color/system_on_tertiary_dark</item>
- <item name="materialColorSurfaceDim">@color/system_surface_dim_dark</item>
- <item name="materialColorSurfaceBright">@color/system_surface_bright_dark</item>
<item name="materialColorOnError">@color/system_on_error_dark</item>
- <item name="materialColorSurface">@color/system_surface_dark</item>
- <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_dark</item>
- <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_dark</item>
+ <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
+ <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
+ <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_dark</item>
+ <item name="materialColorOnSecondary">@color/system_on_secondary_dark</item>
+ <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_dark</item>
+ <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
<item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_dark</item>
+ <item name="materialColorOnTertiary">@color/system_on_tertiary_dark</item>
+ <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_dark</item>
<item name="materialColorOutline">@color/system_outline_dark</item>
<item name="materialColorOutlineVariant">@color/system_outline_variant_dark</item>
- <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
- <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
+ <item name="materialColorPaletteKeyColorNeutral">@color/system_palette_key_color_neutral_dark</item>
+ <item name="materialColorPaletteKeyColorNeutralVariant">@color/system_palette_key_color_neutral_variant_dark</item>
+ <item name="materialColorPaletteKeyColorPrimary">@color/system_palette_key_color_primary_dark</item>
+ <item name="materialColorPaletteKeyColorSecondary">@color/system_palette_key_color_secondary_dark</item>
+ <item name="materialColorPaletteKeyColorTertiary">@color/system_palette_key_color_tertiary_dark</item>
<item name="materialColorPrimary">@color/system_primary_dark</item>
+ <item name="materialColorPrimaryContainer">@color/system_primary_container_dark</item>
+ <item name="materialColorScrim">@color/system_scrim_dark</item>
<item name="materialColorSecondary">@color/system_secondary_dark</item>
+ <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
+ <item name="materialColorShadow">@color/system_shadow_dark</item>
+ <item name="materialColorSurface">@color/system_surface_dark</item>
+ <item name="materialColorSurfaceBright">@color/system_surface_bright_dark</item>
+ <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
+ <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_dark</item>
+ <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_dark</item>
+ <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_dark</item>
+ <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_dark</item>
+ <item name="materialColorSurfaceDim">@color/system_surface_dim_dark</item>
+ <item name="materialColorSurfaceTint">@color/system_surface_tint_dark</item>
+ <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
<item name="materialColorTertiary">@color/system_tertiary_dark</item>
- <item name="materialColorError">@color/system_error_dark</item>
-
- <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
- <item name="customColorClockHour">@color/system_clock_hour_dark</item>
- <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
- <item name="customColorClockSecond">@color/system_clock_second_dark</item>
- <item name="customColorThemeApp">@color/system_theme_app_dark</item>
- <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
- <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
- <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
+ <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
+ <item name="materialColorTextHintInverse">@color/system_text_hint_inverse_dark</item>
+ <item name="materialColorTextPrimaryInverse">@color/system_text_primary_inverse_dark</item>
+ <item name="materialColorTextPrimaryInverseDisableOnly">@color/system_text_primary_inverse_disable_only_dark</item>
+ <item name="materialColorTextSecondaryAndTertiaryInverse">@color/system_text_secondary_and_tertiary_inverse_dark</item>
+ <item name="materialColorTextSecondaryAndTertiaryInverseDisabled">@color/system_text_secondary_and_tertiary_inverse_disabled_dark</item>
+ <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
+ <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
+ <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
+ <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
+ <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
+ <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
+ <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
+ <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
+ <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
+ <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
+ <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
+ <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="customColorBrandA">@color/system_brand_a_dark</item>
<item name="customColorBrandB">@color/system_brand_b_dark</item>
<item name="customColorBrandC">@color/system_brand_c_dark</item>
<item name="customColorBrandD">@color/system_brand_d_dark</item>
- <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
- <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+ <item name="customColorClockHour">@color/system_clock_hour_dark</item>
+ <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
+ <item name="customColorClockSecond">@color/system_clock_second_dark</item>
<item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
<item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
- <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
<item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
<item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
- <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+ <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
<item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
+ <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+ <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+ <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
+ <item name="customColorThemeApp">@color/system_theme_app_dark</item>
+ <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
+ <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
+ <item name="customColorWeatherTemp">@color/system_weather_temp_dark</item>
+ <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
</style>
<style name="Theme.DeviceDefault.Settings.DialogBase" parent="Theme.Material.Light.BaseDialog">
@@ -5387,73 +6115,90 @@
<!-- Dialog attributes -->
<item name="alertDialogTheme">@style/Theme.DeviceDefault.Light.Dialog.Alert</item>
- <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
- <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
- <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_light</item>
- <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
- <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_light</item>
- <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_light</item>
- <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_light</item>
- <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_light</item>
- <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
- <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
- <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
- <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
- <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
- <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
- <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
+ <item name="materialColorBackground">@color/system_background_light</item>
+ <item name="materialColorControlActivated">@color/system_control_activated_light</item>
+ <item name="materialColorControlHighlight">@color/system_control_highlight_light</item>
+ <item name="materialColorControlNormal">@color/system_control_normal_light</item>
+ <item name="materialColorError">@color/system_error_light</item>
<item name="materialColorErrorContainer">@color/system_error_container_light</item>
- <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
- <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
- <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
- <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
- <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
- <item name="materialColorPrimaryContainer">@color/system_primary_container_light</item>
+ <item name="materialColorInverseOnSurface">@color/system_inverse_on_surface_light</item>
+ <item name="materialColorInversePrimary">@color/system_inverse_primary_light</item>
+ <item name="materialColorInverseSurface">@color/system_inverse_surface_light</item>
<item name="materialColorOnBackground">@color/system_on_background_light</item>
- <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
- <item name="materialColorOnSecondary">@color/system_on_secondary_light</item>
- <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item>
- <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item>
- <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item>
<item name="materialColorOnError">@color/system_on_error_light</item>
- <item name="materialColorSurface">@color/system_surface_light</item>
- <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item>
- <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item>
+ <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
+ <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
+ <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_light</item>
+ <item name="materialColorOnSecondary">@color/system_on_secondary_light</item>
+ <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_light</item>
+ <item name="materialColorOnSurface">@color/system_on_surface_light</item>
<item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_light</item>
+ <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item>
+ <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_light</item>
<item name="materialColorOutline">@color/system_outline_light</item>
<item name="materialColorOutlineVariant">@color/system_outline_variant_light</item>
- <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
- <item name="materialColorOnSurface">@color/system_on_surface_light</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
+ <item name="materialColorPaletteKeyColorNeutral">@color/system_palette_key_color_neutral_light</item>
+ <item name="materialColorPaletteKeyColorNeutralVariant">@color/system_palette_key_color_neutral_variant_light</item>
+ <item name="materialColorPaletteKeyColorPrimary">@color/system_palette_key_color_primary_light</item>
+ <item name="materialColorPaletteKeyColorSecondary">@color/system_palette_key_color_secondary_light</item>
+ <item name="materialColorPaletteKeyColorTertiary">@color/system_palette_key_color_tertiary_light</item>
<item name="materialColorPrimary">@color/system_primary_light</item>
+ <item name="materialColorPrimaryContainer">@color/system_primary_container_light</item>
+ <item name="materialColorScrim">@color/system_scrim_light</item>
<item name="materialColorSecondary">@color/system_secondary_light</item>
+ <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
+ <item name="materialColorShadow">@color/system_shadow_light</item>
+ <item name="materialColorSurface">@color/system_surface_light</item>
+ <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item>
+ <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
+ <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item>
+ <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item>
+ <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_light</item>
+ <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_light</item>
+ <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item>
+ <item name="materialColorSurfaceTint">@color/system_surface_tint_light</item>
+ <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
<item name="materialColorTertiary">@color/system_tertiary_light</item>
- <item name="materialColorError">@color/system_error_light</item>
-
- <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
- <item name="customColorClockHour">@color/system_clock_hour_light</item>
- <item name="customColorClockMinute">@color/system_clock_minute_light</item>
- <item name="customColorClockSecond">@color/system_clock_second_light</item>
- <item name="customColorThemeApp">@color/system_theme_app_light</item>
- <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
- <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
+ <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
+ <item name="materialColorTextHintInverse">@color/system_text_hint_inverse_light</item>
+ <item name="materialColorTextPrimaryInverse">@color/system_text_primary_inverse_light</item>
+ <item name="materialColorTextPrimaryInverseDisableOnly">@color/system_text_primary_inverse_disable_only_light</item>
+ <item name="materialColorTextSecondaryAndTertiaryInverse">@color/system_text_secondary_and_tertiary_inverse_light</item>
+ <item name="materialColorTextSecondaryAndTertiaryInverseDisabled">@color/system_text_secondary_and_tertiary_inverse_disabled_light</item>
+ <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
+ <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
+ <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
+ <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
+ <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
+ <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
+ <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
+ <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
+ <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
+ <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
+ <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
+ <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="customColorBrandA">@color/system_brand_a_light</item>
<item name="customColorBrandB">@color/system_brand_b_light</item>
<item name="customColorBrandC">@color/system_brand_c_light</item>
<item name="customColorBrandD">@color/system_brand_d_light</item>
- <item name="customColorUnderSurface">@color/system_under_surface_light</item>
- <item name="customColorShadeActive">@color/system_shade_active_light</item>
+ <item name="customColorClockHour">@color/system_clock_hour_light</item>
+ <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+ <item name="customColorClockSecond">@color/system_clock_second_light</item>
<item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
<item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
- <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
<item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
<item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
- <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+ <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
<item name="customColorOverviewBackground">@color/system_overview_background_light</item>
+ <item name="customColorShadeActive">@color/system_shade_active_light</item>
+ <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+ <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+ <item name="customColorThemeApp">@color/system_theme_app_light</item>
+ <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
+ <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+ <item name="customColorWeatherTemp">@color/system_weather_temp_light</item>
+ <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
</style>
<style name="Theme.DeviceDefault.Settings.Dialog" parent="Theme.DeviceDefault.Settings.DialogBase">
@@ -5528,73 +6273,90 @@
<!-- Toolbar attributes -->
<item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
- <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
- <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
- <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_light</item>
- <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
- <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_light</item>
- <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_light</item>
- <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_light</item>
- <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_light</item>
- <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
- <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
- <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
- <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
- <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
- <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
- <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
+ <item name="materialColorBackground">@color/system_background_light</item>
+ <item name="materialColorControlActivated">@color/system_control_activated_light</item>
+ <item name="materialColorControlHighlight">@color/system_control_highlight_light</item>
+ <item name="materialColorControlNormal">@color/system_control_normal_light</item>
+ <item name="materialColorError">@color/system_error_light</item>
<item name="materialColorErrorContainer">@color/system_error_container_light</item>
- <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
- <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
- <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
- <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
- <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
- <item name="materialColorPrimaryContainer">@color/system_primary_container_light</item>
+ <item name="materialColorInverseOnSurface">@color/system_inverse_on_surface_light</item>
+ <item name="materialColorInversePrimary">@color/system_inverse_primary_light</item>
+ <item name="materialColorInverseSurface">@color/system_inverse_surface_light</item>
<item name="materialColorOnBackground">@color/system_on_background_light</item>
- <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
- <item name="materialColorOnSecondary">@color/system_on_secondary_light</item>
- <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item>
- <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item>
- <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item>
<item name="materialColorOnError">@color/system_on_error_light</item>
- <item name="materialColorSurface">@color/system_surface_light</item>
- <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item>
- <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item>
+ <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
+ <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
+ <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_light</item>
+ <item name="materialColorOnSecondary">@color/system_on_secondary_light</item>
+ <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_light</item>
+ <item name="materialColorOnSurface">@color/system_on_surface_light</item>
<item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_light</item>
+ <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item>
+ <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_light</item>
<item name="materialColorOutline">@color/system_outline_light</item>
<item name="materialColorOutlineVariant">@color/system_outline_variant_light</item>
- <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
- <item name="materialColorOnSurface">@color/system_on_surface_light</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
+ <item name="materialColorPaletteKeyColorNeutral">@color/system_palette_key_color_neutral_light</item>
+ <item name="materialColorPaletteKeyColorNeutralVariant">@color/system_palette_key_color_neutral_variant_light</item>
+ <item name="materialColorPaletteKeyColorPrimary">@color/system_palette_key_color_primary_light</item>
+ <item name="materialColorPaletteKeyColorSecondary">@color/system_palette_key_color_secondary_light</item>
+ <item name="materialColorPaletteKeyColorTertiary">@color/system_palette_key_color_tertiary_light</item>
<item name="materialColorPrimary">@color/system_primary_light</item>
+ <item name="materialColorPrimaryContainer">@color/system_primary_container_light</item>
+ <item name="materialColorScrim">@color/system_scrim_light</item>
<item name="materialColorSecondary">@color/system_secondary_light</item>
+ <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
+ <item name="materialColorShadow">@color/system_shadow_light</item>
+ <item name="materialColorSurface">@color/system_surface_light</item>
+ <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item>
+ <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
+ <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item>
+ <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item>
+ <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_light</item>
+ <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_light</item>
+ <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item>
+ <item name="materialColorSurfaceTint">@color/system_surface_tint_light</item>
+ <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
<item name="materialColorTertiary">@color/system_tertiary_light</item>
- <item name="materialColorError">@color/system_error_light</item>
-
- <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
- <item name="customColorClockHour">@color/system_clock_hour_light</item>
- <item name="customColorClockMinute">@color/system_clock_minute_light</item>
- <item name="customColorClockSecond">@color/system_clock_second_light</item>
- <item name="customColorThemeApp">@color/system_theme_app_light</item>
- <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
- <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
+ <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
+ <item name="materialColorTextHintInverse">@color/system_text_hint_inverse_light</item>
+ <item name="materialColorTextPrimaryInverse">@color/system_text_primary_inverse_light</item>
+ <item name="materialColorTextPrimaryInverseDisableOnly">@color/system_text_primary_inverse_disable_only_light</item>
+ <item name="materialColorTextSecondaryAndTertiaryInverse">@color/system_text_secondary_and_tertiary_inverse_light</item>
+ <item name="materialColorTextSecondaryAndTertiaryInverseDisabled">@color/system_text_secondary_and_tertiary_inverse_disabled_light</item>
+ <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
+ <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
+ <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
+ <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
+ <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
+ <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
+ <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
+ <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
+ <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
+ <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
+ <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
+ <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="customColorBrandA">@color/system_brand_a_light</item>
<item name="customColorBrandB">@color/system_brand_b_light</item>
<item name="customColorBrandC">@color/system_brand_c_light</item>
<item name="customColorBrandD">@color/system_brand_d_light</item>
- <item name="customColorUnderSurface">@color/system_under_surface_light</item>
- <item name="customColorShadeActive">@color/system_shade_active_light</item>
+ <item name="customColorClockHour">@color/system_clock_hour_light</item>
+ <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+ <item name="customColorClockSecond">@color/system_clock_second_light</item>
<item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
<item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
- <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
<item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
<item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
- <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+ <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
<item name="customColorOverviewBackground">@color/system_overview_background_light</item>
+ <item name="customColorShadeActive">@color/system_shade_active_light</item>
+ <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+ <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+ <item name="customColorThemeApp">@color/system_theme_app_light</item>
+ <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
+ <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+ <item name="customColorWeatherTemp">@color/system_weather_temp_light</item>
+ <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
</style>
<style name="Theme.DeviceDefault.Settings.Dialog.Alert" parent="Theme.Material.Settings.Dialog.Alert">
@@ -5647,73 +6409,90 @@
<!-- Toolbar attributes -->
<item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
- <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
- <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
- <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_light</item>
- <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
- <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_light</item>
- <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_light</item>
- <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_light</item>
- <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_light</item>
- <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
- <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
- <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
- <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
- <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
- <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
- <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
+ <item name="materialColorBackground">@color/system_background_light</item>
+ <item name="materialColorControlActivated">@color/system_control_activated_light</item>
+ <item name="materialColorControlHighlight">@color/system_control_highlight_light</item>
+ <item name="materialColorControlNormal">@color/system_control_normal_light</item>
+ <item name="materialColorError">@color/system_error_light</item>
<item name="materialColorErrorContainer">@color/system_error_container_light</item>
- <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
- <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
- <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
- <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
- <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
- <item name="materialColorPrimaryContainer">@color/system_primary_container_light</item>
+ <item name="materialColorInverseOnSurface">@color/system_inverse_on_surface_light</item>
+ <item name="materialColorInversePrimary">@color/system_inverse_primary_light</item>
+ <item name="materialColorInverseSurface">@color/system_inverse_surface_light</item>
<item name="materialColorOnBackground">@color/system_on_background_light</item>
- <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
- <item name="materialColorOnSecondary">@color/system_on_secondary_light</item>
- <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item>
- <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item>
- <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item>
<item name="materialColorOnError">@color/system_on_error_light</item>
- <item name="materialColorSurface">@color/system_surface_light</item>
- <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item>
- <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item>
+ <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
+ <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
+ <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_light</item>
+ <item name="materialColorOnSecondary">@color/system_on_secondary_light</item>
+ <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_light</item>
+ <item name="materialColorOnSurface">@color/system_on_surface_light</item>
<item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_light</item>
+ <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item>
+ <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_light</item>
<item name="materialColorOutline">@color/system_outline_light</item>
<item name="materialColorOutlineVariant">@color/system_outline_variant_light</item>
- <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
- <item name="materialColorOnSurface">@color/system_on_surface_light</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
+ <item name="materialColorPaletteKeyColorNeutral">@color/system_palette_key_color_neutral_light</item>
+ <item name="materialColorPaletteKeyColorNeutralVariant">@color/system_palette_key_color_neutral_variant_light</item>
+ <item name="materialColorPaletteKeyColorPrimary">@color/system_palette_key_color_primary_light</item>
+ <item name="materialColorPaletteKeyColorSecondary">@color/system_palette_key_color_secondary_light</item>
+ <item name="materialColorPaletteKeyColorTertiary">@color/system_palette_key_color_tertiary_light</item>
<item name="materialColorPrimary">@color/system_primary_light</item>
+ <item name="materialColorPrimaryContainer">@color/system_primary_container_light</item>
+ <item name="materialColorScrim">@color/system_scrim_light</item>
<item name="materialColorSecondary">@color/system_secondary_light</item>
+ <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
+ <item name="materialColorShadow">@color/system_shadow_light</item>
+ <item name="materialColorSurface">@color/system_surface_light</item>
+ <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item>
+ <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
+ <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item>
+ <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item>
+ <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_light</item>
+ <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_light</item>
+ <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item>
+ <item name="materialColorSurfaceTint">@color/system_surface_tint_light</item>
+ <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
<item name="materialColorTertiary">@color/system_tertiary_light</item>
- <item name="materialColorError">@color/system_error_light</item>
-
- <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
- <item name="customColorClockHour">@color/system_clock_hour_light</item>
- <item name="customColorClockMinute">@color/system_clock_minute_light</item>
- <item name="customColorClockSecond">@color/system_clock_second_light</item>
- <item name="customColorThemeApp">@color/system_theme_app_light</item>
- <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
- <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
+ <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
+ <item name="materialColorTextHintInverse">@color/system_text_hint_inverse_light</item>
+ <item name="materialColorTextPrimaryInverse">@color/system_text_primary_inverse_light</item>
+ <item name="materialColorTextPrimaryInverseDisableOnly">@color/system_text_primary_inverse_disable_only_light</item>
+ <item name="materialColorTextSecondaryAndTertiaryInverse">@color/system_text_secondary_and_tertiary_inverse_light</item>
+ <item name="materialColorTextSecondaryAndTertiaryInverseDisabled">@color/system_text_secondary_and_tertiary_inverse_disabled_light</item>
+ <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
+ <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
+ <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
+ <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
+ <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
+ <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
+ <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
+ <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
+ <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
+ <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
+ <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
+ <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="customColorBrandA">@color/system_brand_a_light</item>
<item name="customColorBrandB">@color/system_brand_b_light</item>
<item name="customColorBrandC">@color/system_brand_c_light</item>
<item name="customColorBrandD">@color/system_brand_d_light</item>
- <item name="customColorUnderSurface">@color/system_under_surface_light</item>
- <item name="customColorShadeActive">@color/system_shade_active_light</item>
+ <item name="customColorClockHour">@color/system_clock_hour_light</item>
+ <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+ <item name="customColorClockSecond">@color/system_clock_second_light</item>
<item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
<item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
- <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
<item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
<item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
- <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+ <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
<item name="customColorOverviewBackground">@color/system_overview_background_light</item>
+ <item name="customColorShadeActive">@color/system_shade_active_light</item>
+ <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+ <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+ <item name="customColorThemeApp">@color/system_theme_app_light</item>
+ <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
+ <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+ <item name="customColorWeatherTemp">@color/system_weather_temp_light</item>
+ <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
</style>
<style name="Theme.DeviceDefault.Settings.Dialog.NoActionBar" parent="Theme.DeviceDefault.Light.Dialog.NoActionBar" />
@@ -5792,73 +6571,90 @@
<item name="colorAccentSecondary">@color/system_secondary_dark</item>
<item name="colorAccentTertiary">@color/system_tertiary_dark</item>
- <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
- <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
- <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_dark</item>
- <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
- <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_dark</item>
- <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_dark</item>
- <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_dark</item>
- <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_dark</item>
- <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
- <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
- <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
- <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
- <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
- <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
- <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
+ <item name="materialColorBackground">@color/system_background_dark</item>
+ <item name="materialColorControlActivated">@color/system_control_activated_dark</item>
+ <item name="materialColorControlHighlight">@color/system_control_highlight_dark</item>
+ <item name="materialColorControlNormal">@color/system_control_normal_dark</item>
+ <item name="materialColorError">@color/system_error_dark</item>
<item name="materialColorErrorContainer">@color/system_error_container_dark</item>
- <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
- <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
- <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
- <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
- <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
- <item name="materialColorPrimaryContainer">@color/system_primary_container_dark</item>
+ <item name="materialColorInverseOnSurface">@color/system_inverse_on_surface_dark</item>
+ <item name="materialColorInversePrimary">@color/system_inverse_primary_dark</item>
+ <item name="materialColorInverseSurface">@color/system_inverse_surface_dark</item>
<item name="materialColorOnBackground">@color/system_on_background_dark</item>
- <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
- <item name="materialColorOnSecondary">@color/system_on_secondary_dark</item>
- <item name="materialColorOnTertiary">@color/system_on_tertiary_dark</item>
- <item name="materialColorSurfaceDim">@color/system_surface_dim_dark</item>
- <item name="materialColorSurfaceBright">@color/system_surface_bright_dark</item>
<item name="materialColorOnError">@color/system_on_error_dark</item>
- <item name="materialColorSurface">@color/system_surface_dark</item>
- <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_dark</item>
- <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_dark</item>
+ <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
+ <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
+ <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_dark</item>
+ <item name="materialColorOnSecondary">@color/system_on_secondary_dark</item>
+ <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_dark</item>
+ <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
<item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_dark</item>
+ <item name="materialColorOnTertiary">@color/system_on_tertiary_dark</item>
+ <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_dark</item>
<item name="materialColorOutline">@color/system_outline_dark</item>
<item name="materialColorOutlineVariant">@color/system_outline_variant_dark</item>
- <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
- <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
+ <item name="materialColorPaletteKeyColorNeutral">@color/system_palette_key_color_neutral_dark</item>
+ <item name="materialColorPaletteKeyColorNeutralVariant">@color/system_palette_key_color_neutral_variant_dark</item>
+ <item name="materialColorPaletteKeyColorPrimary">@color/system_palette_key_color_primary_dark</item>
+ <item name="materialColorPaletteKeyColorSecondary">@color/system_palette_key_color_secondary_dark</item>
+ <item name="materialColorPaletteKeyColorTertiary">@color/system_palette_key_color_tertiary_dark</item>
<item name="materialColorPrimary">@color/system_primary_dark</item>
+ <item name="materialColorPrimaryContainer">@color/system_primary_container_dark</item>
+ <item name="materialColorScrim">@color/system_scrim_dark</item>
<item name="materialColorSecondary">@color/system_secondary_dark</item>
+ <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
+ <item name="materialColorShadow">@color/system_shadow_dark</item>
+ <item name="materialColorSurface">@color/system_surface_dark</item>
+ <item name="materialColorSurfaceBright">@color/system_surface_bright_dark</item>
+ <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
+ <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_dark</item>
+ <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_dark</item>
+ <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_dark</item>
+ <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_dark</item>
+ <item name="materialColorSurfaceDim">@color/system_surface_dim_dark</item>
+ <item name="materialColorSurfaceTint">@color/system_surface_tint_dark</item>
+ <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
<item name="materialColorTertiary">@color/system_tertiary_dark</item>
- <item name="materialColorError">@color/system_error_dark</item>
-
- <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
- <item name="customColorClockHour">@color/system_clock_hour_dark</item>
- <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
- <item name="customColorClockSecond">@color/system_clock_second_dark</item>
- <item name="customColorThemeApp">@color/system_theme_app_dark</item>
- <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
- <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
- <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
+ <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
+ <item name="materialColorTextHintInverse">@color/system_text_hint_inverse_dark</item>
+ <item name="materialColorTextPrimaryInverse">@color/system_text_primary_inverse_dark</item>
+ <item name="materialColorTextPrimaryInverseDisableOnly">@color/system_text_primary_inverse_disable_only_dark</item>
+ <item name="materialColorTextSecondaryAndTertiaryInverse">@color/system_text_secondary_and_tertiary_inverse_dark</item>
+ <item name="materialColorTextSecondaryAndTertiaryInverseDisabled">@color/system_text_secondary_and_tertiary_inverse_disabled_dark</item>
+ <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
+ <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
+ <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
+ <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
+ <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
+ <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
+ <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
+ <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
+ <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
+ <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
+ <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
+ <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="customColorBrandA">@color/system_brand_a_dark</item>
<item name="customColorBrandB">@color/system_brand_b_dark</item>
<item name="customColorBrandC">@color/system_brand_c_dark</item>
<item name="customColorBrandD">@color/system_brand_d_dark</item>
- <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
- <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+ <item name="customColorClockHour">@color/system_clock_hour_dark</item>
+ <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
+ <item name="customColorClockSecond">@color/system_clock_second_dark</item>
<item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
<item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
- <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
<item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
<item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
- <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+ <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
<item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
+ <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+ <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+ <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
+ <item name="customColorThemeApp">@color/system_theme_app_dark</item>
+ <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
+ <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
+ <item name="customColorWeatherTemp">@color/system_weather_temp_dark</item>
+ <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
</style>
<style name="ThemeOverlay.DeviceDefault.Accent.Light">
@@ -5867,73 +6663,90 @@
<item name="colorAccentSecondary">@color/system_secondary_dark</item>
<item name="colorAccentTertiary">@color/system_tertiary_dark</item>
- <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
- <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
- <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_light</item>
- <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
- <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_light</item>
- <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_light</item>
- <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_light</item>
- <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_light</item>
- <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
- <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
- <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
- <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
- <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
- <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
- <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
+ <item name="materialColorBackground">@color/system_background_light</item>
+ <item name="materialColorControlActivated">@color/system_control_activated_light</item>
+ <item name="materialColorControlHighlight">@color/system_control_highlight_light</item>
+ <item name="materialColorControlNormal">@color/system_control_normal_light</item>
+ <item name="materialColorError">@color/system_error_light</item>
<item name="materialColorErrorContainer">@color/system_error_container_light</item>
- <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
- <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
- <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
- <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
- <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
- <item name="materialColorPrimaryContainer">@color/system_primary_container_light</item>
+ <item name="materialColorInverseOnSurface">@color/system_inverse_on_surface_light</item>
+ <item name="materialColorInversePrimary">@color/system_inverse_primary_light</item>
+ <item name="materialColorInverseSurface">@color/system_inverse_surface_light</item>
<item name="materialColorOnBackground">@color/system_on_background_light</item>
- <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
- <item name="materialColorOnSecondary">@color/system_on_secondary_light</item>
- <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item>
- <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item>
- <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item>
<item name="materialColorOnError">@color/system_on_error_light</item>
- <item name="materialColorSurface">@color/system_surface_light</item>
- <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item>
- <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item>
+ <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
+ <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
+ <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_light</item>
+ <item name="materialColorOnSecondary">@color/system_on_secondary_light</item>
+ <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_light</item>
+ <item name="materialColorOnSurface">@color/system_on_surface_light</item>
<item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_light</item>
+ <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item>
+ <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_light</item>
<item name="materialColorOutline">@color/system_outline_light</item>
<item name="materialColorOutlineVariant">@color/system_outline_variant_light</item>
- <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
- <item name="materialColorOnSurface">@color/system_on_surface_light</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
+ <item name="materialColorPaletteKeyColorNeutral">@color/system_palette_key_color_neutral_light</item>
+ <item name="materialColorPaletteKeyColorNeutralVariant">@color/system_palette_key_color_neutral_variant_light</item>
+ <item name="materialColorPaletteKeyColorPrimary">@color/system_palette_key_color_primary_light</item>
+ <item name="materialColorPaletteKeyColorSecondary">@color/system_palette_key_color_secondary_light</item>
+ <item name="materialColorPaletteKeyColorTertiary">@color/system_palette_key_color_tertiary_light</item>
<item name="materialColorPrimary">@color/system_primary_light</item>
+ <item name="materialColorPrimaryContainer">@color/system_primary_container_light</item>
+ <item name="materialColorScrim">@color/system_scrim_light</item>
<item name="materialColorSecondary">@color/system_secondary_light</item>
+ <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
+ <item name="materialColorShadow">@color/system_shadow_light</item>
+ <item name="materialColorSurface">@color/system_surface_light</item>
+ <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item>
+ <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
+ <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item>
+ <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item>
+ <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_light</item>
+ <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_light</item>
+ <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item>
+ <item name="materialColorSurfaceTint">@color/system_surface_tint_light</item>
+ <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
<item name="materialColorTertiary">@color/system_tertiary_light</item>
- <item name="materialColorError">@color/system_error_light</item>
-
- <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
- <item name="customColorClockHour">@color/system_clock_hour_light</item>
- <item name="customColorClockMinute">@color/system_clock_minute_light</item>
- <item name="customColorClockSecond">@color/system_clock_second_light</item>
- <item name="customColorThemeApp">@color/system_theme_app_light</item>
- <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
- <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
+ <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
+ <item name="materialColorTextHintInverse">@color/system_text_hint_inverse_light</item>
+ <item name="materialColorTextPrimaryInverse">@color/system_text_primary_inverse_light</item>
+ <item name="materialColorTextPrimaryInverseDisableOnly">@color/system_text_primary_inverse_disable_only_light</item>
+ <item name="materialColorTextSecondaryAndTertiaryInverse">@color/system_text_secondary_and_tertiary_inverse_light</item>
+ <item name="materialColorTextSecondaryAndTertiaryInverseDisabled">@color/system_text_secondary_and_tertiary_inverse_disabled_light</item>
+ <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
+ <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
+ <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
+ <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
+ <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
+ <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
+ <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
+ <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
+ <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
+ <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
+ <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
+ <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="customColorBrandA">@color/system_brand_a_light</item>
<item name="customColorBrandB">@color/system_brand_b_light</item>
<item name="customColorBrandC">@color/system_brand_c_light</item>
<item name="customColorBrandD">@color/system_brand_d_light</item>
- <item name="customColorUnderSurface">@color/system_under_surface_light</item>
- <item name="customColorShadeActive">@color/system_shade_active_light</item>
+ <item name="customColorClockHour">@color/system_clock_hour_light</item>
+ <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+ <item name="customColorClockSecond">@color/system_clock_second_light</item>
<item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
<item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
- <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
<item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
<item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
- <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+ <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
<item name="customColorOverviewBackground">@color/system_overview_background_light</item>
+ <item name="customColorShadeActive">@color/system_shade_active_light</item>
+ <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+ <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+ <item name="customColorThemeApp">@color/system_theme_app_light</item>
+ <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
+ <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+ <item name="customColorWeatherTemp">@color/system_weather_temp_light</item>
+ <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
</style>
<!-- Theme overlay that replaces colorAccent with the colorAccent from {@link #Theme_DeviceDefault_DayNight}. -->
@@ -5946,73 +6759,90 @@
<item name="colorAccentSecondary">@color/system_secondary_dark</item>
<item name="colorAccentTertiary">@color/system_tertiary_dark</item>
- <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
- <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
- <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_dark</item>
- <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
- <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_dark</item>
- <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_dark</item>
- <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_dark</item>
- <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_dark</item>
- <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
- <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
- <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
- <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
- <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
- <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
- <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
+ <item name="materialColorBackground">@color/system_background_dark</item>
+ <item name="materialColorControlActivated">@color/system_control_activated_dark</item>
+ <item name="materialColorControlHighlight">@color/system_control_highlight_dark</item>
+ <item name="materialColorControlNormal">@color/system_control_normal_dark</item>
+ <item name="materialColorError">@color/system_error_dark</item>
<item name="materialColorErrorContainer">@color/system_error_container_dark</item>
- <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
- <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
- <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
- <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
- <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
- <item name="materialColorPrimaryContainer">@color/system_primary_container_dark</item>
+ <item name="materialColorInverseOnSurface">@color/system_inverse_on_surface_dark</item>
+ <item name="materialColorInversePrimary">@color/system_inverse_primary_dark</item>
+ <item name="materialColorInverseSurface">@color/system_inverse_surface_dark</item>
<item name="materialColorOnBackground">@color/system_on_background_dark</item>
- <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
- <item name="materialColorOnSecondary">@color/system_on_secondary_dark</item>
- <item name="materialColorOnTertiary">@color/system_on_tertiary_dark</item>
- <item name="materialColorSurfaceDim">@color/system_surface_dim_dark</item>
- <item name="materialColorSurfaceBright">@color/system_surface_bright_dark</item>
<item name="materialColorOnError">@color/system_on_error_dark</item>
- <item name="materialColorSurface">@color/system_surface_dark</item>
- <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_dark</item>
- <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_dark</item>
+ <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
+ <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
+ <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_dark</item>
+ <item name="materialColorOnSecondary">@color/system_on_secondary_dark</item>
+ <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_dark</item>
+ <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
<item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_dark</item>
+ <item name="materialColorOnTertiary">@color/system_on_tertiary_dark</item>
+ <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_dark</item>
<item name="materialColorOutline">@color/system_outline_dark</item>
<item name="materialColorOutlineVariant">@color/system_outline_variant_dark</item>
- <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
- <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
+ <item name="materialColorPaletteKeyColorNeutral">@color/system_palette_key_color_neutral_dark</item>
+ <item name="materialColorPaletteKeyColorNeutralVariant">@color/system_palette_key_color_neutral_variant_dark</item>
+ <item name="materialColorPaletteKeyColorPrimary">@color/system_palette_key_color_primary_dark</item>
+ <item name="materialColorPaletteKeyColorSecondary">@color/system_palette_key_color_secondary_dark</item>
+ <item name="materialColorPaletteKeyColorTertiary">@color/system_palette_key_color_tertiary_dark</item>
<item name="materialColorPrimary">@color/system_primary_dark</item>
+ <item name="materialColorPrimaryContainer">@color/system_primary_container_dark</item>
+ <item name="materialColorScrim">@color/system_scrim_dark</item>
<item name="materialColorSecondary">@color/system_secondary_dark</item>
+ <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
+ <item name="materialColorShadow">@color/system_shadow_dark</item>
+ <item name="materialColorSurface">@color/system_surface_dark</item>
+ <item name="materialColorSurfaceBright">@color/system_surface_bright_dark</item>
+ <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
+ <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_dark</item>
+ <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_dark</item>
+ <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_dark</item>
+ <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_dark</item>
+ <item name="materialColorSurfaceDim">@color/system_surface_dim_dark</item>
+ <item name="materialColorSurfaceTint">@color/system_surface_tint_dark</item>
+ <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
<item name="materialColorTertiary">@color/system_tertiary_dark</item>
- <item name="materialColorError">@color/system_error_dark</item>
-
- <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
- <item name="customColorClockHour">@color/system_clock_hour_dark</item>
- <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
- <item name="customColorClockSecond">@color/system_clock_second_dark</item>
- <item name="customColorThemeApp">@color/system_theme_app_dark</item>
- <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
- <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
- <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
+ <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
+ <item name="materialColorTextHintInverse">@color/system_text_hint_inverse_dark</item>
+ <item name="materialColorTextPrimaryInverse">@color/system_text_primary_inverse_dark</item>
+ <item name="materialColorTextPrimaryInverseDisableOnly">@color/system_text_primary_inverse_disable_only_dark</item>
+ <item name="materialColorTextSecondaryAndTertiaryInverse">@color/system_text_secondary_and_tertiary_inverse_dark</item>
+ <item name="materialColorTextSecondaryAndTertiaryInverseDisabled">@color/system_text_secondary_and_tertiary_inverse_disabled_dark</item>
+ <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
+ <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
+ <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
+ <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
+ <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
+ <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
+ <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
+ <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
+ <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
+ <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
+ <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
+ <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="customColorBrandA">@color/system_brand_a_dark</item>
<item name="customColorBrandB">@color/system_brand_b_dark</item>
<item name="customColorBrandC">@color/system_brand_c_dark</item>
<item name="customColorBrandD">@color/system_brand_d_dark</item>
- <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
- <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+ <item name="customColorClockHour">@color/system_clock_hour_dark</item>
+ <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
+ <item name="customColorClockSecond">@color/system_clock_second_dark</item>
<item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
<item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
- <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
<item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
<item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
- <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+ <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
<item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
+ <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+ <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+ <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
+ <item name="customColorThemeApp">@color/system_theme_app_dark</item>
+ <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
+ <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
+ <item name="customColorWeatherTemp">@color/system_weather_temp_dark</item>
+ <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
</style>
<style name="Theme.DeviceDefault.Light.Dialog.Alert.UserSwitchingDialog" parent="Theme.DeviceDefault.NoActionBar.Fullscreen">
@@ -6021,73 +6851,90 @@
<item name="layout_gravity">center</item>
<item name="windowAnimationStyle">@style/Animation.DeviceDefault.Dialog</item>
- <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
- <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
- <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_light</item>
- <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
- <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_light</item>
- <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_light</item>
- <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_light</item>
- <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_light</item>
- <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
- <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
- <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
- <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
- <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
- <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
- <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
+ <item name="materialColorBackground">@color/system_background_light</item>
+ <item name="materialColorControlActivated">@color/system_control_activated_light</item>
+ <item name="materialColorControlHighlight">@color/system_control_highlight_light</item>
+ <item name="materialColorControlNormal">@color/system_control_normal_light</item>
+ <item name="materialColorError">@color/system_error_light</item>
<item name="materialColorErrorContainer">@color/system_error_container_light</item>
- <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
- <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
- <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
- <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
- <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
- <item name="materialColorPrimaryContainer">@color/system_primary_container_light</item>
+ <item name="materialColorInverseOnSurface">@color/system_inverse_on_surface_light</item>
+ <item name="materialColorInversePrimary">@color/system_inverse_primary_light</item>
+ <item name="materialColorInverseSurface">@color/system_inverse_surface_light</item>
<item name="materialColorOnBackground">@color/system_on_background_light</item>
- <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
- <item name="materialColorOnSecondary">@color/system_on_secondary_light</item>
- <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item>
- <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item>
- <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item>
<item name="materialColorOnError">@color/system_on_error_light</item>
- <item name="materialColorSurface">@color/system_surface_light</item>
- <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item>
- <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item>
+ <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
+ <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
+ <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_light</item>
+ <item name="materialColorOnSecondary">@color/system_on_secondary_light</item>
+ <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_light</item>
+ <item name="materialColorOnSurface">@color/system_on_surface_light</item>
<item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_light</item>
+ <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item>
+ <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_light</item>
<item name="materialColorOutline">@color/system_outline_light</item>
<item name="materialColorOutlineVariant">@color/system_outline_variant_light</item>
- <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
- <item name="materialColorOnSurface">@color/system_on_surface_light</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
+ <item name="materialColorPaletteKeyColorNeutral">@color/system_palette_key_color_neutral_light</item>
+ <item name="materialColorPaletteKeyColorNeutralVariant">@color/system_palette_key_color_neutral_variant_light</item>
+ <item name="materialColorPaletteKeyColorPrimary">@color/system_palette_key_color_primary_light</item>
+ <item name="materialColorPaletteKeyColorSecondary">@color/system_palette_key_color_secondary_light</item>
+ <item name="materialColorPaletteKeyColorTertiary">@color/system_palette_key_color_tertiary_light</item>
<item name="materialColorPrimary">@color/system_primary_light</item>
+ <item name="materialColorPrimaryContainer">@color/system_primary_container_light</item>
+ <item name="materialColorScrim">@color/system_scrim_light</item>
<item name="materialColorSecondary">@color/system_secondary_light</item>
+ <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
+ <item name="materialColorShadow">@color/system_shadow_light</item>
+ <item name="materialColorSurface">@color/system_surface_light</item>
+ <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item>
+ <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
+ <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item>
+ <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item>
+ <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_light</item>
+ <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_light</item>
+ <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item>
+ <item name="materialColorSurfaceTint">@color/system_surface_tint_light</item>
+ <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
<item name="materialColorTertiary">@color/system_tertiary_light</item>
- <item name="materialColorError">@color/system_error_light</item>
-
- <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
- <item name="customColorClockHour">@color/system_clock_hour_light</item>
- <item name="customColorClockMinute">@color/system_clock_minute_light</item>
- <item name="customColorClockSecond">@color/system_clock_second_light</item>
- <item name="customColorThemeApp">@color/system_theme_app_light</item>
- <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
- <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
+ <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
+ <item name="materialColorTextHintInverse">@color/system_text_hint_inverse_light</item>
+ <item name="materialColorTextPrimaryInverse">@color/system_text_primary_inverse_light</item>
+ <item name="materialColorTextPrimaryInverseDisableOnly">@color/system_text_primary_inverse_disable_only_light</item>
+ <item name="materialColorTextSecondaryAndTertiaryInverse">@color/system_text_secondary_and_tertiary_inverse_light</item>
+ <item name="materialColorTextSecondaryAndTertiaryInverseDisabled">@color/system_text_secondary_and_tertiary_inverse_disabled_light</item>
+ <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
+ <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
+ <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
+ <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
+ <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
+ <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
+ <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
+ <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
+ <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
+ <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
+ <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
+ <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="customColorBrandA">@color/system_brand_a_light</item>
<item name="customColorBrandB">@color/system_brand_b_light</item>
<item name="customColorBrandC">@color/system_brand_c_light</item>
<item name="customColorBrandD">@color/system_brand_d_light</item>
- <item name="customColorUnderSurface">@color/system_under_surface_light</item>
- <item name="customColorShadeActive">@color/system_shade_active_light</item>
+ <item name="customColorClockHour">@color/system_clock_hour_light</item>
+ <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+ <item name="customColorClockSecond">@color/system_clock_second_light</item>
<item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
<item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
- <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
<item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
<item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
- <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+ <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
<item name="customColorOverviewBackground">@color/system_overview_background_light</item>
+ <item name="customColorShadeActive">@color/system_shade_active_light</item>
+ <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+ <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+ <item name="customColorThemeApp">@color/system_theme_app_light</item>
+ <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
+ <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+ <item name="customColorWeatherTemp">@color/system_weather_temp_light</item>
+ <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
</style>
<style name="Theme.DeviceDefault.Notification" parent="@style/Theme.Material.Notification">
@@ -6107,73 +6954,90 @@
<item name="textColorPrimary">@color/system_neutral1_900</item>
<item name="textColorSecondary">@color/system_neutral2_700</item>
- <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
- <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
- <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_dark</item>
- <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
- <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_dark</item>
- <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_dark</item>
- <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_dark</item>
- <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_dark</item>
- <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
- <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
- <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
- <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
- <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
- <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
- <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
+ <item name="materialColorBackground">@color/system_background_dark</item>
+ <item name="materialColorControlActivated">@color/system_control_activated_dark</item>
+ <item name="materialColorControlHighlight">@color/system_control_highlight_dark</item>
+ <item name="materialColorControlNormal">@color/system_control_normal_dark</item>
+ <item name="materialColorError">@color/system_error_dark</item>
<item name="materialColorErrorContainer">@color/system_error_container_dark</item>
- <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
- <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
- <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
- <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
- <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
- <item name="materialColorPrimaryContainer">@color/system_primary_container_dark</item>
+ <item name="materialColorInverseOnSurface">@color/system_inverse_on_surface_dark</item>
+ <item name="materialColorInversePrimary">@color/system_inverse_primary_dark</item>
+ <item name="materialColorInverseSurface">@color/system_inverse_surface_dark</item>
<item name="materialColorOnBackground">@color/system_on_background_dark</item>
- <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
- <item name="materialColorOnSecondary">@color/system_on_secondary_dark</item>
- <item name="materialColorOnTertiary">@color/system_on_tertiary_dark</item>
- <item name="materialColorSurfaceDim">@color/system_surface_dim_dark</item>
- <item name="materialColorSurfaceBright">@color/system_surface_bright_dark</item>
<item name="materialColorOnError">@color/system_on_error_dark</item>
- <item name="materialColorSurface">@color/system_surface_dark</item>
- <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_dark</item>
- <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_dark</item>
+ <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
+ <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
+ <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_dark</item>
+ <item name="materialColorOnSecondary">@color/system_on_secondary_dark</item>
+ <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_dark</item>
+ <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
<item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_dark</item>
+ <item name="materialColorOnTertiary">@color/system_on_tertiary_dark</item>
+ <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_dark</item>
<item name="materialColorOutline">@color/system_outline_dark</item>
<item name="materialColorOutlineVariant">@color/system_outline_variant_dark</item>
- <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
- <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
+ <item name="materialColorPaletteKeyColorNeutral">@color/system_palette_key_color_neutral_dark</item>
+ <item name="materialColorPaletteKeyColorNeutralVariant">@color/system_palette_key_color_neutral_variant_dark</item>
+ <item name="materialColorPaletteKeyColorPrimary">@color/system_palette_key_color_primary_dark</item>
+ <item name="materialColorPaletteKeyColorSecondary">@color/system_palette_key_color_secondary_dark</item>
+ <item name="materialColorPaletteKeyColorTertiary">@color/system_palette_key_color_tertiary_dark</item>
<item name="materialColorPrimary">@color/system_primary_dark</item>
+ <item name="materialColorPrimaryContainer">@color/system_primary_container_dark</item>
+ <item name="materialColorScrim">@color/system_scrim_dark</item>
<item name="materialColorSecondary">@color/system_secondary_dark</item>
+ <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
+ <item name="materialColorShadow">@color/system_shadow_dark</item>
+ <item name="materialColorSurface">@color/system_surface_dark</item>
+ <item name="materialColorSurfaceBright">@color/system_surface_bright_dark</item>
+ <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
+ <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_dark</item>
+ <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_dark</item>
+ <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_dark</item>
+ <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_dark</item>
+ <item name="materialColorSurfaceDim">@color/system_surface_dim_dark</item>
+ <item name="materialColorSurfaceTint">@color/system_surface_tint_dark</item>
+ <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
<item name="materialColorTertiary">@color/system_tertiary_dark</item>
- <item name="materialColorError">@color/system_error_dark</item>
-
- <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
- <item name="customColorClockHour">@color/system_clock_hour_dark</item>
- <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
- <item name="customColorClockSecond">@color/system_clock_second_dark</item>
- <item name="customColorThemeApp">@color/system_theme_app_dark</item>
- <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
- <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
- <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
+ <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
+ <item name="materialColorTextHintInverse">@color/system_text_hint_inverse_dark</item>
+ <item name="materialColorTextPrimaryInverse">@color/system_text_primary_inverse_dark</item>
+ <item name="materialColorTextPrimaryInverseDisableOnly">@color/system_text_primary_inverse_disable_only_dark</item>
+ <item name="materialColorTextSecondaryAndTertiaryInverse">@color/system_text_secondary_and_tertiary_inverse_dark</item>
+ <item name="materialColorTextSecondaryAndTertiaryInverseDisabled">@color/system_text_secondary_and_tertiary_inverse_disabled_dark</item>
+ <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
+ <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
+ <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
+ <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
+ <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
+ <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
+ <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
+ <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
+ <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
+ <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
+ <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
+ <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="customColorBrandA">@color/system_brand_a_dark</item>
<item name="customColorBrandB">@color/system_brand_b_dark</item>
<item name="customColorBrandC">@color/system_brand_c_dark</item>
<item name="customColorBrandD">@color/system_brand_d_dark</item>
- <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
- <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+ <item name="customColorClockHour">@color/system_clock_hour_dark</item>
+ <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
+ <item name="customColorClockSecond">@color/system_clock_second_dark</item>
<item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
<item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
- <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
<item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
<item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
- <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+ <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
<item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
+ <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+ <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+ <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
+ <item name="customColorThemeApp">@color/system_theme_app_dark</item>
+ <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
+ <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
+ <item name="customColorWeatherTemp">@color/system_weather_temp_dark</item>
+ <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
</style>
<style name="Theme.DeviceDefault.AutofillHalfScreenDialogList" parent="Theme.DeviceDefault.DayNight">
<item name="colorListDivider">@color/list_divider_opacity_device_default_light</item>
diff --git a/core/res/res/xml/bookmarks.xml b/core/res/res/xml/bookmarks.xml
index 22d0226..e735784 100644
--- a/core/res/res/xml/bookmarks.xml
+++ b/core/res/res/xml/bookmarks.xml
@@ -31,29 +31,37 @@
'u': Calculator
'y': YouTube
-->
-<bookmarks>
+<bookmarks xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
<bookmark
role="android.app.role.BROWSER"
- shortcut="b" />
+ androidprv:keycode="KEYCODE_B"
+ androidprv:modifierState="META" />
<bookmark
category="android.intent.category.APP_CONTACTS"
- shortcut="c" />
+ androidprv:keycode="KEYCODE_C"
+ androidprv:modifierState="META" />
<bookmark
category="android.intent.category.APP_EMAIL"
- shortcut="e" />
+ androidprv:keycode="KEYCODE_E"
+ androidprv:modifierState="META" />
<bookmark
category="android.intent.category.APP_CALENDAR"
- shortcut="k" />
+ androidprv:keycode="KEYCODE_K"
+ androidprv:modifierState="META" />
<bookmark
category="android.intent.category.APP_MAPS"
- shortcut="m" />
+ androidprv:keycode="KEYCODE_M"
+ androidprv:modifierState="META" />
<bookmark
category="android.intent.category.APP_MUSIC"
- shortcut="p" />
+ androidprv:keycode="KEYCODE_P"
+ androidprv:modifierState="META" />
<bookmark
role="android.app.role.SMS"
- shortcut="s" />
+ androidprv:keycode="KEYCODE_S"
+ androidprv:modifierState="META" />
<bookmark
category="android.intent.category.APP_CALCULATOR"
- shortcut="u" />
+ androidprv:keycode="KEYCODE_U"
+ androidprv:modifierState="META" />
</bookmarks>
diff --git a/core/tests/InputMethodCoreTests/src/android/view/inputmethod/EditorInfoTest.java b/core/tests/InputMethodCoreTests/src/android/view/inputmethod/EditorInfoTest.java
index 9dd196d..f62d420 100644
--- a/core/tests/InputMethodCoreTests/src/android/view/inputmethod/EditorInfoTest.java
+++ b/core/tests/InputMethodCoreTests/src/android/view/inputmethod/EditorInfoTest.java
@@ -507,7 +507,8 @@
+ "prefix: supportedHandwritingGestureTypes=(none)\n"
+ "prefix: supportedHandwritingGesturePreviewTypes=(none)\n"
+ "prefix: isStylusHandwritingEnabled=false\n"
- + "prefix: contentMimeTypes=null\n");
+ + "prefix: contentMimeTypes=null\n"
+ + "prefix: writingToolsEnabled=true\n");
}
@Test
@@ -539,6 +540,7 @@
info.hintLocales = LocaleList.forLanguageTags("en,es,zh");
info.contentMimeTypes = new String[] {"image/png"};
info.targetInputMethodUser = UserHandle.of(10);
+ info.setWritingToolsEnabled(false);
final StringBuilder sb = new StringBuilder();
info.dump(new StringBuilderPrinter(sb), "prefix2: ");
assertThat(sb.toString()).isEqualTo(
@@ -555,7 +557,8 @@
+ "prefix2: supportedHandwritingGesturePreviewTypes=SELECT\n"
+ "prefix2: isStylusHandwritingEnabled=" + isStylusHandwritingEnabled + "\n"
+ "prefix2: contentMimeTypes=[image/png]\n"
- + "prefix2: targetInputMethodUserId=10\n");
+ + "prefix2: targetInputMethodUserId=10\n"
+ + "prefix2: writingToolsEnabled=false\n");
}
@Test
@@ -576,7 +579,8 @@
+ "prefix: supportedHandwritingGestureTypes=(none)\n"
+ "prefix: supportedHandwritingGesturePreviewTypes=(none)\n"
+ "prefix: isStylusHandwritingEnabled=false\n"
- + "prefix: contentMimeTypes=null\n");
+ + "prefix: contentMimeTypes=null\n"
+ + "prefix: writingToolsEnabled=true\n");
}
@Test
@@ -621,4 +625,9 @@
infoCopy.extras.putString("testKey2", "testValue");
assertFalse(TEST_EDITOR_INFO.kindofEquals(infoCopy));
}
+
+ @Test
+ public void testWritingToolsEnabledbyDefault() {
+ assertTrue(TEST_EDITOR_INFO.isWritingToolsEnabled());
+ }
}
diff --git a/core/tests/coretests/BinderFrozenStateChangeCallbackTestApp/src/com/android/frameworks/coretests/bfscctestapp/BfsccTestAppCmdService.java b/core/tests/coretests/BinderFrozenStateChangeCallbackTestApp/src/com/android/frameworks/coretests/bfscctestapp/BfsccTestAppCmdService.java
index fe54aa8..945147d 100644
--- a/core/tests/coretests/BinderFrozenStateChangeCallbackTestApp/src/com/android/frameworks/coretests/bfscctestapp/BfsccTestAppCmdService.java
+++ b/core/tests/coretests/BinderFrozenStateChangeCallbackTestApp/src/com/android/frameworks/coretests/bfscctestapp/BfsccTestAppCmdService.java
@@ -18,6 +18,8 @@
import android.app.Service;
import android.content.Intent;
+import android.os.Handler;
+import android.os.HandlerExecutor;
import android.os.IBinder;
import android.os.RemoteException;
@@ -36,6 +38,7 @@
@Override
public void listenTo(IBinder binder) throws RemoteException {
binder.addFrozenStateChangeCallback(
+ new HandlerExecutor(Handler.getMain()),
(IBinder who, int state) -> mNotifications.offer(state));
}
diff --git a/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java b/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java
index a2598f6..2fc72e1 100644
--- a/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java
+++ b/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java
@@ -16,6 +16,7 @@
package android.app;
+import static android.app.Flags.FLAG_PIC_CACHE_NULLS;
import static android.app.Flags.FLAG_PIC_ISOLATE_CACHE_BY_UID;
import static android.app.PropertyInvalidatedCache.NONCE_UNSET;
import static android.app.PropertyInvalidatedCache.MODULE_BLUETOOTH;
@@ -229,7 +230,12 @@
@Override
public String apply(Integer qv) {
mRecomputeCount += 1;
- return "foo" + qv.toString();
+ // Special case for testing caches of nulls. Integers in the range 30-40 return null.
+ if (qv >= 30 && qv < 40) {
+ return null;
+ } else {
+ return "foo" + qv.toString();
+ }
}
int getRecomputeCount() {
@@ -643,4 +649,35 @@
Binder.restoreCallingWorkSource(token);
}
}
+
+ @RequiresFlagsEnabled(FLAG_PIC_CACHE_NULLS)
+ @Test
+ public void testCachingNulls() {
+ TestCache cache = new TestCache(new Args(MODULE_TEST)
+ .maxEntries(4).api("testCachingNulls").cacheNulls(true),
+ new TestQuery());
+ cache.invalidateCache();
+ assertEquals("foo1", cache.query(1));
+ assertEquals("foo2", cache.query(2));
+ assertEquals(null, cache.query(30));
+ assertEquals(3, cache.getRecomputeCount());
+ assertEquals("foo1", cache.query(1));
+ assertEquals("foo2", cache.query(2));
+ assertEquals(null, cache.query(30));
+ assertEquals(3, cache.getRecomputeCount());
+
+ cache = new TestCache(new Args(MODULE_TEST)
+ .maxEntries(4).api("testCachingNulls").cacheNulls(false),
+ new TestQuery());
+ cache.invalidateCache();
+ assertEquals("foo1", cache.query(1));
+ assertEquals("foo2", cache.query(2));
+ assertEquals(null, cache.query(30));
+ assertEquals(3, cache.getRecomputeCount());
+ assertEquals("foo1", cache.query(1));
+ assertEquals("foo2", cache.query(2));
+ assertEquals(null, cache.query(30));
+ // The recompute is 4 because nulls were not cached.
+ assertEquals(4, cache.getRecomputeCount());
+ }
}
diff --git a/core/tests/coretests/src/android/content/pm/SharedLibraryInfoTest.java b/core/tests/coretests/src/android/content/pm/SharedLibraryInfoTest.java
new file mode 100644
index 0000000..df5cd4e
--- /dev/null
+++ b/core/tests/coretests/src/android/content/pm/SharedLibraryInfoTest.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.Parcel;
+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 com.google.common.collect.ImmutableList;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.List;
+
+@Presubmit
+@RunWith(JUnit4.class)
+public final class SharedLibraryInfoTest {
+
+ @Rule
+ public final CheckFlagsRule checkFlagsRule =
+ DeviceFlagsValueProvider.createCheckFlagsRule();
+
+ private static final String LIBRARY_NAME = "name";
+ private static final long VERSION_MAJOR = 1L;
+ private static final List<String> CERT_DIGESTS = ImmutableList.of("digest1", "digest2");
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_SDK_DEPENDENCY_INSTALLER)
+ public void sharedLibraryInfo_serializedAndDeserialized_retainsCertDigestInfo() {
+ SharedLibraryInfo toParcel = new SharedLibraryInfo(LIBRARY_NAME, VERSION_MAJOR,
+ SharedLibraryInfo.TYPE_SDK_PACKAGE, CERT_DIGESTS);
+
+ SharedLibraryInfo fromParcel = parcelAndUnparcel(toParcel);
+
+ assertThat(fromParcel.getCertDigests().size()).isEqualTo(toParcel.getCertDigests().size());
+ assertThat(fromParcel.getCertDigests().get(0)).isEqualTo(toParcel.getCertDigests().get(0));
+ assertThat(fromParcel.getCertDigests().get(1)).isEqualTo(toParcel.getCertDigests().get(1));
+ }
+
+ private SharedLibraryInfo parcelAndUnparcel(SharedLibraryInfo sharedLibraryInfo) {
+ Parcel parcel = Parcel.obtain();
+ parcel.setDataPosition(0);
+ sharedLibraryInfo.writeToParcel(parcel, /* flags= */0);
+
+ parcel.setDataPosition(0);
+ return SharedLibraryInfo.CREATOR.createFromParcel(parcel);
+ }
+}
diff --git a/core/tests/coretests/src/android/content/pm/parsing/ApkLiteParseUtilsTest.java b/core/tests/coretests/src/android/content/pm/parsing/ApkLiteParseUtilsTest.java
index 0db49a7..ecacdb2 100644
--- a/core/tests/coretests/src/android/content/pm/parsing/ApkLiteParseUtilsTest.java
+++ b/core/tests/coretests/src/android/content/pm/parsing/ApkLiteParseUtilsTest.java
@@ -33,6 +33,7 @@
import android.os.FileUtils;
import android.os.ParcelFileDescriptor;
import android.platform.test.annotations.Presubmit;
+import android.platform.test.annotations.RequiresFlagsEnabled;
import android.util.ArraySet;
import android.util.PackageUtils;
@@ -61,6 +62,7 @@
import java.util.Set;
@Presubmit
+@RequiresFlagsEnabled(android.content.pm.Flags.FLAG_SDK_DEPENDENCY_INSTALLER)
public class ApkLiteParseUtilsTest {
@Rule
diff --git a/core/tests/coretests/src/android/hardware/display/DisplayManagerGlobalTest.java b/core/tests/coretests/src/android/hardware/display/DisplayManagerGlobalTest.java
index 9552c88..6a5224d 100644
--- a/core/tests/coretests/src/android/hardware/display/DisplayManagerGlobalTest.java
+++ b/core/tests/coretests/src/android/hardware/display/DisplayManagerGlobalTest.java
@@ -16,6 +16,11 @@
package android.hardware.display;
+import static android.hardware.display.DisplayManagerGlobal.EVENT_DISPLAY_STATE_CHANGED;
+import static android.hardware.display.DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_REFRESH_RATE;
+import static android.hardware.display.DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_STATE;
+
+import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
@@ -28,13 +33,19 @@
import android.os.Handler;
import android.os.RemoteException;
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.view.DisplayInfo;
import androidx.test.InstrumentationRegistry;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
+import com.android.server.display.feature.flags.Flags;
+
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
@@ -55,6 +66,10 @@
@RunWith(AndroidJUnit4.class)
public class DisplayManagerGlobalTest {
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule =
+ DeviceFlagsValueProvider.createCheckFlagsRule();
+
private static final long ALL_DISPLAY_EVENTS =
DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_ADDED
| DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_CHANGED
@@ -117,6 +132,33 @@
}
@Test
+ @RequiresFlagsEnabled(Flags.FLAG_DISPLAY_LISTENER_PERFORMANCE_IMPROVEMENTS)
+ public void testDisplayListenerIsCalled_WhenDisplayPropertyChangeEventOccurs()
+ throws RemoteException {
+ mDisplayManagerGlobal.registerDisplayListener(mListener, mHandler,
+ INTERNAL_EVENT_FLAG_DISPLAY_REFRESH_RATE
+ | INTERNAL_EVENT_FLAG_DISPLAY_STATE,
+ null);
+ Mockito.verify(mDisplayManager)
+ .registerCallbackWithEventMask(mCallbackCaptor.capture(), anyLong());
+ IDisplayManagerCallback callback = mCallbackCaptor.getValue();
+
+ int displayId = 1;
+
+ Mockito.reset(mListener);
+ callback.onDisplayEvent(displayId, DisplayManagerGlobal.EVENT_DISPLAY_REFRESH_RATE_CHANGED);
+ waitForHandler();
+ Mockito.verify(mListener).onDisplayChanged(eq(displayId));
+ Mockito.verifyNoMoreInteractions(mListener);
+
+ Mockito.reset(mListener);
+ callback.onDisplayEvent(displayId, EVENT_DISPLAY_STATE_CHANGED);
+ waitForHandler();
+ Mockito.verify(mListener).onDisplayChanged(eq(displayId));
+ Mockito.verifyNoMoreInteractions(mListener);
+ }
+
+ @Test
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.
@@ -231,6 +273,53 @@
verify(mListener2, never()).onDisplayChanged(anyInt());
}
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_DISPLAY_LISTENER_PERFORMANCE_IMPROVEMENTS)
+ public void testMapFlagsToInternalEventFlag() {
+ // Test public flags mapping
+ assertEquals(DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_ADDED,
+ mDisplayManagerGlobal
+ .mapFlagsToInternalEventFlag(DisplayManager.EVENT_FLAG_DISPLAY_ADDED, 0));
+ assertEquals(DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_CHANGED,
+ mDisplayManagerGlobal
+ .mapFlagsToInternalEventFlag(DisplayManager.EVENT_FLAG_DISPLAY_CHANGED, 0));
+ assertEquals(DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_REMOVED,
+ mDisplayManagerGlobal
+ .mapFlagsToInternalEventFlag(DisplayManager.EVENT_FLAG_DISPLAY_REMOVED, 0));
+ assertEquals(INTERNAL_EVENT_FLAG_DISPLAY_REFRESH_RATE,
+ mDisplayManagerGlobal
+ .mapFlagsToInternalEventFlag(
+ DisplayManager.EVENT_FLAG_DISPLAY_REFRESH_RATE,
+ 0));
+ assertEquals(DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_STATE,
+ mDisplayManagerGlobal
+ .mapFlagsToInternalEventFlag(
+ DisplayManager.EVENT_FLAG_DISPLAY_STATE,
+ 0));
+
+ // test private flags mapping
+ assertEquals(DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_CONNECTION_CHANGED,
+ mDisplayManagerGlobal
+ .mapFlagsToInternalEventFlag(0,
+ DisplayManager.PRIVATE_EVENT_FLAG_DISPLAY_CONNECTION_CHANGED));
+ assertEquals(DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_HDR_SDR_RATIO_CHANGED,
+ mDisplayManagerGlobal
+ .mapFlagsToInternalEventFlag(0,
+ DisplayManager.PRIVATE_EVENT_FLAG_HDR_SDR_RATIO_CHANGED));
+ assertEquals(DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_BRIGHTNESS_CHANGED,
+ mDisplayManagerGlobal
+ .mapFlagsToInternalEventFlag(0,
+ DisplayManager.PRIVATE_EVENT_FLAG_DISPLAY_BRIGHTNESS));
+
+ // Test both public and private flags mapping
+ assertEquals(DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_BRIGHTNESS_CHANGED
+ | INTERNAL_EVENT_FLAG_DISPLAY_REFRESH_RATE,
+ mDisplayManagerGlobal
+ .mapFlagsToInternalEventFlag(
+ DisplayManager.EVENT_FLAG_DISPLAY_REFRESH_RATE,
+ DisplayManager.PRIVATE_EVENT_FLAG_DISPLAY_BRIGHTNESS));
+ }
+
private void waitForHandler() {
mHandler.runWithScissors(() -> {
}, 0);
diff --git a/core/tests/coretests/src/android/os/BinderFrozenStateChangeNotificationTest.java b/core/tests/coretests/src/android/os/BinderFrozenStateChangeNotificationTest.java
index 195a18a..523fe1a 100644
--- a/core/tests/coretests/src/android/os/BinderFrozenStateChangeNotificationTest.java
+++ b/core/tests/coretests/src/android/os/BinderFrozenStateChangeNotificationTest.java
@@ -200,7 +200,7 @@
IBinder.FrozenStateChangeCallback callback =
(IBinder who, int state) -> results.offer(who);
try {
- binder.addFrozenStateChangeCallback(callback);
+ binder.addFrozenStateChangeCallback(new HandlerExecutor(Handler.getMain()), callback);
} catch (UnsupportedOperationException e) {
return;
}
@@ -227,7 +227,7 @@
final IBinder.FrozenStateChangeCallback callback =
(IBinder who, int state) ->
queue.offer(state == IBinder.FrozenStateChangeCallback.STATE_FROZEN);
- binder.addFrozenStateChangeCallback(callback);
+ binder.addFrozenStateChangeCallback(new HandlerExecutor(Handler.getMain()), callback);
return callback;
} catch (UnsupportedOperationException e) {
return null;
diff --git a/core/tests/coretests/src/android/os/OWNERS b/core/tests/coretests/src/android/os/OWNERS
index 6149382..4620cb8 100644
--- a/core/tests/coretests/src/android/os/OWNERS
+++ b/core/tests/coretests/src/android/os/OWNERS
@@ -12,3 +12,6 @@
# Caching
per-file IpcDataCache* = file:/PERFORMANCE_OWNERS
+
+# RemoteCallbackList
+per-file RemoteCallbackListTest.java = shayba@google.com
diff --git a/core/tests/coretests/src/com/android/internal/os/BinderDeathDispatcherTest.java b/core/tests/coretests/src/com/android/internal/os/BinderDeathDispatcherTest.java
index 67de25e..7511887 100644
--- a/core/tests/coretests/src/com/android/internal/os/BinderDeathDispatcherTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BinderDeathDispatcherTest.java
@@ -42,6 +42,7 @@
import org.junit.runner.RunWith;
import java.io.FileDescriptor;
+import java.util.concurrent.Executor;
@SmallTest
@RunWith(AndroidJUnit4.class)
@@ -125,7 +126,7 @@
}
@Override
- public void addFrozenStateChangeCallback(FrozenStateChangeCallback callback)
+ public void addFrozenStateChangeCallback(Executor e, FrozenStateChangeCallback callback)
throws RemoteException {
}
diff --git a/core/tests/systemproperties/src/android/os/SystemPropertiesTest.java b/core/tests/systemproperties/src/android/os/SystemPropertiesTest.java
index 75aca1b..7ce2ed8 100644
--- a/core/tests/systemproperties/src/android/os/SystemPropertiesTest.java
+++ b/core/tests/systemproperties/src/android/os/SystemPropertiesTest.java
@@ -23,10 +23,11 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
-import android.platform.test.ravenwood.RavenwoodConfig;
+import android.platform.test.ravenwood.RavenwoodRule;
import androidx.test.filters.SmallTest;
+import org.junit.Rule;
import org.junit.Test;
import java.util.Objects;
@@ -39,8 +40,8 @@
private static final String PERSIST_KEY = "persist.sys.testkey";
private static final String NONEXIST_KEY = "doesnotexist_2341431";
- @RavenwoodConfig.Config
- public static final RavenwoodConfig mRavenwood = new RavenwoodConfig.Builder()
+ @Rule
+ public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
.setSystemPropertyMutable(KEY, null)
.setSystemPropertyMutable(UNSET_KEY, null)
.setSystemPropertyMutable(PERSIST_KEY, null)
diff --git a/core/tests/vibrator/src/android/os/VibrationEffectTest.java b/core/tests/vibrator/src/android/os/VibrationEffectTest.java
index 1cd1190..ccc5108 100644
--- a/core/tests/vibrator/src/android/os/VibrationEffectTest.java
+++ b/core/tests/vibrator/src/android/os/VibrationEffectTest.java
@@ -414,10 +414,10 @@
@Test
@EnableFlags(Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
- public void computeLegacyPattern_effectsViaStartWaveformEnvelope() {
- // Effects created via startWaveformEnvelope are not expected to be converted to long[]
+ public void computeLegacyPattern_effectsViaWaveformEnvelopeBuilder() {
+ // Effects created via waveformEnvelopeBuilder are not expected to be converted to long[]
// patterns, as they are not configured to always play with the default amplitude.
- VibrationEffect effect = VibrationEffect.startWaveformEnvelope()
+ VibrationEffect effect = new VibrationEffect.WaveformEnvelopeBuilder()
.addControlPoint(/*amplitude=*/ 0.0f, /*frequencyHz=*/ 60f, /*timeMillis=*/ 20)
.addControlPoint(/*amplitude=*/ 0.3f, /*frequencyHz=*/ 100f, /*timeMillis=*/ 50)
.addControlPoint(/*amplitude=*/ 0.4f, /*frequencyHz=*/ 120f, /*timeMillis=*/ 80)
@@ -426,7 +426,8 @@
assertNull(effect.computeCreateWaveformOffOnTimingsOrNull());
- effect = VibrationEffect.startWaveformEnvelope(/*initialFrequency=*/ 60)
+ effect = new VibrationEffect.WaveformEnvelopeBuilder()
+ .setInitialFrequencyHz(/*initialFrequencyHz=*/ 60)
.addControlPoint(/*amplitude=*/ 0.0f, /*frequencyHz=*/ 60f, /*timeMillis=*/ 20)
.addControlPoint(/*amplitude=*/ 0.3f, /*frequencyHz=*/ 100f, /*timeMillis=*/ 50)
.addControlPoint(/*amplitude=*/ 0.4f, /*frequencyHz=*/ 120f, /*timeMillis=*/ 80)
@@ -644,7 +645,7 @@
@Test
@EnableFlags(Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
public void testValidateWaveformEnvelopeBuilder() {
- VibrationEffect.startWaveformEnvelope()
+ new VibrationEffect.WaveformEnvelopeBuilder()
.addControlPoint(/*amplitude=*/ 0.0f, /*frequencyHz=*/ 60f, /*timeMillis=*/ 20)
.addControlPoint(/*amplitude=*/ 0.3f, /*frequencyHz=*/ 100f, /*timeMillis=*/ 50)
.addControlPoint(/*amplitude=*/ 0.4f, /*frequencyHz=*/ 120f, /*timeMillis=*/ 80)
@@ -652,7 +653,8 @@
.build()
.validate();
- VibrationEffect.startWaveformEnvelope(/*initialFrequency=*/ 30)
+ new VibrationEffect.WaveformEnvelopeBuilder()
+ .setInitialFrequencyHz(/*initialFrequencyHz=*/ 30)
.addControlPoint(/*amplitude=*/ 0.0f, /*frequencyHz=*/ 60f, /*timeMillis=*/ 20)
.addControlPoint(/*amplitude=*/ 0.3f, /*frequencyHz=*/ 100f, /*timeMillis=*/ 50)
.addControlPoint(/*amplitude=*/ 0.4f, /*frequencyHz=*/ 120f, /*timeMillis=*/ 80)
@@ -661,13 +663,13 @@
.validate();
VibrationEffect.createRepeatingEffect(
- /*preamble=*/ VibrationEffect.startWaveformEnvelope()
+ /*preamble=*/ new VibrationEffect.WaveformEnvelopeBuilder()
.addControlPoint(/*amplitude=*/ 0.0f, /*frequencyHz=*/ 60f,
/*timeMillis=*/ 20)
.addControlPoint(/*amplitude=*/ 0.3f, /*frequencyHz=*/ 100f,
/*timeMillis=*/ 50)
.build(),
- /*repeatingEffect=*/ VibrationEffect.startWaveformEnvelope()
+ /*repeatingEffect=*/ new VibrationEffect.WaveformEnvelopeBuilder()
.addControlPoint(/*amplitude=*/ 0.0f, /*frequencyHz=*/ 60f,
/*timeMillis=*/ 20)
.addControlPoint(/*amplitude=*/ 0.5f, /*frequencyHz=*/ 150f,
@@ -676,7 +678,7 @@
).validate();
VibrationEffect.createRepeatingEffect(
- /*effect=*/ VibrationEffect.startWaveformEnvelope()
+ /*effect=*/ new VibrationEffect.WaveformEnvelopeBuilder()
.addControlPoint(/*amplitude=*/ 0.0f, /*frequencyHz=*/ 60f,
/*timeMillis=*/ 20)
.addControlPoint(/*amplitude=*/ 0.5f, /*frequencyHz=*/ 150f,
@@ -685,59 +687,70 @@
).validate();
assertThrows(IllegalStateException.class,
- () -> VibrationEffect.startWaveformEnvelope().build().validate());
+ () -> new VibrationEffect.WaveformEnvelopeBuilder().build().validate());
assertThrows(IllegalArgumentException.class,
- () -> VibrationEffect.startWaveformEnvelope()
+ () -> new VibrationEffect.WaveformEnvelopeBuilder()
.addControlPoint(/*amplitude=*/ -1.0f, /*frequencyHz=*/ 60f,
/*timeMillis=*/ 20)
.build()
.validate());
assertThrows(IllegalArgumentException.class,
- () -> VibrationEffect.startWaveformEnvelope()
+ () -> new VibrationEffect.WaveformEnvelopeBuilder()
.addControlPoint(/*amplitude=*/ 1.1f, /*frequencyHz=*/ 60f,
/*timeMillis=*/ 20)
.build()
.validate());
assertThrows(IllegalArgumentException.class,
- () -> VibrationEffect.startWaveformEnvelope()
+ () -> new VibrationEffect.WaveformEnvelopeBuilder()
.addControlPoint(/*amplitude=*/ 0.8f, /*frequencyHz=*/ 0f,
/*timeMillis=*/ 20)
.build()
.validate());
assertThrows(IllegalArgumentException.class,
- () -> VibrationEffect.startWaveformEnvelope()
+ () -> new VibrationEffect.WaveformEnvelopeBuilder()
.addControlPoint(/*amplitude=*/ 0.8f, /*frequencyHz=*/ 100f,
/*timeMillis=*/ 0)
.build()
.validate());
assertThrows(IllegalStateException.class,
- () -> VibrationEffect.startWaveformEnvelope(/*initialFrequency=*/30)
+ () -> new VibrationEffect.WaveformEnvelopeBuilder()
+ .setInitialFrequencyHz(/*initialFrequencyHz=*/ 30)
.build().validate());
assertThrows(IllegalArgumentException.class,
- () -> VibrationEffect.startWaveformEnvelope(/*initialFrequency=*/30)
+ () -> new VibrationEffect.WaveformEnvelopeBuilder()
+ .setInitialFrequencyHz(/*initialFrequencyHz=*/ 30)
.addControlPoint(/*amplitude=*/ -1.0f, /*frequencyHz=*/ 60f,
/*timeMillis=*/ 20)
.build()
.validate());
assertThrows(IllegalArgumentException.class,
- () -> VibrationEffect.startWaveformEnvelope(/*initialFrequency=*/30)
+ () -> new VibrationEffect.WaveformEnvelopeBuilder()
+ .setInitialFrequencyHz(/*initialFrequencyHz=*/ 30)
.addControlPoint(/*amplitude=*/ 1.1f, /*frequencyHz=*/ 60f,
/*timeMillis=*/ 20)
.build()
.validate());
assertThrows(IllegalArgumentException.class,
- () -> VibrationEffect.startWaveformEnvelope(/*initialFrequency=*/30)
+ () -> new VibrationEffect.WaveformEnvelopeBuilder()
+ .setInitialFrequencyHz(/*initialFrequencyHz=*/ 30)
.addControlPoint(/*amplitude=*/ 0.8f, /*frequencyHz=*/ 0f,
/*timeMillis=*/ 20)
.build()
.validate());
assertThrows(IllegalArgumentException.class,
- () -> VibrationEffect.startWaveformEnvelope(/*initialFrequency=*/30)
+ () -> new VibrationEffect.WaveformEnvelopeBuilder()
+ .setInitialFrequencyHz(/*initialFrequencyHz=*/ 30)
.addControlPoint(/*amplitude=*/ 0.8f, /*frequencyHz=*/ 100f,
/*timeMillis=*/ 0)
.build()
.validate());
+ assertThrows(IllegalArgumentException.class,
+ () -> new VibrationEffect.WaveformEnvelopeBuilder()
+ .setInitialFrequencyHz(/*initialFrequencyHz=*/ 0)
+ .addControlPoint(/*amplitude=*/ 0.8f, /*frequencyHz=*/ 100f,
+ /*timeMillis=*/ 20)
+ .build().validate());
}
@Test
@@ -1381,14 +1394,15 @@
@Test
@EnableFlags(Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
public void testIsHapticFeedbackCandidate_longEnvelopeEffects_notCandidates() {
- assertFalse(VibrationEffect.startWaveformEnvelope()
+ assertFalse(new VibrationEffect.WaveformEnvelopeBuilder()
.addControlPoint(/*amplitude=*/ 0.0f, /*frequencyHz=*/ 60f, /*timeMillis=*/ 200)
.addControlPoint(/*amplitude=*/ 0.3f, /*frequencyHz=*/ 100f, /*timeMillis=*/ 500)
.addControlPoint(/*amplitude=*/ 0.4f, /*frequencyHz=*/ 120f, /*timeMillis=*/ 800)
.addControlPoint(/*amplitude=*/ 0.0f, /*frequencyHz=*/ 120f, /*timeMillis=*/ 400)
.build()
.isHapticFeedbackCandidate());
- assertFalse(VibrationEffect.startWaveformEnvelope(/*initialFrequency=*/ 40)
+ assertFalse(new VibrationEffect.WaveformEnvelopeBuilder()
+ .setInitialFrequencyHz(/*initialFrequencyHz=*/ 40)
.addControlPoint(/*amplitude=*/ 0.0f, /*frequencyHz=*/ 60f, /*timeMillis=*/ 200)
.addControlPoint(/*amplitude=*/ 0.3f, /*frequencyHz=*/ 100f, /*timeMillis=*/ 500)
.addControlPoint(/*amplitude=*/ 0.4f, /*frequencyHz=*/ 120f, /*timeMillis=*/ 800)
@@ -1413,13 +1427,14 @@
@Test
@EnableFlags(Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
public void testIsHapticFeedbackCandidate_shortEnvelopeEffects_areCandidates() {
- assertTrue(VibrationEffect.startWaveformEnvelope()
+ assertTrue(new VibrationEffect.WaveformEnvelopeBuilder()
.addControlPoint(/*amplitude=*/ 0.3f, /*frequencyHz=*/ 100f, /*timeMillis=*/ 500)
.addControlPoint(/*amplitude=*/ 0.4f, /*frequencyHz=*/ 120f, /*timeMillis=*/ 400)
.addControlPoint(/*amplitude=*/ 0.0f, /*frequencyHz=*/ 120f, /*timeMillis=*/ 100)
.build()
.isHapticFeedbackCandidate());
- assertTrue(VibrationEffect.startWaveformEnvelope(/*initialFrequency=*/ 30)
+ assertTrue(new VibrationEffect.WaveformEnvelopeBuilder()
+ .setInitialFrequencyHz(/*initialFrequencyHz=*/ 30)
.addControlPoint(/*amplitude=*/ 0.3f, /*frequencyHz=*/ 100f, /*timeMillis=*/ 500)
.addControlPoint(/*amplitude=*/ 0.4f, /*frequencyHz=*/ 120f, /*timeMillis=*/ 400)
.addControlPoint(/*amplitude=*/ 0.0f, /*frequencyHz=*/ 120f, /*timeMillis=*/ 100)
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 541ca60..329e5de 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -93,6 +93,10 @@
<permission name="android.permission.INTERACT_ACROSS_USERS"/>
</privapp-permissions>
+ <privapp-permissions package="com.android.media.quality">
+ <permission name="android.permission.OBSERVE_PICTURE_PROFILES"/>
+ </privapp-permissions>
+
<privapp-permissions package="com.android.mtp">
<permission name="android.permission.ACCESS_MTP"/>
<permission name="android.permission.MANAGE_USB"/>
@@ -262,6 +266,8 @@
<!-- BLUETOOTH_PRIVILEGED is needed for test only -->
<permission name="android.permission.BLUETOOTH_PRIVILEGED"/>
<permission name="android.permission.BIND_APPWIDGET"/>
+ <!-- Needed for CTS tests only -->
+ <permission name="android.permission.OBSERVE_PICTURE_PROFILES"/>
<permission name="android.permission.CHANGE_APP_IDLE_STATE"/>
<permission name="android.permission.CHANGE_COMPONENT_ENABLED_STATE"/>
<permission name="android.permission.CHANGE_CONFIGURATION"/>
@@ -527,6 +533,8 @@
<permission name="com.android.cellbroadcastservice.FULL_ACCESS_CELL_BROADCAST_HISTORY" />
<!-- Permission required for ATS test - CarDevicePolicyManagerTest -->
<permission name="android.permission.LOCK_DEVICE" />
+ <!-- Permission required for AuthenticationPolicyManagerTest -->
+ <permission name="android.permission.MANAGE_SECURE_LOCK_DEVICE" />
<!-- Permissions required for CTS test - CtsSafetyCenterTestCases -->
<permission name="android.permission.SEND_SAFETY_CENTER_UPDATE" />
<permission name="android.permission.READ_SAFETY_CENTER_STATUS" />
@@ -597,6 +605,9 @@
<!-- Permissions required for CTS test - SettingsPreferenceServiceClientTest -->
<permission name="android.permission.READ_SYSTEM_PREFERENCES" />
<permission name="android.permission.WRITE_SYSTEM_PREFERENCES" />
+ <!-- Permission required for CTS test - ForensicManagerTest -->
+ <permission name="android.permission.READ_FORENSIC_STATE" />
+ <permission name="android.permission.MANAGE_FORENSIC_STATE" />
</privapp-permissions>
<privapp-permissions package="com.android.statementservice">
diff --git a/graphics/java/android/graphics/BlendMode.java b/graphics/java/android/graphics/BlendMode.java
index 5c294aa..c6ae680 100644
--- a/graphics/java/android/graphics/BlendMode.java
+++ b/graphics/java/android/graphics/BlendMode.java
@@ -571,10 +571,10 @@
}
@NonNull
- private final Xfermode mXfermode;
+ private final PorterDuffXfermode mXfermode;
BlendMode(int mode) {
- mXfermode = new Xfermode();
+ mXfermode = new PorterDuffXfermode();
mXfermode.porterDuffMode = mode;
}
@@ -582,7 +582,7 @@
* @hide
*/
@NonNull
- public Xfermode getXfermode() {
+ public PorterDuffXfermode getXfermode() {
return mXfermode;
}
}
diff --git a/graphics/java/android/graphics/ComposeShader.java b/graphics/java/android/graphics/ComposeShader.java
index 977aeaa..e714568 100644
--- a/graphics/java/android/graphics/ComposeShader.java
+++ b/graphics/java/android/graphics/ComposeShader.java
@@ -40,9 +40,12 @@
* @param mode The mode that combines the colors from the two shaders. If mode
* is null, then SRC_OVER is assumed.
*/
+ //TODO(358126864): allow a ComposeShader to accept a RuntimeXfermode
@Deprecated
public ComposeShader(@NonNull Shader shaderA, @NonNull Shader shaderB, @NonNull Xfermode mode) {
- this(shaderA, shaderB, mode.porterDuffMode);
+ this(shaderA, shaderB,
+ mode instanceof PorterDuffXfermode ? ((PorterDuffXfermode) mode).porterDuffMode
+ : BlendMode.SRC_OVER.getXfermode().porterDuffMode);
}
/**
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index 56bb0f0..9bf4d65 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -71,6 +71,7 @@
private long mNativePaint;
private long mNativeShader;
private long mNativeColorFilter;
+ private long mNativeXfermode;
// Use a Holder to allow static initialization of Paint in the boot image.
private static class NoImagePreloadHolder {
@@ -735,6 +736,7 @@
mPathEffect = null;
mShader = null;
mNativeShader = 0;
+ mNativeXfermode = 0;
mTypeface = null;
mXfermode = null;
@@ -780,6 +782,7 @@
mNativeShader = paint.mNativeShader;
mTypeface = paint.mTypeface;
mXfermode = paint.mXfermode;
+ mNativeXfermode = paint.mNativeXfermode;
mHasCompatScaling = paint.mHasCompatScaling;
mCompatScaling = paint.mCompatScaling;
@@ -815,7 +818,7 @@
*
* Note: Although this method is |synchronized|, this is simply so it
* is not thread-hostile to multiple threads calling this method. It
- * is still unsafe to attempt to change the Shader/ColorFilter while
+ * is still unsafe to attempt to change the Shader/ColorFilter/Xfermode while
* another thread attempts to access the native object.
*
* @hide
@@ -833,6 +836,15 @@
mNativeColorFilter = newNativeColorFilter;
nSetColorFilter(mNativePaint, mNativeColorFilter);
}
+ if (com.android.graphics.hwui.flags.Flags.runtimeColorFiltersBlenders()) {
+ if (mXfermode instanceof RuntimeXfermode) {
+ long newNativeXfermode = ((RuntimeXfermode) mXfermode).createNativeInstance();
+ if (newNativeXfermode != mNativeXfermode) {
+ mNativeXfermode = newNativeXfermode;
+ nSetXfermode(mNativePaint, mNativeXfermode);
+ }
+ }
+ }
return mNativePaint;
}
@@ -1427,16 +1439,17 @@
}
/**
- * Get the paint's blend mode object.
+ * Get the paint's blend mode object. Will return null if there is a Xfermode applied that
+ * cannot be represented by a blend mode (i.e. a custom {@code RuntimeXfermode}
*
* @return the paint's blend mode (or null)
*/
@Nullable
public BlendMode getBlendMode() {
- if (mXfermode == null) {
+ if (mXfermode == null || !(mXfermode instanceof PorterDuffXfermode)) {
return null;
} else {
- return BlendMode.fromValue(mXfermode.porterDuffMode);
+ return BlendMode.fromValue(((PorterDuffXfermode) mXfermode).porterDuffMode);
}
}
@@ -1459,8 +1472,17 @@
@Nullable
private Xfermode installXfermode(Xfermode xfermode) {
- int newMode = xfermode != null ? xfermode.porterDuffMode : Xfermode.DEFAULT;
- int curMode = mXfermode != null ? mXfermode.porterDuffMode : Xfermode.DEFAULT;
+ if (com.android.graphics.hwui.flags.Flags.runtimeColorFiltersBlenders()) {
+ if (xfermode instanceof RuntimeXfermode) {
+ mXfermode = xfermode;
+ nSetXfermode(mNativePaint, ((RuntimeXfermode) xfermode).createNativeInstance());
+ return xfermode;
+ }
+ }
+ int newMode = (xfermode instanceof PorterDuffXfermode)
+ ? ((PorterDuffXfermode) xfermode).porterDuffMode : PorterDuffXfermode.DEFAULT;
+ int curMode = (mXfermode instanceof PorterDuffXfermode)
+ ? ((PorterDuffXfermode) mXfermode).porterDuffMode : PorterDuffXfermode.DEFAULT;
if (newMode != curMode) {
nSetXfermode(mNativePaint, newMode);
}
@@ -3823,6 +3845,8 @@
@CriticalNative
private static native void nSetXfermode(long paintPtr, int xfermode);
@CriticalNative
+ private static native void nSetXfermode(long paintPtr, long xfermodePtr);
+ @CriticalNative
private static native long nSetPathEffect(long paintPtr, long effect);
@CriticalNative
private static native long nSetMaskFilter(long paintPtr, long maskfilter);
diff --git a/graphics/java/android/graphics/PorterDuffXfermode.java b/graphics/java/android/graphics/PorterDuffXfermode.java
index ff9ff8b..83d0507 100644
--- a/graphics/java/android/graphics/PorterDuffXfermode.java
+++ b/graphics/java/android/graphics/PorterDuffXfermode.java
@@ -29,6 +29,9 @@
*
* @param mode The porter-duff mode that is applied
*/
+ static final int DEFAULT = PorterDuff.Mode.SRC_OVER.nativeInt;
+ int porterDuffMode = DEFAULT;
+ PorterDuffXfermode() {}
public PorterDuffXfermode(PorterDuff.Mode mode) {
porterDuffMode = mode.nativeInt;
}
diff --git a/graphics/java/android/graphics/RuntimeColorFilter.java b/graphics/java/android/graphics/RuntimeColorFilter.java
index 52724ce..d112f71 100644
--- a/graphics/java/android/graphics/RuntimeColorFilter.java
+++ b/graphics/java/android/graphics/RuntimeColorFilter.java
@@ -283,6 +283,23 @@
nativeUpdateChild(getNativeInstance(), filterName, colorFilter.getNativeInstance());
}
+ /**
+ * Assigns the uniform xfermode to the provided xfermode parameter. If the shader program does
+ * not have a uniform xfermode with that name then an IllegalArgumentException is thrown.
+ *
+ * @param xfermodeName name matching the uniform declared in the AGSL program
+ * @param xfermode filter passed into the AGSL program for sampling
+ */
+ public void setInputXfermode(@NonNull String xfermodeName, @NonNull RuntimeXfermode xfermode) {
+ if (xfermodeName == null) {
+ throw new NullPointerException("The xfermodeName parameter must not be null");
+ }
+ if (xfermode == null) {
+ throw new NullPointerException("The xfermode parameter must not be null");
+ }
+ nativeUpdateChild(getNativeInstance(), xfermodeName, xfermode.createNativeInstance());
+ }
+
/** @hide */
@Override
protected long createNativeInstance() {
diff --git a/graphics/java/android/graphics/RuntimeShader.java b/graphics/java/android/graphics/RuntimeShader.java
index 78d257f..6316c1f 100644
--- a/graphics/java/android/graphics/RuntimeShader.java
+++ b/graphics/java/android/graphics/RuntimeShader.java
@@ -18,10 +18,13 @@
import android.annotation.ColorInt;
import android.annotation.ColorLong;
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.util.ArrayMap;
import android.view.Window;
+import com.android.graphics.hwui.flags.Flags;
+
import libcore.util.NativeAllocationRegistry;
/**
@@ -525,6 +528,45 @@
discardNativeInstance();
}
+ /**
+ * Assigns the uniform color filter to the provided color filter parameter. If the shader
+ * program does not have a uniform color filter with that name then an IllegalArgumentException
+ * is thrown.
+ *
+ * @param filterName name matching the uniform declared in the AGSL program
+ * @param colorFilter filter passed into the AGSL program for sampling
+ */
+ @FlaggedApi(Flags.FLAG_RUNTIME_COLOR_FILTERS_BLENDERS)
+ public void setInputColorFilter(@NonNull String filterName, @NonNull ColorFilter colorFilter) {
+ if (filterName == null) {
+ throw new NullPointerException("The filterName parameter must not be null");
+ }
+ if (colorFilter == null) {
+ throw new NullPointerException("The colorFilter parameter must not be null");
+ }
+ nativeUpdateChild(mNativeInstanceRuntimeShaderBuilder, filterName,
+ colorFilter.getNativeInstance());
+ }
+
+ /**
+ * Assigns the uniform xfermode to the provided xfermode parameter. If the shader program does
+ * not have a uniform xfermode with that name then an IllegalArgumentException is thrown.
+ *
+ * @param xfermodeName name matching the uniform declared in the AGSL program
+ * @param xfermode filter passed into the AGSL program for sampling
+ */
+ @FlaggedApi(Flags.FLAG_RUNTIME_COLOR_FILTERS_BLENDERS)
+ public void setInputXfermode(@NonNull String xfermodeName, @NonNull RuntimeXfermode xfermode) {
+ if (xfermodeName == null) {
+ throw new NullPointerException("The xfermodeName parameter must not be null");
+ }
+ if (xfermode == null) {
+ throw new NullPointerException("The xfermode parameter must not be null");
+ }
+ nativeUpdateChild(mNativeInstanceRuntimeShaderBuilder, xfermodeName,
+ xfermode.createNativeInstance());
+ }
+
/** @hide */
@Override
@@ -552,5 +594,7 @@
int value4, int count);
private static native void nativeUpdateShader(
long shaderBuilder, String shaderName, long shader);
+ private static native void nativeUpdateChild(
+ long shaderBuilder, String childName, long child);
}
diff --git a/graphics/java/android/graphics/RuntimeXfermode.java b/graphics/java/android/graphics/RuntimeXfermode.java
new file mode 100644
index 0000000..51d97a4
--- /dev/null
+++ b/graphics/java/android/graphics/RuntimeXfermode.java
@@ -0,0 +1,329 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics;
+
+import android.annotation.ColorInt;
+import android.annotation.ColorLong;
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+
+import com.android.graphics.hwui.flags.Flags;
+
+import libcore.util.NativeAllocationRegistry;
+
+
+/**
+ * <p>A {@link RuntimeXfermode} calculates a per-pixel color based on the output of a user
+ * * defined Android Graphics Shading Language (AGSL) function.</p>
+ *
+ * <p>This AGSL function takes in two input colors to be operated on. These colors are in sRGB
+ * * and the output is also interpreted as sRGB. The AGSL function signature expects a single input
+ * * of color (packed as a half4 or float4 or vec4).</p>
+ *
+ * <pre class="prettyprint">
+ * vec4 main(half4 src, half4 dst);
+ * </pre>
+ */
+@FlaggedApi(Flags.FLAG_RUNTIME_COLOR_FILTERS_BLENDERS)
+public class RuntimeXfermode extends Xfermode {
+
+ private static class NoImagePreloadHolder {
+ public static final NativeAllocationRegistry sRegistry =
+ NativeAllocationRegistry.createMalloced(
+ RuntimeXfermode.class.getClassLoader(), nativeGetFinalizer());
+ }
+
+ private long mBuilderNativeInstance;
+
+ /**
+ * Creates a new RuntimeBlender.
+ *
+ * @param agsl The text of AGSL color filter program to run.
+ */
+ public RuntimeXfermode(@NonNull String agsl) {
+ if (agsl == null) {
+ throw new NullPointerException("RuntimeShader requires a non-null AGSL string");
+ }
+ mBuilderNativeInstance = nativeCreateBlenderBuilder(agsl);
+ RuntimeXfermode.NoImagePreloadHolder.sRegistry.registerNativeAllocation(
+ this, mBuilderNativeInstance);
+ }
+ /**
+ * Sets the uniform color value corresponding to this color filter. If the effect does not have
+ * a uniform with that name or if the uniform is declared with a type other than vec3 or vec4
+ * and corresponding layout(color) annotation then an IllegalArgumentException is thrown.
+ *
+ * @param uniformName name matching the color uniform declared in the AGSL program
+ * @param color the provided sRGB color
+ */
+ public void setColorUniform(@NonNull String uniformName, @ColorInt int color) {
+ setUniform(uniformName, Color.valueOf(color).getComponents(), true);
+ }
+
+ /**
+ * Sets the uniform color value corresponding to this color filter. If the effect does not have
+ * a uniform with that name or if the uniform is declared with a type other than vec3 or vec4
+ * and corresponding layout(color) annotation then an IllegalArgumentException is thrown.
+ *
+ * @param uniformName name matching the color uniform declared in the AGSL program
+ * @param color the provided sRGB color
+ */
+ public void setColorUniform(@NonNull String uniformName, @ColorLong long color) {
+ Color exSRGB = Color.valueOf(color).convert(ColorSpace.get(ColorSpace.Named.EXTENDED_SRGB));
+ setUniform(uniformName, exSRGB.getComponents(), true);
+ }
+
+ /**
+ * Sets the uniform color value corresponding to this color filter. If the effect does not have
+ * a uniform with that name or if the uniform is declared with a type other than vec3 or vec4
+ * and corresponding layout(color) annotation then an IllegalArgumentException is thrown.
+ *
+ * @param uniformName name matching the color uniform declared in the AGSL program
+ * @param color the provided sRGB color
+ */
+ public void setColorUniform(@NonNull String uniformName, @NonNull Color color) {
+ if (color == null) {
+ throw new NullPointerException("The color parameter must not be null");
+ }
+ Color exSRGB = color.convert(ColorSpace.get(ColorSpace.Named.EXTENDED_SRGB));
+ setUniform(uniformName, exSRGB.getComponents(), true);
+ }
+
+ /**
+ * Sets the uniform value corresponding to this color filter. If the effect does not have a
+ * uniform with that name or if the uniform is declared with a type other than a float or
+ * float[1] then an IllegalArgumentException is thrown.
+ *
+ * @param uniformName name matching the uniform declared in the AGSL program
+ */
+ public void setFloatUniform(@NonNull String uniformName, float value) {
+ setFloatUniform(uniformName, value, 0.0f, 0.0f, 0.0f, 1);
+ }
+
+ /**
+ * Sets the uniform value corresponding to this color filter. If the effect does not have a
+ * uniform with that name or if the uniform is declared with a type other than a vec2 or
+ * float[2] then an IllegalArgumentException is thrown.
+ *
+ * @param uniformName name matching the uniform declared in the AGSL program
+ */
+ public void setFloatUniform(@NonNull String uniformName, float value1, float value2) {
+ setFloatUniform(uniformName, value1, value2, 0.0f, 0.0f, 2);
+ }
+
+ /**
+ * Sets the uniform value corresponding to this color filter. If the effect does not have a
+ * uniform with that name or if the uniform is declared with a type other than a vec3 or
+ * float[3] then an IllegalArgumentException is thrown.
+ *
+ * @param uniformName name matching the uniform declared in the AGSL program
+ */
+ public void setFloatUniform(@NonNull String uniformName, float value1, float value2,
+ float value3) {
+ setFloatUniform(uniformName, value1, value2, value3, 0.0f, 3);
+
+ }
+
+ /**
+ * Sets the uniform value corresponding to this color filter. If the effect does not have a
+ * uniform with that name or if the uniform is declared with a type other than a vec4 or
+ * float[4] then an IllegalArgumentException is thrown.
+ *
+ * @param uniformName name matching the uniform declared in the AGSL program
+ */
+ public void setFloatUniform(@NonNull String uniformName, float value1, float value2,
+ float value3, float value4) {
+ setFloatUniform(uniformName, value1, value2, value3, value4, 4);
+ }
+
+ /**
+ * Sets the uniform value corresponding to this color filter. If the effect does not have a
+ * uniform with that name or if the uniform is declared with a type other than a float
+ * (for N=1), vecN, or float[N] where N is the length of the values param then an
+ * IllegalArgumentException is thrown.
+ *
+ * @param uniformName name matching the uniform declared in the AGSL program
+ */
+ public void setFloatUniform(@NonNull String uniformName, @NonNull float[] values) {
+ setUniform(uniformName, values, false);
+ }
+
+ private void setFloatUniform(@NonNull String uniformName, float value1, float value2,
+ float value3, float value4, int count) {
+ if (uniformName == null) {
+ throw new NullPointerException("The uniformName parameter must not be null");
+ }
+ nativeUpdateUniforms(mBuilderNativeInstance, uniformName, value1, value2, value3, value4,
+ count);
+ }
+
+ private void setUniform(@NonNull String uniformName, @NonNull float[] values, boolean isColor) {
+ if (uniformName == null) {
+ throw new NullPointerException("The uniformName parameter must not be null");
+ }
+ if (values == null) {
+ throw new NullPointerException("The uniform values parameter must not be null");
+ }
+ nativeUpdateUniforms(mBuilderNativeInstance, uniformName, values, isColor);
+ }
+
+ /**
+ * Sets the uniform value corresponding to this color filter. If the effect does not have a
+ * uniform with that name or if the uniform is declared with a type other than an int or int[1]
+ * then an IllegalArgumentException is thrown.
+ *
+ * @param uniformName name matching the uniform declared in the AGSL program
+ */
+ public void setIntUniform(@NonNull String uniformName, int value) {
+ setIntUniform(uniformName, value, 0, 0, 0, 1);
+ }
+
+ /**
+ * Sets the uniform value corresponding to this color filter. If the effect does not have a
+ * uniform with that name or if the uniform is declared with a type other than an ivec2 or
+ * int[2] then an IllegalArgumentException is thrown.
+ *
+ * @param uniformName name matching the uniform declared in the AGSL program
+ */
+ public void setIntUniform(@NonNull String uniformName, int value1, int value2) {
+ setIntUniform(uniformName, value1, value2, 0, 0, 2);
+ }
+
+ /**
+ * Sets the uniform value corresponding to this color filter. If the effect does not have a
+ * uniform with that name or if the uniform is declared with a type other than an ivec3 or
+ * int[3] then an IllegalArgumentException is thrown.
+ *
+ * @param uniformName name matching the uniform declared in the AGSL program
+ */
+ public void setIntUniform(@NonNull String uniformName, int value1, int value2, int value3) {
+ setIntUniform(uniformName, value1, value2, value3, 0, 3);
+ }
+
+ /**
+ * Sets the uniform value corresponding to this color filter. If the effect does not have a
+ * uniform with that name or if the uniform is declared with a type other than an ivec4 or
+ * int[4] then an IllegalArgumentException is thrown.
+ *
+ * @param uniformName name matching the uniform declared in the AGSL program
+ */
+ public void setIntUniform(@NonNull String uniformName, int value1, int value2,
+ int value3, int value4) {
+ setIntUniform(uniformName, value1, value2, value3, value4, 4);
+ }
+
+ /**
+ * Sets the uniform value corresponding to this color filter. If the effect does not have a
+ * uniform with that name or if the uniform is declared with a type other than an int (for N=1),
+ * ivecN, or int[N] where N is the length of the values param then an IllegalArgumentException
+ * is thrown.
+ *
+ * @param uniformName name matching the uniform declared in the AGSL program
+ */
+ public void setIntUniform(@NonNull String uniformName, @NonNull int[] values) {
+ if (uniformName == null) {
+ throw new NullPointerException("The uniformName parameter must not be null");
+ }
+ if (values == null) {
+ throw new NullPointerException("The uniform values parameter must not be null");
+ }
+ nativeUpdateUniforms(mBuilderNativeInstance, uniformName, values);
+ }
+
+ private void setIntUniform(@NonNull String uniformName, int value1, int value2, int value3,
+ int value4, int count) {
+ if (uniformName == null) {
+ throw new NullPointerException("The uniformName parameter must not be null");
+ }
+ nativeUpdateUniforms(mBuilderNativeInstance, uniformName, value1, value2, value3, value4,
+ count);
+ }
+
+ /**
+ * Assigns the uniform shader to the provided shader parameter. If the shader program does not
+ * have a uniform shader with that name then an IllegalArgumentException is thrown.
+ *
+ * @param shaderName name matching the uniform declared in the AGSL program
+ * @param shader shader passed into the AGSL program for sampling
+ */
+ public void setInputShader(@NonNull String shaderName, @NonNull Shader shader) {
+ if (shaderName == null) {
+ throw new NullPointerException("The shaderName parameter must not be null");
+ }
+ if (shader == null) {
+ throw new NullPointerException("The shader parameter must not be null");
+ }
+ nativeUpdateChild(mBuilderNativeInstance, shaderName, shader.getNativeInstance());
+ }
+
+ /**
+ * Assigns the uniform color filter to the provided color filter parameter. If the shader
+ * program does not have a uniform color filter with that name then an IllegalArgumentException
+ * is thrown.
+ *
+ * @param filterName name matching the uniform declared in the AGSL program
+ * @param colorFilter filter passed into the AGSL program for sampling
+ */
+ public void setInputColorFilter(@NonNull String filterName, @NonNull ColorFilter colorFilter) {
+ if (filterName == null) {
+ throw new NullPointerException("The filterName parameter must not be null");
+ }
+ if (colorFilter == null) {
+ throw new NullPointerException("The colorFilter parameter must not be null");
+ }
+ nativeUpdateChild(mBuilderNativeInstance, filterName, colorFilter.getNativeInstance());
+ }
+
+ /**
+ * Assigns the uniform xfermode to the provided xfermode parameter. If the shader program does
+ * not have a uniform xfermode with that name then an IllegalArgumentException is thrown.
+ *
+ * @param xfermodeName name matching the uniform declared in the AGSL program
+ * @param xfermode xfermode function passed into the AGSL program for sampling
+ */
+ public void setInputXfermode(@NonNull String xfermodeName, @NonNull RuntimeXfermode xfermode) {
+ if (xfermodeName == null) {
+ throw new NullPointerException("The xfermodeName parameter must not be null");
+ }
+ if (xfermode == null) {
+ throw new NullPointerException("The xfermode parameter must not be null");
+ }
+ nativeUpdateChild(mBuilderNativeInstance, xfermodeName, xfermode.createNativeInstance());
+ }
+
+ /** @hide */
+ public long createNativeInstance() {
+ return nativeCreateNativeInstance(mBuilderNativeInstance);
+ }
+
+ /** @hide */
+ private static native long nativeGetFinalizer();
+ private static native long nativeCreateBlenderBuilder(String agsl);
+ private static native long nativeCreateNativeInstance(long builder);
+ private static native void nativeUpdateUniforms(
+ long builder, String uniformName, float[] uniforms, boolean isColor);
+ private static native void nativeUpdateUniforms(
+ long builder, String uniformName, float value1, float value2, float value3,
+ float value4, int count);
+ private static native void nativeUpdateUniforms(
+ long builder, String uniformName, int[] uniforms);
+ private static native void nativeUpdateUniforms(
+ long builder, String uniformName, int value1, int value2, int value3,
+ int value4, int count);
+ private static native void nativeUpdateChild(long builder, String childName, long child);
+
+}
diff --git a/graphics/java/android/graphics/Xfermode.java b/graphics/java/android/graphics/Xfermode.java
index 6bb22a1..fb689e4 100644
--- a/graphics/java/android/graphics/Xfermode.java
+++ b/graphics/java/android/graphics/Xfermode.java
@@ -21,9 +21,6 @@
package android.graphics;
-import android.compat.annotation.UnsupportedAppUsage;
-import android.os.Build;
-
/**
* Xfermode is the base class for objects that are called to implement custom
* "transfer-modes" in the drawing pipeline. The static function Create(Modes)
@@ -31,8 +28,4 @@
* specified in the Modes enum. When an Xfermode is assigned to a Paint, then
* objects drawn with that paint have the xfermode applied.
*/
-public class Xfermode {
- static final int DEFAULT = PorterDuff.Mode.SRC_OVER.nativeInt;
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- int porterDuffMode = DEFAULT;
-}
+public class Xfermode {}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/BackupHelper.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/BackupHelper.java
index 220fc6f..819cf34 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/BackupHelper.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/BackupHelper.java
@@ -94,7 +94,6 @@
*/
void scheduleBackup() {
if (!mSaveEmbeddingState) {
- // TODO(b/289875940): enabled internally for broader testing.
return;
}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/ParcelableSplitContainerData.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/ParcelableSplitContainerData.java
index cb280c5..0f1246c 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/ParcelableSplitContainerData.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/ParcelableSplitContainerData.java
@@ -44,7 +44,6 @@
@NonNull
private final IBinder mSecondaryContainerToken;
- // TODO(b/289875940): making this as non-null once the tag can be auto-generated from the rule.
@Nullable
final String mSplitRuleTag;
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/ParcelableTaskFragmentContainerData.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/ParcelableTaskFragmentContainerData.java
index a79a89a..bf342d7 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/ParcelableTaskFragmentContainerData.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/ParcelableTaskFragmentContainerData.java
@@ -25,6 +25,9 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import java.util.ArrayList;
+import java.util.List;
+
/**
* This class holds the Parcelable data of a {@link TaskFragmentContainer}.
*/
@@ -61,6 +64,12 @@
@NonNull
final Rect mLastRequestedBounds;
+ /**
+ * Individual associated activity tokens in different containers that should be finished on
+ * exit.
+ */
+ final List<IBinder> mActivitiesToFinishOnExit = new ArrayList<>();
+
ParcelableTaskFragmentContainerData(@NonNull IBinder token, @Nullable String overlayTag,
@Nullable IBinder associatedActivityToken) {
mToken = token;
@@ -74,6 +83,7 @@
mOverlayTag = in.readString();
mAssociatedActivityToken = in.readStrongBinder();
mLastRequestedBounds = in.readTypedObject(Rect.CREATOR);
+ in.readBinderList(mActivitiesToFinishOnExit);
}
public static final Creator<ParcelableTaskFragmentContainerData> CREATOR = new Creator<>() {
@@ -99,7 +109,7 @@
dest.writeString(mOverlayTag);
dest.writeStrongBinder(mAssociatedActivityToken);
dest.writeTypedObject(mLastRequestedBounds, flags);
+ dest.writeBinderList(mActivitiesToFinishOnExit);
}
-
}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java
index faf73c2..5ba30dd 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java
@@ -98,10 +98,20 @@
mCurrentSplitAttributes = mDefaultSplitAttributes;
if (shouldFinishPrimaryWithSecondary(splitRule)) {
- mSecondaryContainer.addContainerToFinishOnExit(mPrimaryContainer);
+ addContainerToFinishOnExitWhenRestore(mSecondaryContainer, mPrimaryContainer);
}
if (shouldFinishSecondaryWithPrimary(splitRule)) {
- mPrimaryContainer.addContainerToFinishOnExit(mSecondaryContainer);
+ addContainerToFinishOnExitWhenRestore(mPrimaryContainer, mSecondaryContainer);
+ }
+ }
+
+ private void addContainerToFinishOnExitWhenRestore(
+ @NonNull TaskFragmentContainer containerToAdd,
+ @NonNull TaskFragmentContainer containerToFinish) {
+ // If an activity was already added to be finished after the restoration, then that's it.
+ // Otherwise, add the container to finish on exit.
+ if (!containerToAdd.hasActivityToFinishOnExit(containerToFinish)) {
+ containerToAdd.addContainerToFinishOnExit(containerToFinish);
}
}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
index dc1d983..b3e003e 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
@@ -96,12 +96,6 @@
new ArrayList<>();
/**
- * Individual associated activity tokens in different containers that should be finished on
- * exit.
- */
- private final List<IBinder> mActivitiesToFinishOnExit = new ArrayList<>();
-
- /**
* The launch options that was used to create this container. Must not {@link Bundle#isEmpty()}
* for {@link #isOverlay()} container.
*/
@@ -114,7 +108,6 @@
/**
* Windowing mode that was requested last via {@link android.window.WindowContainerTransaction}.
*/
- // TODO(b/289875940): review this and other field that might need to be moved in the base class.
@WindowingMode
private int mLastRequestedWindowingMode = WINDOWING_MODE_UNDEFINED;
@@ -443,7 +436,7 @@
// Remove the activity now because there can be a delay before the server callback.
mInfo.getActivities().remove(activityToken);
}
- mActivitiesToFinishOnExit.remove(activityToken);
+ mParcelableData.mActivitiesToFinishOnExit.remove(activityToken);
finishSelfWithActivityIfNeeded(wct, activityToken);
}
@@ -624,7 +617,20 @@
if (mIsFinished) {
return;
}
- mActivitiesToFinishOnExit.add(activityToFinish.getActivityToken());
+ mParcelableData.mActivitiesToFinishOnExit.add(activityToFinish.getActivityToken());
+ }
+
+ /**
+ * Returns {@code true} if an Activity from the given {@code container} was added to be
+ * finished on exit. Otherwise, return {@code false}.
+ */
+ boolean hasActivityToFinishOnExit(@NonNull TaskFragmentContainer container) {
+ for (IBinder activity : mParcelableData.mActivitiesToFinishOnExit) {
+ if (container.hasActivity(activity)) {
+ return true;
+ }
+ }
+ return false;
}
/**
@@ -634,7 +640,7 @@
if (mIsFinished) {
return;
}
- mActivitiesToFinishOnExit.remove(activityToRemove.getActivityToken());
+ mParcelableData.mActivitiesToFinishOnExit.remove(activityToRemove.getActivityToken());
}
/** Removes all dependencies that should be finished when this container is finished. */
@@ -643,7 +649,7 @@
return;
}
mContainersToFinishOnExit.clear();
- mActivitiesToFinishOnExit.clear();
+ mParcelableData.mActivitiesToFinishOnExit.clear();
}
/**
@@ -721,7 +727,7 @@
mContainersToFinishOnExit.clear();
// Finish associated activities
- for (IBinder activityToken : mActivitiesToFinishOnExit) {
+ for (IBinder activityToken : mParcelableData.mActivitiesToFinishOnExit) {
final Activity activity = mController.getActivity(activityToken);
if (activity == null || activity.isFinishing()
|| controller.shouldRetainAssociatedActivity(this, activity)) {
@@ -729,7 +735,7 @@
}
wct.finishActivity(activity.getActivityToken());
}
- mActivitiesToFinishOnExit.clear();
+ mParcelableData.mActivitiesToFinishOnExit.clear();
}
@GuardedBy("mController.mLock")
@@ -1082,7 +1088,7 @@
+ " pendingAppearedActivities=" + mPendingAppearedActivities
+ (includeContainersToFinishOnExit ? " containersToFinishOnExit="
+ containersToFinishOnExitToString() : "")
- + " activitiesToFinishOnExit=" + mActivitiesToFinishOnExit
+ + " activitiesToFinishOnExit=" + mParcelableData.mActivitiesToFinishOnExit
+ " info=" + mInfo
+ "}";
}
diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml
index a18a251..bfd9c81 100644
--- a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml
+++ b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml
@@ -23,7 +23,7 @@
android:clipChildren="false"
android:clipToPadding="false"
android:paddingBottom="@dimen/desktop_mode_handle_menu_pill_elevation"
- android:paddingRight="@dimen/desktop_mode_handle_menu_pill_elevation"
+ android:paddingEnd="@dimen/desktop_mode_handle_menu_pill_elevation"
android:orientation="vertical">
<LinearLayout
diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_maximize_menu.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_maximize_menu.xml
index 5e41865..375968a 100644
--- a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_maximize_menu.xml
+++ b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_maximize_menu.xml
@@ -113,7 +113,7 @@
style="?android:attr/buttonBarButtonStyle"
android:layout_width="41dp"
android:layout_height="@dimen/desktop_mode_maximize_menu_button_height"
- android:layout_marginRight="4dp"
+ android:layout_marginEnd="4dp"
android:background="@drawable/desktop_mode_maximize_menu_button_background"
android:importantForAccessibility="yes"
android:contentDescription="@string/desktop_mode_maximize_menu_snap_left_button_text"
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index 249e9a2..21ec84d 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -504,17 +504,15 @@
<dimen name="desktop_mode_maximize_menu_buttons_fill_radius">4dp</dimen>
<!-- The padding between the outline and fill of the maximize menu snap and maximize buttons. -->
<dimen name="desktop_mode_maximize_menu_snap_and_maximize_buttons_fill_padding">4dp</dimen>
- <!-- The padding between the outline and fill of the maximize menu snap and maximize buttons. -->
- <dimen name="desktop_mode_maximize_menu_snap_and_maximize_buttons_fill_padding_bottom">8dp</dimen>
<!-- The vertical padding between the outline and fill of the maximize menu restore button. -->
<dimen name="desktop_mode_maximize_menu_restore_button_fill_vertical_padding">13dp</dimen>
<!-- The horizontal padding between the outline and fill of the maximize menu restore button. -->
- <dimen name="desktop_mode_maximize_menu_restore_button_fill_horizontal_padding">21dp</dimen>
+ <dimen name="desktop_mode_maximize_menu_restore_button_fill_horizontal_padding">15dp</dimen>
<!-- The padding between the outline and fill of the maximize menu immersive button. -->
- <dimen name="desktop_mode_maximize_menu_immersive_button_fill_padding">4dp</dimen>
+ <dimen name="desktop_mode_maximize_menu_immersive_button_fill_padding">0dp</dimen>
<!-- The corner radius of the maximize menu. -->
- <dimen name="desktop_mode_maximize_menu_corner_radius">8dp</dimen>
+ <dimen name="desktop_mode_maximize_menu_corner_radius">16dp</dimen>
<!-- The radius of the Maximize menu shadow. -->
<dimen name="desktop_mode_maximize_menu_shadow_radius">8dp</dimen>
@@ -661,4 +659,7 @@
<dimen name="desktop_windowing_education_promo_height">352dp</dimen>
<!-- The corner radius of the desktop windowing education promo. -->
<dimen name="desktop_windowing_education_promo_corner_radius">28dp</dimen>
+
+ <!-- The corner radius of freeform tasks in desktop windowing. -->
+ <dimen name="desktop_windowing_freeform_rounded_corner_radius">16dp</dimen>
</resources>
diff --git a/libs/WindowManager/Shell/res/values/strings.xml b/libs/WindowManager/Shell/res/values/strings.xml
index 012579a..468c345 100644
--- a/libs/WindowManager/Shell/res/values/strings.xml
+++ b/libs/WindowManager/Shell/res/values/strings.xml
@@ -318,7 +318,7 @@
<!-- Maximize menu maximize button string. -->
<string name="desktop_mode_maximize_menu_maximize_text">Maximize Screen</string>
<!-- Maximize menu snap buttons string. -->
- <string name="desktop_mode_maximize_menu_snap_text">Snap Screen</string>
+ <string name="desktop_mode_maximize_menu_snap_text">Resize</string>
<!-- Snap resizing non-resizable string. -->
<string name="desktop_mode_non_resizable_snap_text">App can\'t be moved here</string>
<!-- Accessibility text for the Maximize Menu's immersive button [CHAR LIMIT=NONE] -->
diff --git a/libs/WindowManager/Shell/res/values/styles.xml b/libs/WindowManager/Shell/res/values/styles.xml
index 55cda78..597a921 100644
--- a/libs/WindowManager/Shell/res/values/styles.xml
+++ b/libs/WindowManager/Shell/res/values/styles.xml
@@ -29,6 +29,7 @@
<item name="android:navigationBarColor">@android:color/transparent</item>
<item name="android:windowDrawsSystemBarBackgrounds">true</item>
<item name="android:windowAnimationStyle">@null</item>
+ <item name="android:windowIsTranslucent">true</item>
</style>
<style name="Animation.ForcedResizable" parent="@android:style/Animation">
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/GroupedTaskInfo.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/GroupedTaskInfo.java
index 03e0ab0..4300e84 100644
--- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/GroupedTaskInfo.java
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/GroupedTaskInfo.java
@@ -168,6 +168,16 @@
}
/**
+ * @return The task info for the task in this group with the given {@code taskId}.
+ */
+ @Nullable
+ public TaskInfo getTaskById(int taskId) {
+ return mTasks.stream()
+ .filter(task -> task.taskId == taskId)
+ .findFirst().orElse(null);
+ }
+
+ /**
* Get all {@link RecentTaskInfo}s grouped together.
*/
@NonNull
@@ -176,6 +186,14 @@
}
/**
+ * @return Whether this grouped task contains a task with the given {@code taskId}.
+ */
+ public boolean containsTask(int taskId) {
+ return mTasks.stream()
+ .anyMatch((task -> task.taskId == taskId));
+ }
+
+ /**
* Return {@link SplitBounds} if this is a split screen entry or {@code null}
*/
@Nullable
@@ -249,9 +267,10 @@
return null;
}
return "id=" + taskInfo.taskId
- + " baseIntent=" + (taskInfo.baseIntent != null
- ? taskInfo.baseIntent.getComponent()
- : "null")
+ + " baseIntent=" +
+ (taskInfo.baseIntent != null && taskInfo.baseIntent.getComponent() != null
+ ? taskInfo.baseIntent.getComponent().flattenToString()
+ : "null")
+ " winMode=" + WindowConfiguration.windowingModeToString(
taskInfo.getWindowingMode());
}
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/ManageWindowsViewContainer.kt b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/ManageWindowsViewContainer.kt
index 23e7441..f14dfdb 100644
--- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/ManageWindowsViewContainer.kt
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/ManageWindowsViewContainer.kt
@@ -50,6 +50,22 @@
fun createMenu(snapshotList: List<Pair<Int, TaskSnapshot>>,
onIconClickListener: ((Int) -> Unit),
onOutsideClickListener: (() -> Unit)): ManageWindowsView {
+ val bitmapList = snapshotList.map { (index, snapshot) ->
+ index to Bitmap.wrapHardwareBuffer(snapshot.hardwareBuffer, snapshot.colorSpace)
+ }
+ return createAndShowMenuView(
+ bitmapList,
+ onIconClickListener,
+ onOutsideClickListener
+ )
+ }
+
+ /** Creates the menu view with the given bitmaps, and displays it. */
+ fun createAndShowMenuView(
+ snapshotList: List<Pair<Int, Bitmap?>>,
+ onIconClickListener: ((Int) -> Unit),
+ onOutsideClickListener: (() -> Unit)
+ ): ManageWindowsView {
menuView = ManageWindowsView(context, menuBackgroundColor).apply {
this.onOutsideClickListener = onOutsideClickListener
this.onIconClickListener = onIconClickListener
@@ -120,7 +136,7 @@
}
fun generateIconViews(
- snapshotList: List<Pair<Int, TaskSnapshot>>
+ snapshotList: List<Pair<Int, Bitmap?>>
) {
menuWidth = 0
menuHeight = 0
@@ -133,7 +149,7 @@
// Add each icon to the menu, adding a new row when needed.
for ((iconCount, taskInfoSnapshotPair) in snapshotList.withIndex()) {
val taskId = taskInfoSnapshotPair.first
- val snapshot = taskInfoSnapshotPair.second
+ val snapshotBitmap = taskInfoSnapshotPair.second
// Once a row is filled, make a new row and increase the menu height.
if (iconCount % MENU_MAX_ICONS_PER_ROW == 0) {
rowLayout = LinearLayout(context)
@@ -141,10 +157,7 @@
rootView.addView(rowLayout)
menuHeight += (instanceIconHeight + iconMargin).toInt()
}
- val snapshotBitmap = Bitmap.wrapHardwareBuffer(
- snapshot.hardwareBuffer,
- snapshot.colorSpace
- )
+
val croppedBitmap = snapshotBitmap?.let { cropBitmap(it) }
val scaledSnapshotBitmap = croppedBitmap?.let {
Bitmap.createScaledBitmap(
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index 39dc267..b82496e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -1327,7 +1327,11 @@
/** Promote the provided bubble from the overflow view. */
public void promoteBubbleFromOverflow(Bubble bubble) {
- mLogger.log(bubble, BubbleLogger.Event.BUBBLE_OVERFLOW_REMOVE_BACK_TO_STACK);
+ if (isShowingAsBubbleBar()) {
+ mLogger.log(bubble, BubbleLogger.Event.BUBBLE_BAR_OVERFLOW_REMOVE_BACK_TO_BAR);
+ } else {
+ mLogger.log(bubble, BubbleLogger.Event.BUBBLE_OVERFLOW_REMOVE_BACK_TO_STACK);
+ }
ProtoLog.d(WM_SHELL_BUBBLES, "promoteBubbleFromOverflow=%s", bubble.getKey());
bubble.setInflateSynchronously(mInflateSynchronously);
bubble.setShouldAutoExpand(true);
@@ -1350,11 +1354,7 @@
if (BubbleOverflow.KEY.equals(key)) {
mBubbleData.setSelectedBubbleFromLauncher(mBubbleData.getOverflow());
mLayerView.showExpandedView(mBubbleData.getOverflow());
- if (wasExpanded) {
- mLogger.log(BubbleLogger.Event.BUBBLE_BAR_BUBBLE_SWITCHED);
- } else {
- mLogger.log(BubbleLogger.Event.BUBBLE_BAR_EXPANDED);
- }
+ mLogger.log(BubbleLogger.Event.BUBBLE_BAR_OVERFLOW_SELECTED);
return;
}
@@ -2087,8 +2087,9 @@
BubbleLogger.Event event = isExpanded ? BubbleLogger.Event.BUBBLE_BAR_EXPANDED
: BubbleLogger.Event.BUBBLE_BAR_COLLAPSED;
- if (mBubbleData.getSelectedBubble() instanceof Bubble bubble) {
- mLogger.log(bubble, event);
+ BubbleViewProvider selectedBubble = mBubbleData.getSelectedBubble();
+ if (selectedBubble instanceof Bubble) {
+ mLogger.log((Bubble) selectedBubble, event);
} else {
mLogger.log(event);
}
@@ -2099,8 +2100,9 @@
// Only need to update the layer view if we're currently expanded for selection changes.
if (mLayerView != null && mLayerView.isExpanded()) {
mLayerView.showExpandedView(selectedBubble);
- if (selectedBubble instanceof Bubble bubble) {
- mLogger.log(bubble, BubbleLogger.Event.BUBBLE_BAR_BUBBLE_SWITCHED);
+ if (selectedBubble instanceof Bubble) {
+ mLogger.log((Bubble) selectedBubble,
+ BubbleLogger.Event.BUBBLE_BAR_BUBBLE_SWITCHED);
}
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
index 4de9dfa..2945691 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
@@ -759,7 +759,9 @@
if (b != null) {
b.stopInflation();
}
- mLogger.logOverflowRemove(b, reason);
+ if (!mPositioner.isShowingInBubbleBar()) {
+ mLogger.logStackOverflowRemove(b, reason);
+ }
mOverflowBubbles.remove(b);
mStateChange.bubbleRemoved(b, reason);
mStateChange.removedOverflowBubble = b;
@@ -802,6 +804,27 @@
setNewSelectedIndex(indexToRemove);
}
maybeSendDeleteIntent(reason, bubbleToRemove);
+
+ if (mPositioner.isShowingInBubbleBar()) {
+ logBubbleBarBubbleRemoved(bubbleToRemove, reason);
+ }
+ }
+
+ private void logBubbleBarBubbleRemoved(Bubble bubble, @DismissReason int reason) {
+ switch (reason) {
+ case Bubbles.DISMISS_NOTIF_CANCEL:
+ mLogger.log(bubble, BubbleLogger.Event.BUBBLE_BAR_BUBBLE_REMOVED_CANCELED);
+ break;
+ case Bubbles.DISMISS_TASK_FINISHED:
+ mLogger.log(bubble, BubbleLogger.Event.BUBBLE_BAR_BUBBLE_ACTIVITY_FINISH);
+ break;
+ case Bubbles.DISMISS_BLOCKED:
+ case Bubbles.DISMISS_NO_LONGER_BUBBLE:
+ mLogger.log(bubble, BubbleLogger.Event.BUBBLE_BAR_BUBBLE_REMOVED_BLOCKED);
+ break;
+ default:
+ // skip logging other events
+ }
}
private void setNewSelectedIndex(int indexOfSelected) {
@@ -862,7 +885,7 @@
return;
}
ProtoLog.d(WM_SHELL_BUBBLES, "overflowBubble=%s", bubble.getKey());
- mLogger.logOverflowAdd(bubble, reason);
+ mLogger.logOverflowAdd(bubble, mPositioner.isShowingInBubbleBar(), reason);
if (mOverflowBubbles.isEmpty()) {
mStateChange.showOverflowChanged = true;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleLogger.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleLogger.java
index 3663073..347df33 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleLogger.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleLogger.java
@@ -182,10 +182,12 @@
}
/**
+ * Log when a bubble is removed from overflow in stack view
+ *
* @param b Bubble removed from overflow
* @param r Reason that bubble was removed
*/
- public void logOverflowRemove(Bubble b, @Bubbles.DismissReason int r) {
+ public void logStackOverflowRemove(Bubble b, @Bubbles.DismissReason int r) {
if (r == Bubbles.DISMISS_NOTIF_CANCEL) {
log(b, BubbleLogger.Event.BUBBLE_OVERFLOW_REMOVE_CANCEL);
} else if (r == Bubbles.DISMISS_GROUP_CANCELLED) {
@@ -201,13 +203,19 @@
* @param b Bubble added to overflow
* @param r Reason that bubble was added to overflow
*/
- public void logOverflowAdd(Bubble b, @Bubbles.DismissReason int r) {
- if (r == Bubbles.DISMISS_AGED) {
- log(b, Event.BUBBLE_OVERFLOW_ADD_AGED);
- } else if (r == Bubbles.DISMISS_USER_GESTURE) {
- log(b, Event.BUBBLE_OVERFLOW_ADD_USER_GESTURE);
- } else if (r == Bubbles.DISMISS_RELOAD_FROM_DISK) {
- log(b, Event.BUBBLE_OVERFLOW_RECOVER);
+ public void logOverflowAdd(Bubble b, boolean bubbleBar, @Bubbles.DismissReason int r) {
+ if (bubbleBar) {
+ if (r == Bubbles.DISMISS_AGED) {
+ log(b, Event.BUBBLE_BAR_OVERFLOW_ADD_AGED);
+ }
+ } else {
+ if (r == Bubbles.DISMISS_AGED) {
+ log(b, Event.BUBBLE_OVERFLOW_ADD_AGED);
+ } else if (r == Bubbles.DISMISS_USER_GESTURE) {
+ log(b, Event.BUBBLE_OVERFLOW_ADD_USER_GESTURE);
+ } else if (r == Bubbles.DISMISS_RELOAD_FROM_DISK) {
+ log(b, Event.BUBBLE_OVERFLOW_RECOVER);
+ }
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
index c386c93..068b2d2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
@@ -830,6 +830,13 @@
mShowingInBubbleBar = showingInBubbleBar;
}
+ /**
+ * Whether bubbles ar showing in the bubble bar from launcher.
+ */
+ boolean isShowingInBubbleBar() {
+ return mShowingInBubbleBar;
+ }
+
public void setBubbleBarLocation(BubbleBarLocation location) {
mBubbleBarLocation = location;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java
index f532be6..12d20bf 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java
@@ -96,6 +96,14 @@
}
/**
+ * Get all the displays from DisplayManager.
+ */
+ public Display[] getDisplays() {
+ final DisplayManager displayManager = mContext.getSystemService(DisplayManager.class);
+ return displayManager.getDisplays();
+ }
+
+ /**
* Gets the DisplayLayout associated with a display.
*/
public @Nullable DisplayLayout getDisplayLayout(int displayId) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
index dab30b0..0f21756 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
@@ -88,6 +88,7 @@
import com.android.wm.shell.splitscreen.StageTaskListener;
import java.io.PrintWriter;
+import java.util.List;
import java.util.function.Consumer;
/**
@@ -139,15 +140,21 @@
private final Rect mTempRect = new Rect();
private final Rect mRootBounds = new Rect();
private final Rect mDividerBounds = new Rect();
- // Bounds1 final position should be always at top or left
- private final Rect mBounds1 = new Rect();
- // Bounds2 final position should be always at bottom or right
- private final Rect mBounds2 = new Rect();
+ /**
+ * A list of stage bounds, kept in order from top/left to bottom/right. These are the sizes of
+ * the app surfaces, not necessarily the same as the size of the rendered content.
+ * See {@link #mContentBounds}.
+ */
+ private final List<Rect> mStageBounds = List.of(new Rect(), new Rect());
+ /**
+ * A list of app content bounds, kept in order from top/left to bottom/right. These are the
+ * sizes of the rendered app contents, not necessarily the same as the size of the drawn app
+ * surfaces. See {@link #mStageBounds}.
+ */
+ private final List<Rect> mContentBounds = List.of(new Rect(), new Rect());
// The temp bounds outside of display bounds for side stage when split screen inactive to avoid
// flicker next time active split screen.
private final Rect mInvisibleBounds = new Rect();
- private final Rect mWinBounds1 = new Rect();
- private final Rect mWinBounds2 = new Rect();
private final SplitLayoutHandler mSplitLayoutHandler;
private final SplitWindowManager mSplitWindowManager;
private final DisplayController mDisplayController;
@@ -233,26 +240,26 @@
mDividerWindowWidth = mDividerSize + 2 * mDividerInsets;
}
- /** Gets bounds of the primary split with screen based coordinate. */
- public Rect getBounds1() {
- return new Rect(mBounds1);
+ /** Gets the bounds of the top/left app in screen-based coordinates. */
+ public Rect getTopLeftBounds() {
+ return mStageBounds.getFirst();
}
- /** Gets bounds of the primary split with parent based coordinate. */
- public Rect getRefBounds1() {
- Rect outBounds = getBounds1();
+ /** Gets the bounds of the bottom/right app in screen-based coordinates. */
+ public Rect getBottomRightBounds() {
+ return mStageBounds.getLast();
+ }
+
+ /** Gets the bounds of the top/left app in parent-based coordinates. */
+ public Rect getTopLeftRefBounds() {
+ Rect outBounds = getTopLeftBounds();
outBounds.offset(-mRootBounds.left, -mRootBounds.top);
return outBounds;
}
- /** Gets bounds of the secondary split with screen based coordinate. */
- public Rect getBounds2() {
- return new Rect(mBounds2);
- }
-
- /** Gets bounds of the secondary split with parent based coordinate. */
- public Rect getRefBounds2() {
- final Rect outBounds = getBounds2();
+ /** Gets the bounds of the bottom/right app in parent-based coordinates. */
+ public Rect getBottomRightRefBounds() {
+ Rect outBounds = getBottomRightBounds();
outBounds.offset(-mRootBounds.left, -mRootBounds.top);
return outBounds;
}
@@ -274,31 +281,42 @@
return outBounds;
}
- /** Gets bounds of the primary split with screen based coordinate on the param Rect. */
- public void getBounds1(Rect rect) {
- rect.set(mBounds1);
+ /** Copies the top/left bounds to the provided Rect (screen-based coordinates). */
+ public void copyTopLeftBounds(Rect rect) {
+ rect.set(getTopLeftBounds());
}
- /** Gets bounds of the primary split with parent based coordinate on the param Rect. */
- public void getRefBounds1(Rect rect) {
- getBounds1(rect);
+ /** Copies the top/left bounds to the provided Rect (parent-based coordinates). */
+ public void copyTopLeftRefBounds(Rect rect) {
+ copyTopLeftBounds(rect);
rect.offset(-mRootBounds.left, -mRootBounds.top);
}
- /** Gets bounds of the secondary split with screen based coordinate on the param Rect. */
- public void getBounds2(Rect rect) {
- rect.set(mBounds2);
+ /** Copies the bottom/right bounds to the provided Rect (screen-based coordinates). */
+ public void copyBottomRightBounds(Rect rect) {
+ rect.set(getBottomRightBounds());
}
- /** Gets bounds of the secondary split with parent based coordinate on the param Rect. */
- public void getRefBounds2(Rect rect) {
- getBounds2(rect);
+ /** Copies the bottom/right bounds to the provided Rect (parent-based coordinates). */
+ public void copyBottomRightRefBounds(Rect rect) {
+ copyBottomRightBounds(rect);
rect.offset(-mRootBounds.left, -mRootBounds.top);
}
- /** Gets root bounds of the whole split layout on the param Rect. */
- public void getRootBounds(Rect rect) {
- rect.set(mRootBounds);
+ /**
+ * Gets the content bounds of the top/left app (the bounds of where the app contents would be
+ * drawn). Might be larger than the available surface space.
+ */
+ public Rect getTopLeftContentBounds() {
+ return mContentBounds.getFirst();
+ }
+
+ /**
+ * Gets the content bounds of the bottom/right app (the bounds of where the app contents would
+ * be drawn). Might be larger than the available surface space.
+ */
+ public Rect getBottomRightContentBounds() {
+ return mContentBounds.getLast();
}
/** Gets bounds of divider window with screen based coordinate on the param Rect. */
@@ -340,8 +358,10 @@
*/
public float getDividerPositionAsFraction() {
return Math.min(1f, Math.max(0f, mIsLeftRightSplit
- ? (float) ((mBounds1.right + mBounds2.left) / 2f) / mBounds2.right
- : (float) ((mBounds1.bottom + mBounds2.top) / 2f) / mBounds2.bottom));
+ ? (float) ((getTopLeftBounds().right + getBottomRightBounds().left) / 2f)
+ / getBottomRightBounds().right
+ : (float) ((getTopLeftBounds().bottom + getBottomRightBounds().top) / 2f)
+ / getBottomRightBounds().bottom));
}
private void updateInvisibleRect() {
@@ -435,7 +455,8 @@
}
private void updateBounds(int position) {
- updateBounds(position, mBounds1, mBounds2, mDividerBounds, true /* setEffectBounds */);
+ updateBounds(position, getTopLeftBounds(), getBottomRightBounds(), mDividerBounds,
+ true /* setEffectBounds */);
}
/** Updates recording bounds of divider window and both of the splits. */
@@ -638,8 +659,8 @@
updateBounds(mDividerPosition);
mWinToken1 = null;
mWinToken2 = null;
- mWinBounds1.setEmpty();
- mWinBounds2.setEmpty();
+ getTopLeftContentBounds().setEmpty();
+ getBottomRightContentBounds().setEmpty();
}
/**
@@ -835,7 +856,8 @@
insets.left != 0 || insets.top != 0 || insets.right != 0 || insets.bottom != 0;
final int dividerPos = mDividerSnapAlgorithm.calculateNonDismissingSnapTarget(
- mIsLeftRightSplit ? mBounds2.width() : mBounds2.height()).position;
+ mIsLeftRightSplit ? getBottomRightBounds().width() : getBottomRightBounds().height()
+ ).position;
final Rect endBounds1 = new Rect();
final Rect endBounds2 = new Rect();
final Rect endDividerBounds = new Rect();
@@ -847,12 +869,12 @@
endBounds2.offset(-mRootBounds.left, -mRootBounds.top);
endDividerBounds.offset(-mRootBounds.left, -mRootBounds.top);
- ValueAnimator animator1 = moveSurface(t, topLeftStage, getRefBounds1(), endBounds1,
+ ValueAnimator animator1 = moveSurface(t, topLeftStage, getTopLeftRefBounds(), endBounds1,
-insets.left, -insets.top, true /* roundCorners */, true /* isGoingBehind */,
shouldVeil);
- ValueAnimator animator2 = moveSurface(t, bottomRightStage, getRefBounds2(), endBounds2,
- insets.left, insets.top, true /* roundCorners */, false /* isGoingBehind */,
- shouldVeil);
+ ValueAnimator animator2 = moveSurface(t, bottomRightStage, getBottomRightRefBounds(),
+ endBounds2, insets.left, insets.top, true /* roundCorners */,
+ false /* isGoingBehind */, shouldVeil);
ValueAnimator animator3 = moveSurface(t, null /* stage */, getRefDividerBounds(),
endDividerBounds, 0 /* offsetX */, 0 /* offsetY */, false /* roundCorners */,
false /* isGoingBehind */, false /* addVeil */);
@@ -1059,10 +1081,10 @@
// Resets layer of divider bar to make sure it is always on top.
t.setLayer(dividerLeash, Integer.MAX_VALUE);
}
- getRefBounds1(mTempRect);
+ copyTopLeftRefBounds(mTempRect);
t.setPosition(leash1, mTempRect.left, mTempRect.top)
.setWindowCrop(leash1, mTempRect.width(), mTempRect.height());
- getRefBounds2(mTempRect);
+ copyBottomRightRefBounds(mTempRect);
t.setPosition(leash2, mTempRect.left, mTempRect.top)
.setWindowCrop(leash2, mTempRect.width(), mTempRect.height());
@@ -1084,15 +1106,17 @@
public boolean applyTaskChanges(WindowContainerTransaction wct,
ActivityManager.RunningTaskInfo task1, ActivityManager.RunningTaskInfo task2) {
boolean boundsChanged = false;
- if (!mBounds1.equals(mWinBounds1) || !task1.token.equals(mWinToken1)) {
- setTaskBounds(wct, task1, mBounds1);
- mWinBounds1.set(mBounds1);
+ if (!getTopLeftBounds().equals(getTopLeftContentBounds())
+ || !task1.token.equals(mWinToken1)) {
+ setTaskBounds(wct, task1, getTopLeftBounds());
+ getTopLeftContentBounds().set(getTopLeftBounds());
mWinToken1 = task1.token;
boundsChanged = true;
}
- if (!mBounds2.equals(mWinBounds2) || !task2.token.equals(mWinToken2)) {
- setTaskBounds(wct, task2, mBounds2);
- mWinBounds2.set(mBounds2);
+ if (!getBottomRightBounds().equals(getBottomRightContentBounds())
+ || !task2.token.equals(mWinToken2)) {
+ setTaskBounds(wct, task2, getBottomRightBounds());
+ getBottomRightContentBounds().set(getBottomRightBounds());
mWinToken2 = task2.token;
boundsChanged = true;
}
@@ -1129,22 +1153,22 @@
public void applyLayoutOffsetTarget(WindowContainerTransaction wct, int offsetX, int offsetY,
ActivityManager.RunningTaskInfo taskInfo1, ActivityManager.RunningTaskInfo taskInfo2) {
if (offsetX == 0 && offsetY == 0) {
- wct.setBounds(taskInfo1.token, mBounds1);
+ wct.setBounds(taskInfo1.token, getTopLeftBounds());
wct.setScreenSizeDp(taskInfo1.token,
SCREEN_WIDTH_DP_UNDEFINED, SCREEN_HEIGHT_DP_UNDEFINED);
- wct.setBounds(taskInfo2.token, mBounds2);
+ wct.setBounds(taskInfo2.token, getBottomRightBounds());
wct.setScreenSizeDp(taskInfo2.token,
SCREEN_WIDTH_DP_UNDEFINED, SCREEN_HEIGHT_DP_UNDEFINED);
} else {
- getBounds1(mTempRect);
+ copyTopLeftBounds(mTempRect);
mTempRect.offset(offsetX, offsetY);
wct.setBounds(taskInfo1.token, mTempRect);
wct.setScreenSizeDp(taskInfo1.token,
taskInfo1.configuration.screenWidthDp,
taskInfo1.configuration.screenHeightDp);
- getBounds2(mTempRect);
+ copyBottomRightBounds(mTempRect);
mTempRect.offset(offsetX, offsetY);
wct.setBounds(taskInfo2.token, mTempRect);
wct.setScreenSizeDp(taskInfo2.token,
@@ -1162,9 +1186,9 @@
pw.println(innerPrefix + "mFreezeDividerWindow=" + mFreezeDividerWindow);
pw.println(innerPrefix + "mDimNonImeSide=" + mDimNonImeSide);
pw.println(innerPrefix + "mDividerPosition=" + mDividerPosition);
- pw.println(innerPrefix + "bounds1=" + mBounds1.toShortString());
+ pw.println(innerPrefix + "bounds1=" + getTopLeftBounds().toShortString());
pw.println(innerPrefix + "dividerBounds=" + mDividerBounds.toShortString());
- pw.println(innerPrefix + "bounds2=" + mBounds2.toShortString());
+ pw.println(innerPrefix + "bounds2=" + getBottomRightBounds().toShortString());
}
/** Handles layout change event. */
@@ -1274,15 +1298,16 @@
}
final boolean topLeftShrink = isLeftRightSplit
- ? position < mWinBounds1.right : position < mWinBounds1.bottom;
+ ? position < getTopLeftContentBounds().right
+ : position < getTopLeftContentBounds().bottom;
if (topLeftShrink) {
mShrinkSide = isLeftRightSplit ? DOCKED_LEFT : DOCKED_TOP;
- mContentBounds.set(mWinBounds1);
- mSurfaceBounds.set(mBounds1);
+ mContentBounds.set(getTopLeftContentBounds());
+ mSurfaceBounds.set(getTopLeftBounds());
} else {
mShrinkSide = isLeftRightSplit ? DOCKED_RIGHT : DOCKED_BOTTOM;
- mContentBounds.set(mWinBounds2);
- mSurfaceBounds.set(mBounds2);
+ mContentBounds.set(getBottomRightContentBounds());
+ mSurfaceBounds.set(getBottomRightBounds());
}
if (mDismissingSide != DOCKED_INVALID) {
@@ -1334,12 +1359,12 @@
case DOCKED_TOP:
case DOCKED_LEFT:
targetLeash = leash1;
- mTempRect.set(mBounds1);
+ mTempRect.set(getTopLeftBounds());
break;
case DOCKED_BOTTOM:
case DOCKED_RIGHT:
targetLeash = leash2;
- mTempRect.set(mBounds2);
+ mTempRect.set(getBottomRightBounds());
break;
}
} else if (mParallaxType == PARALLAX_ALIGN_CENTER) {
@@ -1347,12 +1372,12 @@
case DOCKED_TOP:
case DOCKED_LEFT:
targetLeash = leash1;
- mTempRect.set(mBounds1);
+ mTempRect.set(getTopLeftBounds());
break;
case DOCKED_BOTTOM:
case DOCKED_RIGHT:
targetLeash = leash2;
- mTempRect.set(mBounds2);
+ mTempRect.set(getBottomRightBounds());
break;
}
}
@@ -1530,7 +1555,7 @@
private int getTargetYOffset() {
final int desireOffset = Math.abs(mEndImeTop - mStartImeTop);
// Make sure to keep at least 30% visible for the top split.
- final int maxOffset = (int) (mBounds1.height() * ADJUSTED_SPLIT_FRACTION_MAX);
+ final int maxOffset = (int) (getTopLeftBounds().height() * ADJUSTED_SPLIT_FRACTION_MAX);
return -Math.min(desireOffset, maxOffset);
}
@@ -1580,11 +1605,11 @@
t.setPosition(dividerLeash, mTempRect.left, mTempRect.top);
}
- getRefBounds1(mTempRect);
+ copyTopLeftRefBounds(mTempRect);
mTempRect.offset(0, mYOffsetForIme);
t.setPosition(leash1, mTempRect.left, mTempRect.top);
- getRefBounds2(mTempRect);
+ copyBottomRightRefBounds(mTempRect);
mTempRect.offset(0, mYOffsetForIme);
t.setPosition(leash2, mTempRect.left, mTempRect.top);
adjusted = true;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxCommandHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxCommandHandler.kt
new file mode 100644
index 0000000..819b110
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxCommandHandler.kt
@@ -0,0 +1,184 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.compatui.letterbox
+
+import android.content.Context
+import android.graphics.Color
+import com.android.internal.protolog.ProtoLog
+import com.android.window.flags.Flags
+import com.android.wm.shell.dagger.WMSingleton
+import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_APP_COMPAT
+import com.android.wm.shell.sysui.ShellCommandHandler
+import com.android.wm.shell.sysui.ShellCommandHandler.ShellCommandActionHandler
+import com.android.wm.shell.sysui.ShellInit
+import java.io.PrintWriter
+import javax.inject.Inject
+
+/**
+ * Handles the shell commands for the CompatUI.
+ *
+ * <p> Use with [adb shell dumpsys activity service SystemUIService WMShell letterbox
+ * <command>].
+ */
+@WMSingleton
+class LetterboxCommandHandler @Inject constructor(
+ private val context: Context,
+ shellInit: ShellInit,
+ shellCommandHandler: ShellCommandHandler,
+ private val letterboxConfiguration: LetterboxConfiguration
+) : ShellCommandActionHandler {
+
+ companion object {
+ @JvmStatic
+ private val TAG = "LetterboxCommandHandler"
+ }
+
+ init {
+ if (Flags.appCompatRefactoring()) {
+ ProtoLog.v(
+ WM_SHELL_APP_COMPAT,
+ "%s: %s",
+ TAG,
+ "Initializing LetterboxCommandHandler"
+ )
+ shellInit.addInitCallback({
+ shellCommandHandler.addCommandCallback("letterbox", this, this)
+ }, this)
+ }
+ }
+
+ override fun onShellCommand(args: Array<out String>?, pw: PrintWriter?): Boolean {
+ if (args == null || pw == null) {
+ pw!!.println("Missing arguments.")
+ return false
+ }
+ return when (args.size) {
+ 1 -> onShellDisplayCommand(args[0], pw)
+ 2 -> onShellUpdateCommand(args[0], args[1], pw)
+ else -> {
+ pw.println("Invalid command: " + args[0])
+ return false
+ }
+ }
+ }
+
+ override fun printShellCommandHelp(pw: PrintWriter?, prefix: String?) {
+ pw?.println(
+ """
+ $prefix backgroundColor color"
+ $prefix Color of letterbox which is to be used when letterbox background
+ $prefix type is 'solid-color'. See Color#parseColor for allowed color
+ $prefix formats (#RRGGBB and some colors by name, e.g. magenta or olive).
+ $prefix backgroundColorResource resource_name"
+ $prefix Color resource name of letterbox background which is used when
+ $prefix background type is 'solid-color'. Parameter is a color resource
+ $prefix name, for example, @android:color/system_accent2_50.
+ $prefix backgroundColorReset"
+ $prefix Resets the background color to the default value."
+ """.trimIndent()
+ )
+ }
+
+ private fun onShellUpdateCommand(command: String, value: String, pw: PrintWriter): Boolean {
+ when (command) {
+ "backgroundColor" -> {
+ return invokeWhenValid(
+ pw,
+ value,
+ ::strToColor,
+ { color ->
+ letterboxConfiguration.setLetterboxBackgroundColor(color)
+ },
+ { c -> "$c is not a valid color." }
+ )
+ }
+
+ "backgroundColorResource" -> return invokeWhenValid(
+ pw,
+ value,
+ ::nameToColorId,
+ { color ->
+ letterboxConfiguration.setLetterboxBackgroundColorResourceId(color)
+ },
+ { c ->
+ "$c is not a valid resource. Color in '@android:color/resource_name'" +
+ " format should be provided as an argument."
+ }
+ )
+
+ "backgroundColorReset" -> {
+ letterboxConfiguration.resetLetterboxBackgroundColor()
+ return true
+ }
+
+ else -> {
+ pw.println("Invalid command: $value")
+ return false
+ }
+ }
+ }
+
+ private fun onShellDisplayCommand(command: String, pw: PrintWriter): Boolean {
+ when (command) {
+ "backgroundColor" -> {
+ pw.println(
+ " Background color: " + Integer.toHexString(
+ letterboxConfiguration.getLetterboxBackgroundColor()
+ .toArgb()
+ )
+ )
+ return true
+ }
+
+ else -> {
+ pw.println("Invalid command: $command")
+ return false
+ }
+ }
+ }
+
+ private fun <T> invokeWhenValid(
+ pw: PrintWriter,
+ input: String,
+ converter: (String) -> T?,
+ consumer: (T) -> Unit,
+ errorMessage: (String) -> String = { value -> " Wrong input value: $value." }
+ ): Boolean {
+ converter(input)?.let {
+ consumer(it)
+ return true
+ }
+ pw.println(errorMessage(input))
+ return false
+ }
+
+ // Converts a String to Color if possible or it returns null otherwise.
+ private fun strToColor(str: String): Color? =
+ try {
+ Color.valueOf(Color.parseColor(str))
+ } catch (e: IllegalArgumentException) {
+ null
+ }
+
+ // Converts a resource id to Color if possible or it returns null otherwise.
+ private fun nameToColorId(str: String): Int? =
+ try {
+ context.resources.getIdentifier(str, "color", "com.android.internal")
+ } catch (e: IllegalArgumentException) {
+ null
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxConfiguration.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxConfiguration.kt
new file mode 100644
index 0000000..83a8e31
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxConfiguration.kt
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.compatui.letterbox
+
+import android.annotation.ColorRes
+import android.content.Context
+import android.graphics.Color
+import com.android.internal.R
+import com.android.wm.shell.dagger.WMSingleton
+import javax.inject.Inject
+
+/**
+ * Contains configuration properties for the letterbox implementation in Shell.
+ */
+@WMSingleton
+class LetterboxConfiguration @Inject constructor(
+ private val context: Context
+) {
+ // Color to use for the solid color letterbox background type.
+ private var letterboxBackgroundColorOverride: Color? = null
+
+ // Color resource id for the solid color letterbox background type.
+ private var letterboxBackgroundColorResourceIdOverride: Int? = null
+
+ /**
+ * Sets color of letterbox background which is used when using the solid background mode.
+ */
+ fun setLetterboxBackgroundColor(color: Color) {
+ letterboxBackgroundColorOverride = color
+ }
+
+ /**
+ * Sets color ID of letterbox background which is used when using the solid background mode.
+ */
+ fun setLetterboxBackgroundColorResourceId(@ColorRes colorId: Int) {
+ letterboxBackgroundColorResourceIdOverride = colorId
+ }
+
+ /**
+ * Gets color of letterbox background which is used when the solid color mode is active.
+ */
+ fun getLetterboxBackgroundColor(): Color {
+ if (letterboxBackgroundColorOverride != null) {
+ return letterboxBackgroundColorOverride!!
+ }
+ val colorId = if (letterboxBackgroundColorResourceIdOverride != null) {
+ letterboxBackgroundColorResourceIdOverride
+ } else {
+ R.color.config_letterboxBackgroundColor
+ }
+ // Query color dynamically because material colors extracted from wallpaper are updated
+ // when wallpaper is changed.
+ return Color.valueOf(context.getResources().getColor(colorId!!, /* theme */null))
+ }
+
+ /**
+ * Resets color of letterbox background to the default.
+ */
+ fun resetLetterboxBackgroundColor() {
+ letterboxBackgroundColorOverride = null
+ letterboxBackgroundColorResourceIdOverride = null
+ }
+
+ /**
+ * The background color for the Letterbox.
+ */
+ fun getBackgroundColorRgbArray(): FloatArray = getLetterboxBackgroundColor().components
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxController.kt
new file mode 100644
index 0000000..0ac7aff
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxController.kt
@@ -0,0 +1,150 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.compatui.letterbox
+
+import android.graphics.Rect
+import android.view.SurfaceControl
+import com.android.internal.protolog.ProtoLog
+import com.android.wm.shell.dagger.WMSingleton
+import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_APP_COMPAT
+import javax.inject.Inject
+
+/**
+ * Component responsible for handling the lifecycle of the letterbox surfaces.
+ */
+@WMSingleton
+class LetterboxController @Inject constructor(
+ private val letterboxConfiguration: LetterboxConfiguration
+) {
+
+ companion object {
+ /*
+ * Letterbox surfaces need to stay below the activity layer which is 0.
+ */
+ // TODO(b/378673153): Consider adding this to [TaskConstants].
+ @JvmStatic
+ private val TASK_CHILD_LAYER_LETTERBOX_BACKGROUND = -1000
+ @JvmStatic
+ private val TAG = "LetterboxController"
+ }
+
+ private val letterboxMap = mutableMapOf<LetterboxKey, LetterboxItem>()
+
+ /**
+ * Creates a Letterbox Surface for a given displayId/taskId if it doesn't exist.
+ */
+ fun createLetterboxSurface(
+ key: LetterboxKey,
+ startTransaction: SurfaceControl.Transaction,
+ parentLeash: SurfaceControl
+ ) {
+ letterboxMap.runOnItem(key, onMissed = { k, m ->
+ m[k] = LetterboxItem(
+ SurfaceControl.Builder()
+ .setName("ShellLetterboxSurface-$key")
+ .setHidden(true)
+ .setColorLayer()
+ .setParent(parentLeash)
+ .setCallsite("LetterboxController-createLetterboxSurface")
+ .build().apply {
+ startTransaction.setLayer(
+ this,
+ TASK_CHILD_LAYER_LETTERBOX_BACKGROUND
+ ).setColorSpaceAgnostic(this, true)
+ .setColor(this, letterboxConfiguration.getBackgroundColorRgbArray())
+ }
+ )
+ })
+ }
+
+ /**
+ * Invoked to destroy the surfaces for a letterbox session for given displayId/taskId.
+ */
+ fun destroyLetterboxSurface(
+ key: LetterboxKey,
+ startTransaction: SurfaceControl.Transaction
+ ) {
+ letterboxMap.runOnItem(key, onFound = { item ->
+ item.fullWindowSurface?.run {
+ startTransaction.remove(this)
+ }
+ })
+ letterboxMap.remove(key)
+ }
+
+ /**
+ * Invoked to show/hide the letterbox surfaces for given displayId/taskId.
+ */
+ fun updateLetterboxSurfaceVisibility(
+ key: LetterboxKey,
+ startTransaction: SurfaceControl.Transaction,
+ visible: Boolean = true
+ ) {
+ letterboxMap.runOnItem(key, onFound = { item ->
+ item.fullWindowSurface?.run {
+ startTransaction.setVisibility(this, visible)
+ }
+ })
+ }
+
+ /**
+ * Updates the bounds for the letterbox surfaces for given displayId/taskId.
+ */
+ fun updateLetterboxSurfaceBounds(
+ key: LetterboxKey,
+ startTransaction: SurfaceControl.Transaction,
+ bounds: Rect
+ ) {
+ letterboxMap.runOnItem(key, onFound = { item ->
+ item.fullWindowSurface?.run {
+ startTransaction.moveAndCrop(this, bounds)
+ }
+ })
+ }
+
+ /*
+ * Executes [onFound] on the [LetterboxItem] if present or [onMissed] if not present.
+ */
+ private fun MutableMap<LetterboxKey, LetterboxItem>.runOnItem(
+ key: LetterboxKey,
+ onFound: (LetterboxItem) -> Unit = { _ -> },
+ onMissed: (
+ LetterboxKey,
+ MutableMap<LetterboxKey, LetterboxItem>
+ ) -> Unit = { _, _ -> }
+ ) {
+ this[key]?.let {
+ return onFound(it)
+ }
+ return onMissed(key, this)
+ }
+
+ fun dump() {
+ ProtoLog.v(WM_SHELL_APP_COMPAT, "%s: %s", TAG, "${letterboxMap.keys}")
+ }
+
+ private fun SurfaceControl.Transaction.moveAndCrop(
+ surface: SurfaceControl,
+ rect: Rect
+ ): SurfaceControl.Transaction =
+ setPosition(surface, rect.left.toFloat(), rect.top.toFloat())
+ .setWindowCrop(
+ surface,
+ rect.width(),
+ rect.height()
+ )
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxData.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxData.kt
new file mode 100644
index 0000000..98fd247
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxData.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.compatui.letterbox
+
+import android.view.SurfaceControl
+
+// The key to use for identify the letterbox sessions.
+data class LetterboxKey(val displayId: Int, val taskId: Int)
+
+// Encapsulate the objects for the specific letterbox session.
+data class LetterboxItem(val fullWindowSurface: SurfaceControl?)
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxTransitionObserver.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxTransitionObserver.kt
new file mode 100644
index 0000000..67429bd
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxTransitionObserver.kt
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.compatui.letterbox
+
+import android.graphics.Rect
+import android.os.IBinder
+import android.view.SurfaceControl
+import android.window.TransitionInfo
+import com.android.internal.protolog.ProtoLog
+import com.android.window.flags.Flags.appCompatRefactoring
+import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_APP_COMPAT
+import com.android.wm.shell.shared.TransitionUtil.isClosingType
+import com.android.wm.shell.sysui.ShellInit
+import com.android.wm.shell.transition.Transitions
+
+/**
+ * The [TransitionObserver] to handle Letterboxing events in Shell.
+ */
+class LetterboxTransitionObserver(
+ shellInit: ShellInit,
+ private val transitions: Transitions,
+ private val letterboxController: LetterboxController
+) : Transitions.TransitionObserver {
+
+ companion object {
+ @JvmStatic
+ private val TAG = "LetterboxTransitionObserver"
+ }
+
+ init {
+ if (appCompatRefactoring()) {
+ ProtoLog.v(
+ WM_SHELL_APP_COMPAT,
+ "%s: %s",
+ TAG,
+ "Initializing LetterboxTransitionObserver"
+ )
+ shellInit.addInitCallback({
+ transitions.registerObserver(this)
+ }, this)
+ }
+ }
+
+ override fun onTransitionReady(
+ transition: IBinder,
+ info: TransitionInfo,
+ startTransaction: SurfaceControl.Transaction,
+ finishTransaction: SurfaceControl.Transaction
+ ) {
+ // We recognise the operation to execute and delegate to the LetterboxController
+ // the related operation.
+ // TODO(b/377875151): Identify Desktop Windowing Transactions.
+ // TODO(b/377857898): Handling multiple surfaces
+ // TODO(b/371500295): Handle input events detection.
+ for (change in info.changes) {
+ change.taskInfo?.let { ti ->
+ val key = LetterboxKey(ti.displayId, ti.taskId)
+ if (isClosingType(change.mode)) {
+ letterboxController.destroyLetterboxSurface(
+ key,
+ startTransaction
+ )
+ } else {
+ val isTopActivityLetterboxed = ti.appCompatTaskInfo.isTopActivityLetterboxed
+ if (isTopActivityLetterboxed) {
+ letterboxController.createLetterboxSurface(
+ key,
+ startTransaction,
+ change.leash
+ )
+ letterboxController.updateLetterboxSurfaceBounds(
+ key,
+ startTransaction,
+ Rect(
+ change.endRelOffset.x,
+ change.endRelOffset.y,
+ change.endAbsBounds.width(),
+ change.endAbsBounds.height()
+ )
+ )
+ }
+ letterboxController.updateLetterboxSurfaceVisibility(
+ key,
+ startTransaction,
+ isTopActivityLetterboxed
+ )
+ }
+ letterboxController.dump()
+ }
+ }
+ }
+}
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 77e041e..02df38e 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
@@ -795,13 +795,14 @@
static KeyguardTransitionHandler provideKeyguardTransitionHandler(
ShellInit shellInit,
ShellController shellController,
+ DisplayController displayController,
Transitions transitions,
TaskStackListenerImpl taskStackListener,
@ShellMainThread Handler mainHandler,
@ShellMainThread ShellExecutor mainExecutor) {
return new KeyguardTransitionHandler(
- shellInit, shellController, transitions, taskStackListener, mainHandler,
- mainExecutor);
+ shellInit, shellController, displayController, transitions, taskStackListener,
+ mainHandler, mainExecutor);
}
@WMSingleton
@@ -1024,10 +1025,13 @@
@WMSingleton
@Provides
static TaskStackTransitionObserver provideTaskStackTransitionObserver(
- Lazy<Transitions> transitions,
- ShellInit shellInit
+ ShellInit shellInit,
+ Lazy<ShellTaskOrganizer> shellTaskOrganizer,
+ ShellCommandHandler shellCommandHandler,
+ Lazy<Transitions> transitions
) {
- return new TaskStackTransitionObserver(transitions, shellInit);
+ return new TaskStackTransitionObserver(shellInit, shellTaskOrganizer, shellCommandHandler,
+ transitions);
}
//
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 601cf70..96f8024 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
@@ -20,6 +20,10 @@
import static android.window.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_TASK_LIMIT;
import static android.window.DesktopModeFlags.ENABLE_WINDOWING_TRANSITION_HANDLERS_OBSERVERS;
+import static com.android.hardware.input.Flags.manageKeyGestures;
+import static com.android.hardware.input.Flags.useKeyGestureEventHandler;
+
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.KeyguardManager;
import android.content.Context;
@@ -62,6 +66,9 @@
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.TaskStackListenerImpl;
+import com.android.wm.shell.compatui.letterbox.LetterboxCommandHandler;
+import com.android.wm.shell.compatui.letterbox.LetterboxController;
+import com.android.wm.shell.compatui.letterbox.LetterboxTransitionObserver;
import com.android.wm.shell.dagger.back.ShellBackAnimationModule;
import com.android.wm.shell.dagger.pip.PipModule;
import com.android.wm.shell.desktopmode.CloseDesktopTaskTransitionHandler;
@@ -73,6 +80,7 @@
import com.android.wm.shell.desktopmode.DesktopMixedTransitionHandler;
import com.android.wm.shell.desktopmode.DesktopModeDragAndDropTransitionHandler;
import com.android.wm.shell.desktopmode.DesktopModeEventLogger;
+import com.android.wm.shell.desktopmode.DesktopModeKeyGestureHandler;
import com.android.wm.shell.desktopmode.DesktopModeLoggerTransitionObserver;
import com.android.wm.shell.desktopmode.DesktopRepository;
import com.android.wm.shell.desktopmode.DesktopTaskChangeListener;
@@ -755,8 +763,6 @@
dragToDesktopTransitionHandler,
desktopImmersiveController.get(),
desktopRepository,
- desktopModeLoggerTransitionObserver,
- launchAdjacentController,
recentsTransitionHandler,
multiInstanceHelper,
mainExecutor,
@@ -764,8 +770,6 @@
recentTasksController.orElse(null),
interactionJankMonitor,
mainHandler,
- inputManager,
- focusTransitionObserver,
desktopModeEventLogger,
desktopTilingDecorViewModel);
}
@@ -880,6 +884,72 @@
@WMSingleton
@Provides
+ static Optional<DesktopModeKeyGestureHandler> provideDesktopModeKeyGestureHandler(
+ Context context,
+ Optional<DesktopModeWindowDecorViewModel> desktopModeWindowDecorViewModel,
+ Optional<DesktopTasksController> desktopTasksController,
+ InputManager inputManager,
+ ShellTaskOrganizer shellTaskOrganizer,
+ FocusTransitionObserver focusTransitionObserver) {
+ if (DesktopModeStatus.canEnterDesktopMode(context) && useKeyGestureEventHandler()
+ && manageKeyGestures()
+ && (Flags.enableMoveToNextDisplayShortcut()
+ || Flags.enableTaskResizingKeyboardShortcuts())) {
+ return Optional.of(new DesktopModeKeyGestureHandler(context,
+ desktopModeWindowDecorViewModel, desktopTasksController,
+ inputManager, shellTaskOrganizer, focusTransitionObserver));
+ }
+ return Optional.empty();
+ }
+
+ @WMSingleton
+ @Provides
+ static Optional<DesktopModeWindowDecorViewModel> provideDesktopModeWindowDecorViewModel(
+ Context context,
+ @ShellMainThread ShellExecutor shellExecutor,
+ @ShellMainThread Handler mainHandler,
+ Choreographer mainChoreographer,
+ @ShellBackgroundThread ShellExecutor bgExecutor,
+ ShellInit shellInit,
+ ShellCommandHandler shellCommandHandler,
+ IWindowManager windowManager,
+ ShellTaskOrganizer taskOrganizer,
+ @DynamicOverride DesktopRepository desktopRepository,
+ DisplayController displayController,
+ ShellController shellController,
+ DisplayInsetsController displayInsetsController,
+ SyncTransactionQueue syncQueue,
+ Transitions transitions,
+ Optional<DesktopTasksController> desktopTasksController,
+ RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
+ InteractionJankMonitor interactionJankMonitor,
+ AppToWebGenericLinksParser genericLinksParser,
+ AssistContentRequester assistContentRequester,
+ MultiInstanceHelper multiInstanceHelper,
+ Optional<DesktopTasksLimiter> desktopTasksLimiter,
+ AppHandleEducationController appHandleEducationController,
+ AppToWebEducationController appToWebEducationController,
+ WindowDecorCaptionHandleRepository windowDecorCaptionHandleRepository,
+ Optional<DesktopActivityOrientationChangeHandler> activityOrientationChangeHandler,
+ FocusTransitionObserver focusTransitionObserver,
+ DesktopModeEventLogger desktopModeEventLogger
+ ) {
+ if (!DesktopModeStatus.canEnterDesktopMode(context)) {
+ return Optional.empty();
+ }
+ return Optional.of(new DesktopModeWindowDecorViewModel(context, shellExecutor, mainHandler,
+ mainChoreographer, bgExecutor, shellInit, shellCommandHandler, windowManager,
+ taskOrganizer, desktopRepository, displayController, shellController,
+ displayInsetsController, syncQueue, transitions, desktopTasksController,
+ rootTaskDisplayAreaOrganizer, interactionJankMonitor, genericLinksParser,
+ assistContentRequester, multiInstanceHelper, desktopTasksLimiter,
+ appHandleEducationController, appToWebEducationController,
+ windowDecorCaptionHandleRepository, activityOrientationChangeHandler,
+ focusTransitionObserver, desktopModeEventLogger));
+ }
+
+ @WMSingleton
+ @Provides
static EnterDesktopTaskTransitionHandler provideEnterDesktopModeTaskTransitionHandler(
Transitions transitions,
Optional<DesktopTasksLimiter> desktopTasksLimiter,
@@ -1229,8 +1299,25 @@
@Provides
static Object provideIndependentShellComponentsToCreate(
DragAndDropController dragAndDropController,
+ @NonNull LetterboxTransitionObserver letterboxTransitionObserver,
+ @NonNull LetterboxCommandHandler letterboxCommandHandler,
Optional<DesktopTasksTransitionObserver> desktopTasksTransitionObserverOptional,
- Optional<DesktopDisplayEventHandler> desktopDisplayEventHandler) {
+ Optional<DesktopDisplayEventHandler> desktopDisplayEventHandler,
+ Optional<DesktopModeKeyGestureHandler> desktopModeKeyGestureHandler) {
return new Object();
}
+
+ //
+ // App Compat
+ //
+
+ @WMSingleton
+ @Provides
+ static LetterboxTransitionObserver provideLetterboxTransitionObserver(
+ @NonNull ShellInit shellInit,
+ @NonNull Transitions transitions,
+ @NonNull LetterboxController letterboxController
+ ) {
+ return new LetterboxTransitionObserver(shellInit, transitions, letterboxController);
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandler.kt
index 2001f97..82c2ebc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandler.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandler.kt
@@ -23,6 +23,7 @@
import android.os.IBinder
import android.view.SurfaceControl
import android.view.WindowManager
+import android.view.WindowManager.TRANSIT_OPEN
import android.window.DesktopModeFlags
import android.window.TransitionInfo
import android.window.TransitionInfo.Change
@@ -95,7 +96,7 @@
fun startLaunchTransition(
@WindowManager.TransitionType transitionType: Int,
wct: WindowContainerTransaction,
- taskId: Int,
+ taskId: Int?,
minimizingTaskId: Int? = null,
exitingImmersiveTask: Int? = null,
): IBinder {
@@ -216,12 +217,12 @@
): Boolean {
// Check if there's also an immersive change during this launch.
val immersiveExitChange = pending.exitingImmersiveTask?.let { exitingTask ->
- findDesktopTaskChange(info, exitingTask)
+ findTaskChange(info, exitingTask)
}
val minimizeChange = pending.minimizingTask?.let { minimizingTask ->
- findDesktopTaskChange(info, minimizingTask)
+ findTaskChange(info, minimizingTask)
}
- val launchChange = findDesktopTaskChange(info, pending.launchingTask)
+ val launchChange = findDesktopTaskLaunchChange(info, pending.launchingTask)
if (launchChange == null) {
check(minimizeChange == null)
check(immersiveExitChange == null)
@@ -291,7 +292,7 @@
): Boolean {
if (!DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION.isTrue) return false
- val minimizeChange = findDesktopTaskChange(info, pending.minimizingTask)
+ val minimizeChange = findTaskChange(info, pending.minimizingTask)
if (minimizeChange == null) {
logW("Should have minimizing desktop task")
return false
@@ -417,8 +418,24 @@
}
}
- private fun findDesktopTaskChange(info: TransitionInfo, taskId: Int): TransitionInfo.Change? {
- return info.changes.firstOrNull { change -> change.taskInfo?.taskId == taskId }
+ private fun findTaskChange(info: TransitionInfo, taskId: Int): TransitionInfo.Change? =
+ info.changes.firstOrNull { change -> change.taskInfo?.taskId == taskId }
+
+ private fun findDesktopTaskLaunchChange(
+ info: TransitionInfo,
+ launchTaskId: Int?
+ ): TransitionInfo.Change? {
+ return if (launchTaskId != null) {
+ // Launching a known task (probably from background or moving to front), so
+ // specifically look for it.
+ findTaskChange(info, launchTaskId)
+ } else {
+ // Launching a new task, so the first opening freeform task.
+ info.changes.firstOrNull { change ->
+ change.mode == TRANSIT_OPEN
+ && change.taskInfo != null && change.taskInfo!!.isFreeform
+ }
+ }
}
private fun WindowContainerTransaction?.merge(
@@ -441,7 +458,7 @@
/** A task is opening or moving to front. */
data class Launch(
override val transition: IBinder,
- val launchingTask: Int,
+ val launchingTask: Int?,
val minimizingTask: Int?,
val exitingImmersiveTask: Int?,
) : PendingMixedTransition()
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeKeyGestureHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeKeyGestureHandler.kt
new file mode 100644
index 0000000..ac07eaa
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeKeyGestureHandler.kt
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.desktopmode
+
+import android.hardware.input.KeyGestureEvent
+
+import android.hardware.input.InputManager
+import android.hardware.input.InputManager.KeyGestureEventHandler
+import android.os.IBinder
+import com.android.window.flags.Flags.enableMoveToNextDisplayShortcut
+import com.android.wm.shell.ShellTaskOrganizer
+import android.app.ActivityManager.RunningTaskInfo
+import com.android.wm.shell.windowdecor.DesktopModeWindowDecorViewModel
+import com.android.internal.protolog.ProtoLog
+import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
+import android.content.Context
+import com.android.hardware.input.Flags.manageKeyGestures
+import com.android.window.flags.Flags.enableTaskResizingKeyboardShortcuts
+import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ResizeTrigger
+import com.android.wm.shell.transition.FocusTransitionObserver
+import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
+import java.util.Optional
+
+/**
+ * Handles key gesture events (keyboard shortcuts) in Desktop Mode.
+ */
+class DesktopModeKeyGestureHandler(
+ private val context: Context,
+ private val desktopModeWindowDecorViewModel: Optional<DesktopModeWindowDecorViewModel>,
+ private val desktopTasksController: Optional<DesktopTasksController>,
+ inputManager: InputManager,
+ private val shellTaskOrganizer: ShellTaskOrganizer,
+ private val focusTransitionObserver: FocusTransitionObserver,
+ ) : KeyGestureEventHandler {
+
+ init {
+ inputManager.registerKeyGestureEventHandler(this)
+ }
+
+ override fun handleKeyGestureEvent(event: KeyGestureEvent, focusedToken: IBinder?): Boolean {
+ if (!isKeyGestureSupported(event.keyGestureType) || !desktopTasksController.isPresent
+ || !desktopModeWindowDecorViewModel.isPresent) {
+ return false
+ }
+ when (event.keyGestureType) {
+ KeyGestureEvent.KEY_GESTURE_TYPE_MOVE_TO_NEXT_DISPLAY -> {
+ logV("Key gesture MOVE_TO_NEXT_DISPLAY is handled")
+ getGloballyFocusedFreeformTask()?.let {
+ desktopTasksController.get().moveToNextDisplay(
+ it.taskId
+ )
+ }
+ return true
+ }
+ // TODO(b/375356876): Modify function to pass in keyboard shortcut as the input
+ // method for logging task resize
+ KeyGestureEvent.KEY_GESTURE_TYPE_SNAP_LEFT_FREEFORM_WINDOW -> {
+ logV("Key gesture SNAP_LEFT_FREEFORM_WINDOW is handled")
+ getGloballyFocusedFreeformTask()?.let {
+ desktopModeWindowDecorViewModel.get().onSnapResize(
+ it.taskId,
+ true,
+ null
+ )
+ }
+ return true
+ }
+ KeyGestureEvent.KEY_GESTURE_TYPE_SNAP_RIGHT_FREEFORM_WINDOW -> {
+ logV("Key gesture SNAP_RIGHT_FREEFORM_WINDOW is handled")
+ getGloballyFocusedFreeformTask()?.let {
+ desktopModeWindowDecorViewModel.get().onSnapResize(
+ it.taskId,
+ false,
+ null
+ )
+ }
+ return true
+ }
+ KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_MAXIMIZE_FREEFORM_WINDOW -> {
+ logV("Key gesture TOGGLE_MAXIMIZE_FREEFORM_WINDOW is handled")
+ getGloballyFocusedFreeformTask()?.let {
+ desktopTasksController.get().toggleDesktopTaskSize(
+ it,
+ ResizeTrigger.MAXIMIZE_MENU,
+ null,
+ )
+ }
+ return true
+ }
+ KeyGestureEvent.KEY_GESTURE_TYPE_MINIMIZE_FREEFORM_WINDOW -> {
+ logV("Key gesture MINIMIZE_FREEFORM_WINDOW is handled")
+ getGloballyFocusedFreeformTask()?.let {
+ desktopTasksController.get().minimizeTask(
+ it,
+ )
+ }
+ return true
+ }
+ else -> return false
+ }
+ }
+
+ override fun isKeyGestureSupported(gestureType: Int): Boolean = when (gestureType) {
+ KeyGestureEvent.KEY_GESTURE_TYPE_MOVE_TO_NEXT_DISPLAY
+ -> enableMoveToNextDisplayShortcut()
+ KeyGestureEvent.KEY_GESTURE_TYPE_SNAP_LEFT_FREEFORM_WINDOW,
+ KeyGestureEvent.KEY_GESTURE_TYPE_SNAP_RIGHT_FREEFORM_WINDOW,
+ KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_MAXIMIZE_FREEFORM_WINDOW,
+ KeyGestureEvent.KEY_GESTURE_TYPE_MINIMIZE_FREEFORM_WINDOW
+ -> enableTaskResizingKeyboardShortcuts() && manageKeyGestures()
+ else -> false
+ }
+
+ // TODO: b/364154795 - wait for the completion of moveToNextDisplay transition, otherwise it
+ // will pick a wrong task when a user quickly perform other actions with keyboard shortcuts
+ // after moveToNextDisplay, and move this to FocusTransitionObserver class.
+ private fun getGloballyFocusedFreeformTask(): RunningTaskInfo? =
+ shellTaskOrganizer.getRunningTasks().find { taskInfo ->
+ taskInfo.windowingMode == WINDOWING_MODE_FREEFORM &&
+ focusTransitionObserver.hasGlobalFocus(taskInfo)
+ }
+
+ private fun logV(msg: String, vararg arguments: Any?) {
+ ProtoLog.v(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments)
+ }
+
+ companion object {
+ private const val TAG = "DesktopModeKeyGestureHandler"
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt
index 08ca55f..7fcb767 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt
@@ -411,6 +411,12 @@
desktopTaskDataByDisplayId[displayId]?.freeformTasksInZOrder?.toDumpString())
// Remove task from unminimized task if it is minimized.
unminimizeTask(displayId, taskId)
+ // Mark task as not in immersive if it was immersive.
+ setTaskInFullImmersiveState(
+ displayId = displayId,
+ taskId = taskId,
+ immersive = false
+ )
removeActiveTask(taskId)
removeVisibleTask(taskId)
if (DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_PERSISTENCE.isTrue()) {
@@ -487,6 +493,9 @@
mainCoroutineScope.launch {
try {
persistentRepository.addOrUpdateDesktop(
+ // Use display id as desktop id for now since only once desktop per display
+ // is supported.
+ desktopId = displayId,
visibleTasks = desktopTaskDataByDisplayIdCopy.visibleTasks,
minimizedTasks = desktopTaskDataByDisplayIdCopy.minimizedTasks,
freeformTasksInZOrder = desktopTaskDataByDisplayIdCopy.freeformTasksInZOrder
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
index 6928c25..a0bdd9f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
@@ -35,9 +35,6 @@
import android.graphics.PointF
import android.graphics.Rect
import android.graphics.Region
-import android.hardware.input.InputManager
-import android.hardware.input.InputManager.KeyGestureEventHandler
-import android.hardware.input.KeyGestureEvent
import android.os.Binder
import android.os.Handler
import android.os.IBinder
@@ -46,7 +43,6 @@
import android.util.Size
import android.view.Display.DEFAULT_DISPLAY
import android.view.DragEvent
-import android.view.KeyEvent
import android.view.MotionEvent
import android.view.SurfaceControl
import android.view.SurfaceControl.Transaction
@@ -66,7 +62,6 @@
import android.window.TransitionRequestInfo
import android.window.WindowContainerTransaction
import androidx.annotation.BinderThread
-import com.android.hardware.input.Flags.useKeyGestureEventHandler
import com.android.internal.annotations.VisibleForTesting
import com.android.internal.jank.Cuj.CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_HOLD
import com.android.internal.jank.Cuj.CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_RELEASE
@@ -76,12 +71,13 @@
import com.android.internal.protolog.ProtoLog
import com.android.window.flags.Flags
import com.android.window.flags.Flags.enableMoveToNextDisplayShortcut
+import com.android.wm.shell.Flags.enableFlexibleSplit
+import com.android.wm.shell.R
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.DisplayLayout
import com.android.wm.shell.common.ExternalInterfaceBinder
-import com.android.wm.shell.common.LaunchAdjacentController
import com.android.wm.shell.common.MultiInstanceHelper
import com.android.wm.shell.common.MultiInstanceHelper.Companion.getComponent
import com.android.wm.shell.common.RemoteCallable
@@ -89,7 +85,6 @@
import com.android.wm.shell.common.SingleInstanceRemoteListener
import com.android.wm.shell.common.SyncTransactionQueue
import com.android.wm.shell.compatui.isTopActivityExemptFromDesktopWindowing
-import com.android.wm.shell.desktopmode.DesktopImmersiveController.ExitResult
import com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.DragStartState
import com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.IndicatorType
import com.android.wm.shell.desktopmode.DesktopRepository.VisibleTasksListener
@@ -109,6 +104,7 @@
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus.DESKTOP_DENSITY_OVERRIDE
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus.useDesktopOverrideDensity
import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource
+import com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_INDEX_UNDEFINED
import com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT
import com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT
import com.android.wm.shell.splitscreen.SplitScreenController
@@ -117,7 +113,6 @@
import com.android.wm.shell.sysui.ShellController
import com.android.wm.shell.sysui.ShellInit
import com.android.wm.shell.sysui.UserChangeListener
-import com.android.wm.shell.transition.FocusTransitionObserver
import com.android.wm.shell.transition.OneShotRemoteHandler
import com.android.wm.shell.transition.Transitions
import com.android.wm.shell.transition.Transitions.TransitionFinishCallback
@@ -135,6 +130,10 @@
import java.util.concurrent.Executor
import java.util.function.Consumer
import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ResizeTrigger
+import com.android.wm.shell.desktopmode.DragToDesktopTransitionHandler.Companion.DRAG_TO_DESKTOP_FINISH_ANIM_DURATION_MS
+import com.android.wm.shell.desktopmode.EnterDesktopTaskTransitionHandler.FREEFORM_ANIMATION_DURATION
+import com.android.wm.shell.desktopmode.ExitDesktopTaskTransitionHandler.FULLSCREEN_ANIMATION_DURATION
+
/** Handles moving tasks in and out of desktop */
class DesktopTasksController(
private val context: Context,
@@ -157,8 +156,6 @@
private val dragToDesktopTransitionHandler: DragToDesktopTransitionHandler,
private val desktopImmersiveController: DesktopImmersiveController,
private val taskRepository: DesktopRepository,
- private val desktopModeLoggerTransitionObserver: DesktopModeLoggerTransitionObserver,
- private val launchAdjacentController: LaunchAdjacentController,
private val recentsTransitionHandler: RecentsTransitionHandler,
private val multiInstanceHelper: MultiInstanceHelper,
@ShellMainThread private val mainExecutor: ShellExecutor,
@@ -166,16 +163,13 @@
private val recentTasksController: RecentTasksController?,
private val interactionJankMonitor: InteractionJankMonitor,
@ShellMainThread private val handler: Handler,
- private val inputManager: InputManager,
- private val focusTransitionObserver: FocusTransitionObserver,
private val desktopModeEventLogger: DesktopModeEventLogger,
private val desktopTilingDecorViewModel: DesktopTilingDecorViewModel,
) :
RemoteCallable<DesktopTasksController>,
Transitions.TransitionHandler,
DragAndDropController.DragAndDropListener,
- UserChangeListener,
- KeyGestureEventHandler {
+ UserChangeListener {
private val desktopMode: DesktopModeImpl
private var visualIndicator: DesktopModeVisualIndicator? = null
@@ -208,6 +202,9 @@
@VisibleForTesting
var taskbarDesktopTaskListener: TaskbarDesktopTaskListener? = null
+ @VisibleForTesting
+ var desktopModeEnterExitTransitionListener: DesktopModeEntryExitTransitionListener? = null
+
/** Task id of the task currently being dragged from fullscreen/split. */
val draggingTaskId
get() = dragToDesktopTransitionHandler.draggingTaskId
@@ -249,9 +246,6 @@
}
)
dragAndDropController.addListener(this)
- if (useKeyGestureEventHandler() && enableMoveToNextDisplayShortcut()) {
- inputManager.registerKeyGestureEventHandler(this)
- }
}
@VisibleForTesting
@@ -396,6 +390,7 @@
)
// TODO(343149901): Add DPI changes for task launch
val transition = enterDesktopTaskTransitionHandler.moveToDesktop(wct, transitionSource)
+ desktopModeEnterExitTransitionListener?.onEnterDesktopModeTransitionStarted(FREEFORM_ANIMATION_DURATION)
taskIdToMinimize?.let { addPendingMinimizeTransition(transition, it) }
exitResult.asExit()?.runOnTransitionStart?.invoke(transition)
return true
@@ -425,6 +420,7 @@
addMoveToDesktopChanges(wct, task)
val transition = enterDesktopTaskTransitionHandler.moveToDesktop(wct, transitionSource)
+ desktopModeEnterExitTransitionListener?.onEnterDesktopModeTransitionStarted(FREEFORM_ANIMATION_DURATION)
taskIdToMinimize?.let { addPendingMinimizeTransition(transition, it) }
exitResult.asExit()?.runOnTransitionStart?.invoke(transition)
}
@@ -466,6 +462,9 @@
val exitResult = desktopImmersiveController.exitImmersiveIfApplicable(
wct, taskInfo.displayId)
val transition = dragToDesktopTransitionHandler.finishDragToDesktopTransition(wct)
+ desktopModeEnterExitTransitionListener?.onEnterDesktopModeTransitionStarted(
+ DRAG_TO_DESKTOP_FINISH_ANIM_DURATION_MS.toInt()
+ )
transition?.let {
taskIdToMinimize?.let { taskId -> addPendingMinimizeTransition(it, taskId) }
exitResult.asExit()?.runOnTransitionStart?.invoke(transition)
@@ -501,9 +500,7 @@
): ((IBinder) -> Unit)? {
val taskId = taskInfo.taskId
desktopTilingDecorViewModel.removeTaskIfTiled(displayId, taskId)
- if (taskRepository.isOnlyVisibleNonClosingTask(taskId)) {
- removeWallpaperActivity(wct)
- }
+ performDesktopExitCleanupIfNeeded(taskId, wct)
taskRepository.addClosingTask(displayId, taskId)
taskbarDesktopTaskListener?.onTaskbarCornerRoundingUpdate(
doesAnyTaskRequireTaskbarRounding(
@@ -519,11 +516,7 @@
val taskId = taskInfo.taskId
val displayId = taskInfo.displayId
val wct = WindowContainerTransaction()
- if (taskRepository.isOnlyVisibleNonClosingTask(taskId)) {
- // Perform clean up of the desktop wallpaper activity if the minimized window task is
- // the last active task.
- removeWallpaperActivity(wct)
- }
+ performDesktopExitCleanupIfNeeded(taskId, wct)
// Notify immersive handler as it might need to exit immersive state.
val exitResult = desktopImmersiveController.exitImmersiveIfApplicable(wct, taskInfo)
@@ -592,6 +585,11 @@
position,
mOnAnimationFinishedCallback
)
+
+ // handles case where we are moving to full screen without closing all DW tasks.
+ if (!taskRepository.isOnlyVisibleNonClosingTask(task.taskId)) {
+ desktopModeEnterExitTransitionListener?.onExitDesktopModeTransitionStarted(FULLSCREEN_ANIMATION_DURATION)
+ }
}
/**
@@ -618,25 +616,18 @@
private fun moveBackgroundTaskToFront(taskId: Int, remoteTransition: RemoteTransition?) {
logV("moveBackgroundTaskToFront taskId=%s", taskId)
val wct = WindowContainerTransaction()
- // TODO: b/342378842 - Instead of using default display, support multiple displays
- val exitResult = desktopImmersiveController.exitImmersiveIfApplicable(
- wct = wct,
- displayId = DEFAULT_DISPLAY,
- excludeTaskId = taskId,
- )
wct.startTask(
taskId,
ActivityOptions.makeBasic().apply {
launchWindowingMode = WINDOWING_MODE_FREEFORM
}.toBundle(),
)
- val transition = startLaunchTransition(
+ startLaunchTransition(
TRANSIT_OPEN,
wct,
taskId,
remoteTransition = remoteTransition
)
- exitResult.asExit()?.runOnTransitionStart?.invoke(transition)
}
/**
@@ -655,47 +646,53 @@
}
val wct = WindowContainerTransaction()
wct.reorder(taskInfo.token, true /* onTop */, true /* includingParents */)
- val result = desktopImmersiveController.exitImmersiveIfApplicable(
- wct = wct,
- displayId = taskInfo.displayId,
- excludeTaskId = taskInfo.taskId,
- )
- val exitResult = if (result is ExitResult.Exit) { result } else { null }
- val transition = startLaunchTransition(
+ startLaunchTransition(
transitionType = TRANSIT_TO_FRONT,
wct = wct,
- taskId = taskInfo.taskId,
- exitingImmersiveTask = exitResult?.exitingTask,
+ launchingTaskId = taskInfo.taskId,
remoteTransition = remoteTransition,
displayId = taskInfo.displayId,
)
- exitResult?.runOnTransitionStart?.invoke(transition)
}
private fun startLaunchTransition(
transitionType: Int,
wct: WindowContainerTransaction,
- taskId: Int,
- exitingImmersiveTask: Int? = null,
+ launchingTaskId: Int?,
remoteTransition: RemoteTransition? = null,
displayId: Int = DEFAULT_DISPLAY,
): IBinder {
- val taskIdToMinimize = addAndGetMinimizeChanges(displayId, wct, taskId)
+ val taskIdToMinimize = if (launchingTaskId != null) {
+ addAndGetMinimizeChanges(displayId, wct, newTaskId = launchingTaskId)
+ } else {
+ logW("Starting desktop task launch without checking the task-limit")
+ // TODO(b/378920066): This currently does not respect the desktop window limit.
+ // It's possible that |launchingTaskId| is null when launching using an intent, and
+ // the task-limit should be respected then too.
+ null
+ }
+ val exitImmersiveResult = desktopImmersiveController.exitImmersiveIfApplicable(
+ wct = wct,
+ displayId = displayId,
+ excludeTaskId = launchingTaskId,
+ )
if (remoteTransition == null) {
val t = desktopMixedTransitionHandler.startLaunchTransition(
transitionType = transitionType,
wct = wct,
- taskId = taskId,
+ taskId = launchingTaskId,
minimizingTaskId = taskIdToMinimize,
- exitingImmersiveTask = exitingImmersiveTask,
+ exitingImmersiveTask = exitImmersiveResult.asExit()?.exitingTask,
)
taskIdToMinimize?.let { addPendingMinimizeTransition(t, it) }
+ exitImmersiveResult.asExit()?.runOnTransitionStart?.invoke(t)
return t
}
if (taskIdToMinimize == null) {
val remoteTransitionHandler = OneShotRemoteHandler(mainExecutor, remoteTransition)
val t = transitions.startTransition(transitionType, wct, remoteTransitionHandler)
remoteTransitionHandler.setTransition(t)
+ exitImmersiveResult.asExit()?.runOnTransitionStart?.invoke(t)
return t
}
val remoteTransitionHandler =
@@ -704,6 +701,7 @@
val t = transitions.startTransition(transitionType, wct, remoteTransitionHandler)
remoteTransitionHandler.setTransition(t)
taskIdToMinimize.let { addPendingMinimizeTransition(t, it) }
+ exitImmersiveResult.asExit()?.runOnTransitionStart?.invoke(t)
return t
}
@@ -794,6 +792,10 @@
resizeTrigger: ResizeTrigger,
motionEvent: MotionEvent?,
) {
+ desktopModeEventLogger.logTaskResizingStarted(
+ resizeTrigger, motionEvent, taskInfo, displayController
+ )
+
val displayLayout = displayController.getDisplayLayout(taskInfo.displayId) ?: return
val stableBounds = Rect().apply { displayLayout.getStableBounds(this) }
@@ -872,9 +874,6 @@
return
}
- desktopModeEventLogger.logTaskResizingStarted(
- ResizeTrigger.DRAG_TO_TOP_RESIZE_TRIGGER, motionEvent, taskInfo, displayController
- )
toggleDesktopTaskSize(taskInfo, ResizeTrigger.DRAG_TO_TOP_RESIZE_TRIGGER, motionEvent)
}
@@ -962,13 +961,17 @@
*/
fun snapToHalfScreen(
taskInfo: RunningTaskInfo,
- taskSurface: SurfaceControl,
+ taskSurface: SurfaceControl?,
currentDragBounds: Rect,
position: SnapPosition,
resizeTrigger: ResizeTrigger,
motionEvent: MotionEvent?,
desktopWindowDecoration: DesktopModeWindowDecoration,
) {
+ desktopModeEventLogger.logTaskResizingStarted(
+ resizeTrigger, motionEvent, taskInfo, displayController
+ )
+
if (DesktopModeFlags.ENABLE_TILE_RESIZING.isTrue()) {
val isTiled = desktopTilingDecorViewModel.snapToHalfScreen(
taskInfo,
@@ -994,7 +997,7 @@
// Handle the case where we attempt to snap resize when already snap resized: the task
// position won't need to change but we want to animate the surface going back to the
// snapped position from the "dragged-to-the-edge" position.
- if (destinationBounds != currentDragBounds) {
+ if (destinationBounds != currentDragBounds && taskSurface != null) {
returnToDragStartAnimator.start(
taskInfo.taskId,
taskSurface,
@@ -1011,8 +1014,40 @@
toggleResizeDesktopTaskTransitionHandler.startTransition(wct, currentDragBounds)
}
+ /**
+ * Handles snap resizing a [taskInfo] to [position] instantaneously, for example when the
+ * [resizeTrigger] is the snap resize menu using any [motionEvent] or a keyboard shortcut.
+ */
+ fun handleInstantSnapResizingTask(
+ taskInfo: RunningTaskInfo,
+ position: SnapPosition,
+ resizeTrigger: ResizeTrigger,
+ motionEvent: MotionEvent? = null,
+ desktopModeWindowDecoration: DesktopModeWindowDecoration,
+ ) {
+ if (!isSnapResizingAllowed(taskInfo)) {
+ Toast.makeText(
+ getContext(),
+ R.string.desktop_mode_non_resizable_snap_text,
+ Toast.LENGTH_SHORT
+ ).show()
+ return
+ }
+
+ snapToHalfScreen(
+ taskInfo,
+ null,
+ taskInfo.configuration.windowConfiguration.bounds,
+ position,
+ resizeTrigger,
+ motionEvent,
+ desktopModeWindowDecoration
+ )
+ }
+
+
@VisibleForTesting
- fun handleSnapResizingTask(
+ fun handleSnapResizingTaskOnDrag(
taskInfo: RunningTaskInfo,
position: SnapPosition,
taskSurface: SurfaceControl,
@@ -1022,7 +1057,7 @@
desktopModeWindowDecoration: DesktopModeWindowDecoration,
) {
releaseVisualIndicator()
- if (!taskInfo.isResizeable && DISABLE_NON_RESIZABLE_APP_SNAP_RESIZE.isTrue()) {
+ if (!isSnapResizingAllowed(taskInfo)) {
interactionJankMonitor.begin(
taskSurface, context, handler, CUJ_DESKTOP_MODE_SNAP_RESIZE, "drag_non_resizable"
)
@@ -1047,9 +1082,6 @@
} else {
ResizeTrigger.DRAG_RIGHT
}
- desktopModeEventLogger.logTaskResizingStarted(
- resizeTrigger, motionEvent, taskInfo, displayController
- )
interactionJankMonitor.begin(
taskSurface, context, handler, CUJ_DESKTOP_MODE_SNAP_RESIZE, "drag_resizable"
)
@@ -1065,6 +1097,9 @@
}
}
+ private fun isSnapResizingAllowed(taskInfo: RunningTaskInfo) =
+ taskInfo.isResizeable || !DISABLE_NON_RESIZABLE_APP_SNAP_RESIZE.isTrue()
+
private fun getSnapBounds(taskInfo: RunningTaskInfo, position: SnapPosition): Rect {
val displayLayout = displayController.getDisplayLayout(taskInfo.displayId) ?: return Rect()
@@ -1205,6 +1240,21 @@
}
}
+ /**
+ * Remove wallpaper activity if task provided is last task and wallpaper activity token is not
+ * null
+ */
+ private fun performDesktopExitCleanupIfNeeded(taskId: Int, wct: WindowContainerTransaction) {
+ if (!taskRepository.isOnlyVisibleNonClosingTask(taskId)) {
+ return
+ }
+ desktopModeEnterExitTransitionListener?.onExitDesktopModeTransitionStarted(FULLSCREEN_ANIMATION_DURATION)
+ if (taskRepository.wallpaperActivityToken != null) {
+ removeWallpaperActivity(wct)
+ }
+ }
+
+
fun releaseVisualIndicator() {
val t = SurfaceControl.Transaction()
visualIndicator?.releaseVisualIndicator(t)
@@ -1465,17 +1515,25 @@
WINDOWING_MODE_MULTI_WINDOW -> {
val splitPosition = splitScreenController
.determineNewInstancePosition(callingTaskInfo)
+ // TODO(b/349828130) currently pass in index_undefined until we can revisit these
+ // specific cases in the future.
+ val splitIndex = if (enableFlexibleSplit())
+ splitScreenController.determineNewInstanceIndex(callingTaskInfo) else
+ SPLIT_INDEX_UNDEFINED
splitScreenController.startIntent(
launchIntent, context.userId, fillIn, splitPosition,
options.toBundle(), null /* hideTaskToken */,
- true /* forceLaunchNewTask */
- )
+ true /* forceLaunchNewTask */, splitIndex)
}
WINDOWING_MODE_FREEFORM -> {
- // TODO(b/336289597): This currently does not respect the desktop window limit.
val wct = WindowContainerTransaction()
wct.sendPendingIntent(launchIntent, fillIn, options.toBundle())
- transitions.startTransition(TRANSIT_OPEN, wct, null)
+ startLaunchTransition(
+ transitionType = TRANSIT_OPEN,
+ wct = wct,
+ launchingTaskId = null,
+ displayId = callingTaskInfo.displayId
+ )
}
}
}
@@ -1610,6 +1668,13 @@
transition, wct, task.displayId
)
}
+ } else if (taskRepository.isActiveTask(task.taskId)) {
+ // If a freeform task receives a request for a fullscreen launch, apply the same
+ // changes we do for similar transitions. The task not having WINDOWING_MODE_UNDEFINED
+ // set when needed can interfere with future split / multi-instance transitions.
+ return WindowContainerTransaction().also { wct ->
+ addMoveToFullscreenChanges(wct, task)
+ }
}
return null
}
@@ -1637,12 +1702,7 @@
return null
val wct = WindowContainerTransaction()
- if (taskRepository.isOnlyVisibleNonClosingTask(task.taskId)
- && taskRepository.wallpaperActivityToken != null
- ) {
- // Remove wallpaper activity when the last active task is removed
- removeWallpaperActivity(wct)
- }
+ performDesktopExitCleanupIfNeeded(task.taskId, wct)
if (!DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION.isTrue()) {
taskRepository.addClosingTask(task.displayId, task.taskId)
@@ -1733,10 +1793,8 @@
if (useDesktopOverrideDensity()) {
wct.setDensityDpi(taskInfo.token, getDefaultDensityDpi())
}
- if (taskRepository.isOnlyVisibleNonClosingTask(taskInfo.taskId)) {
- // Remove wallpaper activity when leaving desktop mode
- removeWallpaperActivity(wct)
- }
+
+ performDesktopExitCleanupIfNeeded(taskInfo.taskId, wct)
}
private fun cascadeWindow(bounds: Rect, displayLayout: DisplayLayout, displayId: Int) {
@@ -1765,10 +1823,8 @@
// The task's density may have been overridden in freeform; revert it here as we don't
// want it overridden in multi-window.
wct.setDensityDpi(taskInfo.token, getDefaultDensityDpi())
- if (taskRepository.isOnlyVisibleNonClosingTask(taskInfo.taskId)) {
- // Remove wallpaper activity when leaving desktop mode
- removeWallpaperActivity(wct)
- }
+
+ performDesktopExitCleanupIfNeeded(taskInfo.taskId, wct)
}
/** Returns the ID of the Task that will be minimized, or null if no task will be minimized. */
@@ -1832,26 +1888,12 @@
getFocusedFreeformTask(displayId)?.let { requestSplit(it, leftOrTop) }
}
- /** Move the focused desktop task in given `displayId` to next display. */
- fun moveFocusedTaskToNextDisplay(displayId: Int) {
- getFocusedFreeformTask(displayId)?.let { moveToNextDisplay(it.taskId) }
- }
-
private fun getFocusedFreeformTask(displayId: Int): RunningTaskInfo? {
return shellTaskOrganizer.getRunningTasks(displayId).find { taskInfo ->
taskInfo.isFocused && taskInfo.windowingMode == WINDOWING_MODE_FREEFORM
}
}
- // TODO(b/364154795): wait for the completion of moveToNextDisplay transition, otherwise it will
- // pick a wrong task when a user quickly perform other actions with keyboard shortcuts after
- // moveToNextDisplay.
- private fun getGloballyFocusedFreeformTask(): RunningTaskInfo? =
- shellTaskOrganizer.getRunningTasks().find { taskInfo ->
- taskInfo.windowingMode == WINDOWING_MODE_FREEFORM &&
- focusTransitionObserver.hasGlobalFocus(taskInfo)
- }
-
/**
* Requests a task be transitioned from desktop to split select. Applies needed windowing
* changes if this transition is enabled.
@@ -1993,7 +2035,7 @@
}
}
IndicatorType.TO_SPLIT_LEFT_INDICATOR -> {
- handleSnapResizingTask(
+ handleSnapResizingTaskOnDrag(
taskInfo,
SnapPosition.LEFT,
taskSurface,
@@ -2004,7 +2046,7 @@
)
}
IndicatorType.TO_SPLIT_RIGHT_INDICATOR -> {
- handleSnapResizingTask(
+ handleSnapResizingTaskOnDrag(
taskInfo,
SnapPosition.RIGHT,
taskSurface,
@@ -2253,31 +2295,6 @@
taskRepository.dump(pw, innerPrefix)
}
- override fun handleKeyGestureEvent(
- event: KeyGestureEvent,
- focusedToken: IBinder?
- ): Boolean {
- if (!isKeyGestureSupported(event.keyGestureType)) return false
- when (event.keyGestureType) {
- KeyGestureEvent.KEY_GESTURE_TYPE_MOVE_TO_NEXT_DISPLAY -> {
- if (event.keycodes.contains(KeyEvent.KEYCODE_D) &&
- event.hasModifiers(KeyEvent.META_CTRL_ON or KeyEvent.META_META_ON)) {
- logV("Key gesture MOVE_TO_NEXT_DISPLAY is handled")
- getGloballyFocusedFreeformTask()?.let { moveToNextDisplay(it.taskId) }
- return true
- }
- return false
- }
- else -> return false
- }
- }
-
- override fun isKeyGestureSupported(gestureType: Int): Boolean = when (gestureType) {
- KeyGestureEvent.KEY_GESTURE_TYPE_MOVE_TO_NEXT_DISPLAY
- -> enableMoveToNextDisplayShortcut()
- else -> false
- }
-
/** The interface for calls from outside the shell, within the host process. */
@ExternalThread
private inner class DesktopModeImpl : DesktopMode {
@@ -2345,7 +2362,7 @@
}
}
- private val mTaskbarDesktopTaskListener: TaskbarDesktopTaskListener =
+ private val taskbarDesktopTaskListener: TaskbarDesktopTaskListener =
object : TaskbarDesktopTaskListener {
override fun onTaskbarCornerRoundingUpdate(
hasTasksRequiringTaskbarRounding: Boolean) {
@@ -2362,6 +2379,27 @@
}
}
+ private val desktopModeEntryExitTransitionListener: DesktopModeEntryExitTransitionListener =
+ object : DesktopModeEntryExitTransitionListener {
+ override fun onEnterDesktopModeTransitionStarted(transitionDuration: Int) {
+ ProtoLog.v(
+ WM_SHELL_DESKTOP_MODE,
+ "IDesktopModeImpl: onEnterDesktopModeTransitionStarted transitionTime=%s",
+ transitionDuration
+ )
+ remoteListener.call { l -> l.onEnterDesktopModeTransitionStarted(transitionDuration) }
+ }
+
+ override fun onExitDesktopModeTransitionStarted(transitionDuration: Int) {
+ ProtoLog.v(
+ WM_SHELL_DESKTOP_MODE,
+ "IDesktopModeImpl: onExitDesktopModeTransitionStarted transitionTime=%s",
+ transitionDuration
+ )
+ remoteListener.call { l -> l.onExitDesktopModeTransitionStarted(transitionDuration) }
+ }
+ }
+
init {
remoteListener =
SingleInstanceRemoteListener<DesktopTasksController, IDesktopTaskListener>(
@@ -2369,13 +2407,16 @@
{ c ->
run {
c.taskRepository.addVisibleTasksListener(listener, c.mainExecutor)
- c.taskbarDesktopTaskListener = mTaskbarDesktopTaskListener
+ c.taskbarDesktopTaskListener = taskbarDesktopTaskListener
+ c.desktopModeEnterExitTransitionListener =
+ desktopModeEntryExitTransitionListener
}
},
{ c ->
run {
c.taskRepository.removeVisibleTasksListener(listener)
c.taskbarDesktopTaskListener = null
+ c.desktopModeEnterExitTransitionListener = null
}
}
)
@@ -2482,6 +2523,15 @@
fun onTaskbarCornerRoundingUpdate(hasTasksRequiringTaskbarRounding: Boolean)
}
+ /** Defines interface for entering and exiting desktop windowing mode. */
+ interface DesktopModeEntryExitTransitionListener {
+ /** [transitionDuration] time it takes to run enter desktop mode transition */
+ fun onEnterDesktopModeTransitionStarted(transitionDuration: Int)
+
+ /** [transitionDuration] time it takes to run exit desktop mode transition */
+ fun onExitDesktopModeTransitionStarted(transitionDuration: Int)
+ }
+
/** The positions on a screen that a task can snap to. */
enum class SnapPosition {
RIGHT,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandler.java
index d537da8..b902bb4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandler.java
@@ -60,8 +60,7 @@
* entering and exiting freeform.
*/
public class ExitDesktopTaskTransitionHandler implements Transitions.TransitionHandler {
- @VisibleForTesting
- static final int FULLSCREEN_ANIMATION_DURATION = 336;
+ public static final int FULLSCREEN_ANIMATION_DURATION = 336;
private final Context mContext;
private final Transitions mTransitions;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopTaskListener.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopTaskListener.aidl
index c2acb87..6002a4d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopTaskListener.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopTaskListener.aidl
@@ -33,4 +33,10 @@
* [hasTasksRequiringTaskbarRounding] is true.
*/
oneway void onTaskbarCornerRoundingUpdate(boolean hasTasksRequiringTaskbarRounding);
+
+ /** Entering desktop mode transition is started, send the signal with transition duration. */
+ oneway void onEnterDesktopModeTransitionStarted(int transitionDuration);
+
+ /** Exiting desktop mode transition is started, send the signal with transition duration. */
+ oneway void onExitDesktopModeTransitionStarted(int transitionDuration);
}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/SplitDragPolicy.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/SplitDragPolicy.java
index 5d22c1e..ae9d21f62 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/SplitDragPolicy.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/SplitDragPolicy.java
@@ -41,6 +41,9 @@
import static com.android.wm.shell.draganddrop.SplitDragPolicy.Target.TYPE_SPLIT_TOP;
import static com.android.wm.shell.shared.draganddrop.DragAndDropConstants.EXTRA_DISALLOW_HIT_REGION;
import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_50_50;
+import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_INDEX_0;
+import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_INDEX_1;
+import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_INDEX_UNDEFINED;
import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED;
@@ -81,6 +84,7 @@
import com.android.wm.shell.draganddrop.anim.TwoFiftyFiftyTargetAnimator;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.shared.split.SplitScreenConstants;
+import com.android.wm.shell.shared.split.SplitScreenConstants.SplitIndex;
import com.android.wm.shell.shared.split.SplitScreenConstants.SplitPosition;
import com.android.wm.shell.splitscreen.SplitScreenController;
@@ -219,8 +223,10 @@
displayRegion.splitHorizontally(startHitRegion, endHitRegion);
}
- mTargets.add(new Target(TYPE_SPLIT_LEFT, startHitRegion, startBounds, -1));
- mTargets.add(new Target(TYPE_SPLIT_RIGHT, endHitRegion, endBounds, -1));
+ mTargets.add(new Target(TYPE_SPLIT_LEFT, startHitRegion, startBounds,
+ SPLIT_INDEX_0));
+ mTargets.add(new Target(TYPE_SPLIT_RIGHT, endHitRegion, endBounds,
+ SPLIT_INDEX_1));
} else {
// TODO(b/349828130), move this into init function and/or the insets updating
// callback
@@ -287,9 +293,10 @@
displayRegion.splitVertically(leftHitRegion, rightHitRegion);
}
- mTargets.add(new Target(TYPE_SPLIT_LEFT, leftHitRegion, topOrLeftBounds, -1));
+ mTargets.add(new Target(TYPE_SPLIT_LEFT, leftHitRegion, topOrLeftBounds,
+ SPLIT_INDEX_UNDEFINED));
mTargets.add(new Target(TYPE_SPLIT_RIGHT, rightHitRegion, bottomOrRightBounds,
- -1));
+ SPLIT_INDEX_UNDEFINED));
} else {
final Rect topHitRegion = new Rect();
final Rect bottomHitRegion = new Rect();
@@ -308,9 +315,10 @@
displayRegion.splitHorizontally(topHitRegion, bottomHitRegion);
}
- mTargets.add(new Target(TYPE_SPLIT_TOP, topHitRegion, topOrLeftBounds, -1));
+ mTargets.add(new Target(TYPE_SPLIT_TOP, topHitRegion, topOrLeftBounds,
+ SPLIT_INDEX_UNDEFINED));
mTargets.add(new Target(TYPE_SPLIT_BOTTOM, bottomHitRegion, bottomOrRightBounds,
- -1));
+ SPLIT_INDEX_UNDEFINED));
}
}
} else {
@@ -378,9 +386,9 @@
? mFullscreenStarter
: mSplitscreenStarter;
if (mSession.appData != null) {
- launchApp(mSession, starter, position, hideTaskToken);
+ launchApp(mSession, starter, position, hideTaskToken, target.index);
} else {
- launchIntent(mSession, starter, position, hideTaskToken);
+ launchIntent(mSession, starter, position, hideTaskToken, target.index);
}
if (enableFlexibleSplit()) {
@@ -392,9 +400,10 @@
* Launches an app provided by SysUI.
*/
private void launchApp(DragSession session, Starter starter, @SplitPosition int position,
- @Nullable WindowContainerToken hideTaskToken) {
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP, "Launching app data at position=%d",
- position);
+ @Nullable WindowContainerToken hideTaskToken, @SplitIndex int splitIndex) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP,
+ "Launching app data at position=%d index=%d",
+ position, splitIndex);
final ClipDescription description = session.getClipDescription();
final boolean isTask = description.hasMimeType(MIMETYPE_APPLICATION_TASK);
final boolean isShortcut = description.hasMimeType(MIMETYPE_APPLICATION_SHORTCUT);
@@ -429,7 +438,7 @@
}
}
starter.startIntent(launchIntent, user.getIdentifier(), null /* fillIntent */,
- position, opts, hideTaskToken);
+ position, opts, hideTaskToken, splitIndex);
}
}
@@ -437,7 +446,7 @@
* Launches an intent sender provided by an application.
*/
private void launchIntent(DragSession session, Starter starter, @SplitPosition int position,
- @Nullable WindowContainerToken hideTaskToken) {
+ @Nullable WindowContainerToken hideTaskToken, @SplitIndex int index) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP, "Launching intent at position=%d",
position);
final ActivityOptions baseActivityOpts = ActivityOptions.makeBasic();
@@ -452,7 +461,7 @@
final Bundle opts = baseActivityOpts.toBundle();
starter.startIntent(session.launchableIntent,
session.launchableIntent.getCreatorUserHandle().getIdentifier(),
- null /* fillIntent */, position, opts, hideTaskToken);
+ null /* fillIntent */, position, opts, hideTaskToken, index);
}
@Override
@@ -541,7 +550,7 @@
@Nullable Bundle options, UserHandle user);
void startIntent(PendingIntent intent, int userId, Intent fillInIntent,
@SplitPosition int position, @Nullable Bundle options,
- @Nullable WindowContainerToken hideTaskToken);
+ @Nullable WindowContainerToken hideTaskToken, @SplitIndex int index);
void enterSplitScreen(int taskId, boolean leftOrTop);
/**
@@ -592,7 +601,7 @@
@Override
public void startIntent(PendingIntent intent, int userId, @Nullable Intent fillInIntent,
int position, @Nullable Bundle options,
- @Nullable WindowContainerToken hideTaskToken) {
+ @Nullable WindowContainerToken hideTaskToken, @SplitIndex int index) {
if (hideTaskToken != null) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP,
"Default starter does not support hide task token");
@@ -641,13 +650,13 @@
final Rect hitRegion;
// The approximate visual region for where the task will start
final Rect drawRegion;
- int index;
+ @SplitIndex int index;
/**
* @param index 0-indexed, represents which position of drop target this object represents,
* 0 to N for left to right, top to bottom
*/
- public Target(@Type int t, Rect hit, Rect draw, int index) {
+ public Target(@Type int t, Rect hit, Rect draw, @SplitIndex int index) {
type = t;
hitRegion = hit;
drawRegion = draw;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/anim/TwoFiftyFiftyTargetAnimator.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/anim/TwoFiftyFiftyTargetAnimator.kt
index 9f532f5..5461952 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/anim/TwoFiftyFiftyTargetAnimator.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/anim/TwoFiftyFiftyTargetAnimator.kt
@@ -22,6 +22,10 @@
import com.android.wm.shell.R
import com.android.wm.shell.common.DisplayLayout
import com.android.wm.shell.draganddrop.SplitDragPolicy.Target
+import com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_INDEX_0
+import com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_INDEX_1
+import com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_INDEX_2
+import com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_INDEX_3
/**
* Represents Drop Zone targets and animations for when the system is currently in a 2 app 50/50
@@ -98,7 +102,7 @@
farStartBounds.right + halfDividerWidth,
farStartBounds.bottom
),
- farStartBounds, 0
+ farStartBounds, SPLIT_INDEX_0
)
)
targets.add(
@@ -110,7 +114,7 @@
startBounds.right + halfDividerWidth,
startBounds.bottom
),
- startBounds, 1
+ startBounds, SPLIT_INDEX_1
)
)
targets.add(
@@ -120,7 +124,7 @@
endBounds.left - halfDividerWidth,
endBounds.top, endBounds.right, endBounds.bottom
),
- endBounds, 2
+ endBounds, SPLIT_INDEX_2
)
)
targets.add(
@@ -130,7 +134,7 @@
farEndBounds.left - halfDividerWidth,
farEndBounds.top, farEndBounds.right, farEndBounds.bottom
),
- farEndBounds, 3
+ farEndBounds, SPLIT_INDEX_3
)
)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionHandler.java
index e848b88..2ae9828 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionHandler.java
@@ -254,8 +254,13 @@
finishT.hide(sc);
final Rect startBounds = new Rect(change.getStartAbsBounds());
animator.addUpdateListener(animation -> {
- t.setPosition(sc, startBounds.left,
- startBounds.top + (animation.getAnimatedFraction() * screenHeight));
+ final float newTop = startBounds.top + (animation.getAnimatedFraction() * screenHeight);
+ t.setPosition(sc, startBounds.left, newTop);
+ if (newTop > screenHeight) {
+ // At this point the task surface is off-screen, so hide it to prevent flicker
+ // failures. See b/377651666.
+ t.hide(sc);
+ }
t.apply();
});
animator.addListener(
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java
index b618bf1..e4f8333 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java
@@ -42,6 +42,7 @@
import android.os.RemoteException;
import android.util.ArrayMap;
import android.util.Log;
+import android.view.Display;
import android.view.SurfaceControl;
import android.view.WindowManager;
import android.window.IRemoteTransition;
@@ -54,6 +55,7 @@
import com.android.internal.protolog.ProtoLog;
import com.android.window.flags.Flags;
+import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.TaskStackListenerCallback;
import com.android.wm.shell.common.TaskStackListenerImpl;
@@ -80,6 +82,8 @@
private final Transitions mTransitions;
private final ShellController mShellController;
+
+ private final DisplayController mDisplayController;
private final Handler mMainHandler;
private final ShellExecutor mMainExecutor;
@@ -121,12 +125,14 @@
public KeyguardTransitionHandler(
@NonNull ShellInit shellInit,
@NonNull ShellController shellController,
+ @NonNull DisplayController displayController,
@NonNull Transitions transitions,
@NonNull TaskStackListenerImpl taskStackListener,
@NonNull Handler mainHandler,
@NonNull ShellExecutor mainExecutor) {
mTransitions = transitions;
mShellController = shellController;
+ mDisplayController = displayController;
mMainHandler = mainHandler;
mMainExecutor = mainExecutor;
mTaskStackListener = taskStackListener;
@@ -167,6 +173,10 @@
return mKeyguardShowing;
}
+ public boolean isKeyguardAnimating() {
+ return !mStartedTransitions.isEmpty();
+ }
+
@Override
public void onTaskMovedToFront(ActivityManager.RunningTaskInfo taskInfo) {
mDreamToken = taskInfo.getActivityType() == ACTIVITY_TYPE_DREAM ? taskInfo.token : null;
@@ -429,10 +439,10 @@
@Override
public void startKeyguardTransition(boolean keyguardShowing, boolean aodShowing) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
- final KeyguardState keyguardState =
- new KeyguardState.Builder(android.view.Display.DEFAULT_DISPLAY)
- .setKeyguardShowing(keyguardShowing).setAodShowing(aodShowing).build();
- wct.addKeyguardState(keyguardState);
+ for (Display display : mDisplayController.getDisplays()) {
+ wct.addKeyguardState(new KeyguardState.Builder(display.getDisplayId())
+ .setKeyguardShowing(keyguardShowing).setAodShowing(aodShowing).build());
+ }
mMainExecutor.execute(() -> {
mTransitions.startTransition(keyguardShowing ? TRANSIT_TO_FRONT : TRANSIT_TO_BACK,
wct, KeyguardTransitionHandler.this);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipEnterAnimator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipEnterAnimator.java
index eb33ff4..35c90ac 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipEnterAnimator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipEnterAnimator.java
@@ -52,6 +52,9 @@
private final SurfaceControl.Transaction mStartTransaction;
private final SurfaceControl.Transaction mFinishTransaction;
+ private final int mCornerRadius;
+ private final int mShadowRadius;
+
// Bounds updated by the evaluator as animator is running.
private final Rect mAnimatedRect = new Rect();
@@ -128,6 +131,8 @@
final int enterAnimationDuration = context.getResources()
.getInteger(R.integer.config_pipEnterAnimationDuration);
+ mCornerRadius = context.getResources().getDimensionPixelSize(R.dimen.pip_corner_radius);
+ mShadowRadius = context.getResources().getDimensionPixelSize(R.dimen.pip_shadow_radius);
setDuration(enterAnimationDuration);
setFloatValues(0f, 1f);
setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
@@ -177,6 +182,8 @@
mTransformTensor.postRotate(degrees);
tx.setMatrix(mLeash, mTransformTensor, mMatrixTmp);
+ tx.setCornerRadius(mLeash, mCornerRadius).setShadowRadius(mLeash, mShadowRadius);
+
if (mContentOverlay != null) {
mContentOverlay.onAnimationUpdate(tx, 1f / scaleX, fraction, mEndBounds);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipResizeAnimator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipResizeAnimator.java
index 4558a9f..06e8349 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipResizeAnimator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipResizeAnimator.java
@@ -29,6 +29,7 @@
import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.wm.shell.R;
import com.android.wm.shell.pip2.PipSurfaceTransactionHelper;
import com.android.wm.shell.shared.animation.Interpolators;
@@ -50,6 +51,9 @@
private Runnable mAnimationEndCallback;
private RectEvaluator mRectEvaluator;
+ private final int mCornerRadius;
+ private final int mShadowRadius;
+
// Bounds relative to which scaling/cropping must be done.
private final Rect mBaseBounds = new Rect();
@@ -74,7 +78,8 @@
mAnimationStartCallback.run();
}
if (mStartTx != null) {
- setBoundsAndRotation(mStartTx, mLeash, mBaseBounds, mStartBounds, mDelta);
+ setBoundsAndRotation(mStartTx, mLeash, mBaseBounds, mStartBounds, mDelta,
+ mCornerRadius, mShadowRadius);
mStartTx.apply();
}
}
@@ -83,7 +88,8 @@
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
if (mFinishTx != null) {
- setBoundsAndRotation(mFinishTx, mLeash, mBaseBounds, mEndBounds, 0f);
+ setBoundsAndRotation(mFinishTx, mLeash, mBaseBounds, mEndBounds, 0f,
+ mCornerRadius, mShadowRadius);
}
if (mAnimationEndCallback != null) {
mAnimationEndCallback.run();
@@ -99,7 +105,8 @@
mSurfaceControlTransactionFactory.getTransaction();
final float fraction = getAnimatedFraction();
final float degrees = (1.0f - fraction) * mDelta;
- setBoundsAndRotation(tx, mLeash, mBaseBounds, mAnimatedRect, degrees);
+ setBoundsAndRotation(tx, mLeash, mBaseBounds, mAnimatedRect, degrees,
+ mCornerRadius, mShadowRadius);
tx.apply();
}
};
@@ -128,6 +135,9 @@
mRectEvaluator = new RectEvaluator(mAnimatedRect);
+ mCornerRadius = mContext.getResources().getDimensionPixelSize(R.dimen.pip_corner_radius);
+ mShadowRadius = mContext.getResources().getDimensionPixelSize(R.dimen.pip_shadow_radius);
+
setObjectValues(startBounds, endBounds);
setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
addListener(mAnimatorListener);
@@ -152,7 +162,7 @@
* @param degrees degrees of rotation - counter-clockwise is positive by convention.
*/
private static void setBoundsAndRotation(SurfaceControl.Transaction tx, SurfaceControl leash,
- Rect baseBounds, Rect targetBounds, float degrees) {
+ Rect baseBounds, Rect targetBounds, float degrees, int cornerRadius, int shadowRadius) {
Matrix transformTensor = new Matrix();
final float[] mMatrixTmp = new float[9];
final float scaleX = (float) targetBounds.width() / baseBounds.width();
@@ -162,7 +172,9 @@
transformTensor.postTranslate(targetBounds.left, targetBounds.top);
transformTensor.postRotate(degrees, targetBounds.centerX(), targetBounds.centerY());
- tx.setMatrix(leash, transformTensor, mMatrixTmp);
+ tx.setMatrix(leash, transformTensor, mMatrixTmp)
+ .setCornerRadius(leash, cornerRadius)
+ .setShadowRadius(leash, shadowRadius);
}
@VisibleForTesting
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java
index e901c39..6d2df95 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java
@@ -16,6 +16,7 @@
package com.android.wm.shell.pip2.phone;
+import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
@@ -293,6 +294,11 @@
// Update the display layout caches even if we are not in PiP.
setDisplayLayout(mDisplayController.getDisplayLayout(displayId));
+ if (toRotation != ROTATION_UNDEFINED) {
+ // Make sure we rotate to final rotation ourselves in case display change is coming
+ // from the remote rotation as a part of an already collecting transition.
+ mPipDisplayLayoutState.rotateTo(toRotation);
+ }
if (!mPipTransitionState.isInPip()) {
// Skip the PiP-relevant updates if we aren't in a valid PiP state.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMotionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMotionHelper.java
index 3738353..fd387d1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMotionHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMotionHelper.java
@@ -785,8 +785,16 @@
private void handleFlingTransition(SurfaceControl.Transaction startTx,
SurfaceControl.Transaction finishTx, Rect destinationBounds) {
- startTx.setPosition(mPipTransitionState.getPinnedTaskLeash(),
- destinationBounds.left, destinationBounds.top);
+ SurfaceControl pipLeash = mPipTransitionState.getPinnedTaskLeash();
+ int cornerRadius = mContext.getResources().getDimensionPixelSize(R.dimen.pip_corner_radius);
+ int shadowRadius = mContext.getResources().getDimensionPixelSize(R.dimen.pip_shadow_radius);
+
+ // merge transactions so everything is done on startTx
+ startTx.merge(finishTx);
+
+ startTx.setPosition(pipLeash, destinationBounds.left, destinationBounds.top)
+ .setCornerRadius(pipLeash, cornerRadius)
+ .setShadowRadius(pipLeash, shadowRadius);
startTx.apply();
// All motion operations have actually finished, so make bounds cache updates.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
index d415c10..08e6727 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
@@ -241,7 +241,7 @@
extra.putParcelable(PIP_TASK_LEASH, pipChange.getLeash());
mPipTransitionState.setState(PipTransitionState.ENTERING_PIP, extra);
- if (mPipTransitionState.isInSwipePipToHomeTransition()) {
+ if (isInSwipePipToHomeTransition()) {
// If this is the second transition as a part of swipe PiP to home cuj,
// handle this transition as a special case with no-op animation.
return handleSwipePipToHomeTransition(info, startTransaction, finishTransaction,
@@ -702,6 +702,13 @@
@NonNull TransitionInfo.Change pipChange) {
TransitionInfo.Change fixedRotationChange = findFixedRotationChange(info);
int startRotation = pipChange.getStartRotation();
+ if (pipChange.getEndRotation() != ROTATION_UNDEFINED
+ && startRotation != pipChange.getEndRotation()) {
+ // If PiP change was collected along with the display change and the orientation change
+ // happened in sync with the PiP change, then do not treat this as fixed-rotation case.
+ return ROTATION_0;
+ }
+
int endRotation = fixedRotationChange != null
? fixedRotationChange.getEndFixedRotation() : mPipDisplayLayoutState.getRotation();
int delta = endRotation == ROTATION_UNDEFINED ? ROTATION_0
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java b/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java
index 49cf8ae..35e6c8d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java
@@ -46,6 +46,8 @@
"ShellBackPreview"),
WM_SHELL_RECENT_TASKS(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
Consts.TAG_WM_SHELL),
+ WM_SHELL_TASK_OBSERVER(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, true,
+ Consts.TAG_WM_SHELL),
// TODO(b/282232877): turn logToLogcat to false.
WM_SHELL_PICTURE_IN_PICTURE(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, true,
Consts.TAG_WM_SHELL),
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentTasksListener.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentTasksListener.aidl
index b58f068..68dc0f2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentTasksListener.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentTasksListener.aidl
@@ -45,9 +45,15 @@
*/
void onRunningTaskChanged(in RunningTaskInfo taskInfo);
- /** A task has moved to front. */
- void onTaskMovedToFront(in GroupedTaskInfo[] visibleTasks);
+ /** A task has moved to front. Only used if enableShellTopTaskTracking() is disabled. */
+ void onTaskMovedToFront(in GroupedTaskInfo taskToFront);
- /** A task info has changed. */
+ /** A task info has changed. Only used if enableShellTopTaskTracking() is disabled. */
void onTaskInfoChanged(in RunningTaskInfo taskInfo);
+
+ /**
+ * If enableShellTopTaskTracking() is enabled, this reports the set of all visible tasks.
+ * Otherwise, this reports only the new top most visible task.
+ */
+ void onVisibleTasksChanged(in GroupedTaskInfo[] visibleTasks);
}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
index 9911669..6da4f51 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
@@ -17,14 +17,18 @@
package com.android.wm.shell.recents;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.content.pm.PackageManager.FEATURE_PC;
+import static com.android.wm.shell.Flags.enableShellTopTaskTracking;
+import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TASK_OBSERVER;
import static com.android.wm.shell.shared.ShellSharedConstants.KEY_EXTRA_SHELL_RECENT_TASKS;
import android.Manifest;
import android.annotation.RequiresPermission;
import android.app.ActivityManager;
import android.app.ActivityManager.RecentTaskInfo;
+import android.app.ActivityManager.RunningTaskInfo;
import android.app.ActivityTaskManager;
import android.app.IApplicationThread;
import android.app.KeyguardManager;
@@ -65,7 +69,6 @@
import com.android.wm.shell.sysui.ShellCommandHandler;
import com.android.wm.shell.sysui.ShellController;
import com.android.wm.shell.sysui.ShellInit;
-import com.android.wm.shell.transition.Transitions;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -111,6 +114,11 @@
private final Map<Integer, SplitBounds> mTaskSplitBoundsMap = new HashMap<>();
/**
+ * Cached list of the visible tasks, sorted from top most to bottom most.
+ */
+ private final List<RunningTaskInfo> mVisibleTasks = new ArrayList<>();
+
+ /**
* Creates {@link RecentTasksController}, returns {@code null} if the feature is not
* supported.
*/
@@ -170,10 +178,8 @@
mShellCommandHandler.addDumpCallback(this::dump, this);
mTaskStackListener.addListener(this);
mDesktopRepository.ifPresent(it -> it.addActiveTaskListener(this));
- if (Transitions.ENABLE_SHELL_TRANSITIONS) {
- mTaskStackTransitionObserver.addTaskStackTransitionObserverListener(this,
- mMainExecutor);
- }
+ mTaskStackTransitionObserver.addTaskStackTransitionObserverListener(this,
+ mMainExecutor);
mContext.getSystemService(KeyguardManager.class).addKeyguardLockedStateListener(
mMainExecutor, isKeyguardLocked -> notifyRecentTasksChanged());
}
@@ -205,7 +211,7 @@
mTaskSplitBoundsMap.put(taskId1, splitBounds);
mTaskSplitBoundsMap.put(taskId2, splitBounds);
notifyRecentTasksChanged();
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENT_TASKS, "Add split pair: %d, %d, %s",
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Add split pair: %d, %d, %s",
taskId1, taskId2, splitBounds);
return true;
}
@@ -221,7 +227,7 @@
mTaskSplitBoundsMap.remove(taskId);
mTaskSplitBoundsMap.remove(pairedTaskId);
notifyRecentTasksChanged();
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENT_TASKS, "Remove split pair: %d, %d",
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Remove split pair: %d, %d",
taskId, pairedTaskId);
}
}
@@ -234,7 +240,17 @@
// We could do extra verification of requiring both taskIds of a pair and verifying that
// the same split bounds object is returned... but meh. Seems unnecessary.
- return mTaskSplitBoundsMap.get(taskId);
+ SplitBounds splitBounds = mTaskSplitBoundsMap.get(taskId);
+ if (splitBounds != null) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
+ "getSplitBoundsForTaskId: taskId=%d splitBoundsTasks=[%d, %d]", taskId,
+ splitBounds.leftTopTaskId, splitBounds.rightBottomTaskId);
+ } else {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
+ "getSplitBoundsForTaskId: expected split bounds for taskId=%d but not found",
+ taskId);
+ }
+ return splitBounds;
}
@Override
@@ -249,7 +265,10 @@
@Override
public void onTaskStackChanged() {
- notifyRecentTasksChanged();
+ if (!enableShellTopTaskTracking()) {
+ // Skip notifying recent tasks changed whenever task stack changes
+ notifyRecentTasksChanged();
+ }
}
@Override
@@ -263,15 +282,18 @@
notifyRecentTasksChanged();
}
- public void onTaskAdded(ActivityManager.RunningTaskInfo taskInfo) {
+ public void onTaskAdded(RunningTaskInfo taskInfo) {
notifyRunningTaskAppeared(taskInfo);
}
- public void onTaskRemoved(ActivityManager.RunningTaskInfo taskInfo) {
+ public void onTaskRemoved(RunningTaskInfo taskInfo) {
// Remove any split pairs associated with this task
removeSplitPair(taskInfo.taskId);
- notifyRecentTasksChanged();
notifyRunningTaskVanished(taskInfo);
+ if (!enableShellTopTaskTracking()) {
+ // Only notify recent tasks changed if we aren't already notifying the visible tasks
+ notifyRecentTasksChanged();
+ }
}
/**
@@ -279,7 +301,7 @@
*
* This currently includes windowing mode and visibility.
*/
- public void onTaskRunningInfoChanged(ActivityManager.RunningTaskInfo taskInfo) {
+ public void onTaskRunningInfoChanged(RunningTaskInfo taskInfo) {
notifyRecentTasksChanged();
notifyRunningTaskChanged(taskInfo);
}
@@ -290,14 +312,21 @@
}
@Override
+ public void onTaskMovedToFrontThroughTransition(RunningTaskInfo runningTaskInfo) {
+ notifyTaskMovedToFront(runningTaskInfo);
+ }
+
+ @Override
public void onTaskChangedThroughTransition(@NonNull ActivityManager.RunningTaskInfo taskInfo) {
notifyTaskInfoChanged(taskInfo);
}
@Override
- public void onTaskMovedToFrontThroughTransition(
- ActivityManager.RunningTaskInfo runningTaskInfo) {
- notifyTaskMovedToFront(runningTaskInfo);
+ public void onVisibleTasksChanged(@NonNull List<? extends RunningTaskInfo> visibleTasks) {
+ mVisibleTasks.clear();
+ mVisibleTasks.addAll(visibleTasks);
+ // Notify with all the info and not just the running task info
+ notifyVisibleTasksChanged(visibleTasks);
}
@VisibleForTesting
@@ -316,7 +345,7 @@
/**
* Notify the running task listener that a task appeared on desktop environment.
*/
- private void notifyRunningTaskAppeared(ActivityManager.RunningTaskInfo taskInfo) {
+ private void notifyRunningTaskAppeared(RunningTaskInfo taskInfo) {
if (mListener == null
|| !shouldEnableRunningTasksForDesktopMode()
|| taskInfo.realActivity == null) {
@@ -330,9 +359,25 @@
}
/**
+ * Notify the running task listener that a task was changed on desktop environment.
+ */
+ private void notifyRunningTaskChanged(RunningTaskInfo taskInfo) {
+ if (mListener == null
+ || !shouldEnableRunningTasksForDesktopMode()
+ || taskInfo.realActivity == null) {
+ return;
+ }
+ try {
+ mListener.onRunningTaskChanged(taskInfo);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed call onRunningTaskChanged", e);
+ }
+ }
+
+ /**
* Notify the running task listener that a task was removed on desktop environment.
*/
- private void notifyRunningTaskVanished(ActivityManager.RunningTaskInfo taskInfo) {
+ private void notifyRunningTaskVanished(RunningTaskInfo taskInfo) {
if (mListener == null
|| !shouldEnableRunningTasksForDesktopMode()
|| taskInfo.realActivity == null) {
@@ -346,25 +391,30 @@
}
/**
- * Notify the running task listener that a task was changed on desktop environment.
+ * Notify the recents task listener that a task moved to front via a transition.
*/
- private void notifyRunningTaskChanged(ActivityManager.RunningTaskInfo taskInfo) {
+ private void notifyTaskMovedToFront(ActivityManager.RunningTaskInfo taskInfo) {
if (mListener == null
- || !shouldEnableRunningTasksForDesktopMode()
- || taskInfo.realActivity == null) {
+ || !DesktopModeFlags.ENABLE_TASK_STACK_OBSERVER_IN_SHELL.isTrue()
+ || taskInfo.realActivity == null
+ || enableShellTopTaskTracking()) {
return;
}
try {
- mListener.onRunningTaskChanged(taskInfo);
+ mListener.onTaskMovedToFront(GroupedTaskInfo.forFullscreenTasks(taskInfo));
} catch (RemoteException e) {
- Slog.w(TAG, "Failed call onRunningTaskChanged", e);
+ Slog.w(TAG, "Failed call onTaskMovedToFront", e);
}
}
+ /**
+ * Notify the recents task listener that a task changed via a transition.
+ */
private void notifyTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) {
if (mListener == null
|| !DesktopModeFlags.ENABLE_TASK_STACK_OBSERVER_IN_SHELL.isTrue()
- || taskInfo.realActivity == null) {
+ || taskInfo.realActivity == null
+ || enableShellTopTaskTracking()) {
return;
}
try {
@@ -374,17 +424,21 @@
}
}
- private void notifyTaskMovedToFront(ActivityManager.RunningTaskInfo taskInfo) {
+ /**
+ * Notifies that the test of visible tasks have changed.
+ */
+ private void notifyVisibleTasksChanged(@NonNull List<? extends RunningTaskInfo> visibleTasks) {
if (mListener == null
|| !DesktopModeFlags.ENABLE_TASK_STACK_OBSERVER_IN_SHELL.isTrue()
- || taskInfo.realActivity == null) {
+ || !enableShellTopTaskTracking()) {
return;
}
try {
- GroupedTaskInfo runningTask = GroupedTaskInfo.forFullscreenTasks(taskInfo);
- mListener.onTaskMovedToFront(new GroupedTaskInfo[]{ runningTask });
+ // Compute the visible recent tasks in order, and move the task to the top
+ mListener.onVisibleTasksChanged(generateList(visibleTasks)
+ .toArray(new GroupedTaskInfo[0]));
} catch (RemoteException e) {
- Slog.w(TAG, "Failed call onTaskMovedToFront", e);
+ Slog.w(TAG, "Failed call onVisibleTasksChanged", e);
}
}
@@ -397,6 +451,11 @@
@VisibleForTesting
void registerRecentTasksListener(IRecentTasksListener listener) {
mListener = listener;
+ if (enableShellTopTaskTracking()) {
+ ProtoLog.v(WM_SHELL_TASK_OBSERVER, "registerRecentTasksListener");
+ // Post a notification for the current set of visible tasks
+ mMainExecutor.executeDelayed(() -> notifyVisibleTasksChanged(mVisibleTasks), 0);
+ }
}
@VisibleForTesting
@@ -411,14 +470,18 @@
@VisibleForTesting
ArrayList<GroupedTaskInfo> getRecentTasks(int maxNum, int flags, int userId) {
- // Note: the returned task list is from the most-recent to least-recent order
- final List<RecentTaskInfo> rawList = mActivityTaskManager.getRecentTasks(
- maxNum, flags, userId);
+ // Note: the returned task list is ordered from the most-recent to least-recent order
+ return generateList(mActivityTaskManager.getRecentTasks(maxNum, flags, userId));
+ }
+ /**
+ * Generates a list of GroupedTaskInfos for the given list of tasks.
+ */
+ private <T extends TaskInfo> ArrayList<GroupedTaskInfo> generateList(@NonNull List<T> tasks) {
// Make a mapping of task id -> task info
final SparseArray<TaskInfo> rawMapping = new SparseArray<>();
- for (int i = 0; i < rawList.size(); i++) {
- final TaskInfo taskInfo = rawList.get(i);
+ for (int i = 0; i < tasks.size(); i++) {
+ final TaskInfo taskInfo = tasks.get(i);
rawMapping.put(taskInfo.taskId, taskInfo);
}
@@ -427,10 +490,10 @@
int mostRecentFreeformTaskIndex = Integer.MAX_VALUE;
+ ArrayList<GroupedTaskInfo> groupedTasks = new ArrayList<>();
// Pull out the pairs as we iterate back in the list
- ArrayList<GroupedTaskInfo> recentTasks = new ArrayList<>();
- for (int i = 0; i < rawList.size(); i++) {
- final RecentTaskInfo taskInfo = rawList.get(i);
+ for (int i = 0; i < tasks.size(); i++) {
+ final TaskInfo taskInfo = tasks.get(i);
if (!rawMapping.contains(taskInfo.taskId)) {
// If it's not in the mapping, then it was already paired with another task
continue;
@@ -441,7 +504,7 @@
&& mDesktopRepository.get().isActiveTask(taskInfo.taskId)) {
// Freeform tasks will be added as a separate entry
if (mostRecentFreeformTaskIndex == Integer.MAX_VALUE) {
- mostRecentFreeformTaskIndex = recentTasks.size();
+ mostRecentFreeformTaskIndex = groupedTasks.size();
}
// If task has their app bounds set to null which happens after reboot, set the
// app bounds to persisted lastFullscreenBounds. Also set the position in parent
@@ -461,36 +524,34 @@
}
final int pairedTaskId = mSplitTasks.get(taskInfo.taskId, INVALID_TASK_ID);
- if (pairedTaskId != INVALID_TASK_ID && rawMapping.contains(
- pairedTaskId)) {
+ if (pairedTaskId != INVALID_TASK_ID && rawMapping.contains(pairedTaskId)) {
final TaskInfo pairedTaskInfo = rawMapping.get(pairedTaskId);
rawMapping.remove(pairedTaskId);
- recentTasks.add(GroupedTaskInfo.forSplitTasks(taskInfo, pairedTaskInfo,
+ groupedTasks.add(GroupedTaskInfo.forSplitTasks(taskInfo, pairedTaskInfo,
mTaskSplitBoundsMap.get(pairedTaskId)));
} else {
- recentTasks.add(GroupedTaskInfo.forFullscreenTasks(taskInfo));
+ // TODO(346588978): Consolidate multiple visible fullscreen tasks into the same
+ // grouped task
+ groupedTasks.add(GroupedTaskInfo.forFullscreenTasks(taskInfo));
}
}
// Add a special entry for freeform tasks
if (!freeformTasks.isEmpty()) {
- recentTasks.add(mostRecentFreeformTaskIndex,
+ groupedTasks.add(mostRecentFreeformTaskIndex,
GroupedTaskInfo.forFreeformTasks(
freeformTasks,
minimizedFreeformTasks));
}
- return recentTasks;
- }
+ if (enableShellTopTaskTracking()) {
+ // We don't current send pinned tasks as a part of recent or running tasks, so remove
+ // them from the list here
+ groupedTasks.removeIf(
+ gti -> gti.getTaskInfo1().getWindowingMode() == WINDOWING_MODE_PINNED);
+ }
- /**
- * Returns the top running leaf task.
- */
- @Nullable
- public ActivityManager.RunningTaskInfo getTopRunningTask() {
- List<ActivityManager.RunningTaskInfo> tasks = mActivityTaskManager.getTasks(1,
- false /* filterOnlyVisibleRecents */);
- return tasks.isEmpty() ? null : tasks.get(0);
+ return groupedTasks;
}
/**
@@ -498,12 +559,13 @@
* NOTE: This path currently makes assumptions that ignoreTaskToken is for the top task.
*/
@Nullable
- public ActivityManager.RunningTaskInfo getTopRunningTask(
+ public RunningTaskInfo getTopRunningTask(
@Nullable WindowContainerToken ignoreTaskToken) {
- List<ActivityManager.RunningTaskInfo> tasks = mActivityTaskManager.getTasks(2,
- false /* filterOnlyVisibleRecents */);
+ final List<RunningTaskInfo> tasks = enableShellTopTaskTracking()
+ ? mVisibleTasks
+ : mActivityTaskManager.getTasks(2, false /* filterOnlyVisibleRecents */);
for (int i = 0; i < tasks.size(); i++) {
- final ActivityManager.RunningTaskInfo task = tasks.get(i);
+ final RunningTaskInfo task = tasks.get(i);
if (task.token.equals(ignoreTaskToken)) {
continue;
}
@@ -541,7 +603,7 @@
}
/**
- * Find the background task that match the given taskId.
+ * Find the background task (in the recent tasks list) that matches the given taskId.
*/
@Nullable
public RecentTaskInfo findTaskInBackground(int taskId) {
@@ -638,29 +700,34 @@
}
@Override
- public void onRunningTaskAppeared(ActivityManager.RunningTaskInfo taskInfo) {
+ public void onRunningTaskAppeared(RunningTaskInfo taskInfo) {
mListener.call(l -> l.onRunningTaskAppeared(taskInfo));
}
@Override
- public void onRunningTaskVanished(ActivityManager.RunningTaskInfo taskInfo) {
+ public void onRunningTaskVanished(RunningTaskInfo taskInfo) {
mListener.call(l -> l.onRunningTaskVanished(taskInfo));
}
@Override
- public void onRunningTaskChanged(ActivityManager.RunningTaskInfo taskInfo) {
+ public void onRunningTaskChanged(RunningTaskInfo taskInfo) {
mListener.call(l -> l.onRunningTaskChanged(taskInfo));
}
@Override
- public void onTaskMovedToFront(GroupedTaskInfo[] taskInfo) {
- mListener.call(l -> l.onTaskMovedToFront(taskInfo));
+ public void onTaskMovedToFront(GroupedTaskInfo taskToFront) {
+ mListener.call(l -> l.onTaskMovedToFront(taskToFront));
}
@Override
public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) {
mListener.call(l -> l.onTaskInfoChanged(taskInfo));
}
+
+ @Override
+ public void onVisibleTasksChanged(GroupedTaskInfo[] visibleTasks) {
+ mListener.call(l -> l.onVisibleTasksChanged(visibleTasks));
+ }
};
public IRecentTasksImpl(RecentTasksController controller) {
@@ -714,12 +781,12 @@
}
@Override
- public ActivityManager.RunningTaskInfo[] getRunningTasks(int maxNum) {
- final ActivityManager.RunningTaskInfo[][] tasks =
- new ActivityManager.RunningTaskInfo[][]{null};
+ public RunningTaskInfo[] getRunningTasks(int maxNum) {
+ final RunningTaskInfo[][] tasks =
+ new RunningTaskInfo[][]{null};
executeRemoteCallWithTaskPermission(mController, "getRunningTasks",
(controller) -> tasks[0] = ActivityTaskManager.getInstance().getTasks(maxNum)
- .toArray(new ActivityManager.RunningTaskInfo[0]),
+ .toArray(new RunningTaskInfo[0]),
true /* blocking */);
return tasks[0];
}
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 417a655..1c58dbb 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
@@ -1308,6 +1308,9 @@
// otherwise a new transition will notify the relevant observers
if (returningToApp && allAppsAreTranslucent(mPausingTasks)) {
mHomeTransitionObserver.notifyHomeVisibilityChanged(true);
+ } else if (!toHome && mState == STATE_NEW_TASK
+ && allAppsAreTranslucent(mOpeningTasks)) {
+ // We are opening a translucent app. Launcher is still visible so we do nothing.
} else if (!toHome) {
// For some transitions, we may have notified home activity that it became
// visible. We need to notify the observer that we are no longer going home.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/TaskStackTransitionObserver.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/TaskStackTransitionObserver.kt
index d28a462..93f2e4c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/TaskStackTransitionObserver.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/TaskStackTransitionObserver.kt
@@ -17,37 +17,162 @@
package com.android.wm.shell.recents
import android.app.ActivityManager.RunningTaskInfo
+import android.app.WindowConfiguration.WINDOWING_MODE_PINNED
import android.os.IBinder
import android.util.ArrayMap
import android.view.SurfaceControl
import android.view.WindowManager.TRANSIT_CHANGE
import android.window.DesktopModeFlags
import android.window.TransitionInfo
+import com.android.internal.protolog.ProtoLog
+import com.android.wm.shell.Flags.enableShellTopTaskTracking
+import com.android.wm.shell.ShellTaskOrganizer
+import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TASK_OBSERVER
import com.android.wm.shell.shared.TransitionUtil
+import com.android.wm.shell.sysui.ShellCommandHandler
import com.android.wm.shell.sysui.ShellInit
import com.android.wm.shell.transition.Transitions
import dagger.Lazy
+import java.io.PrintWriter
+import java.util.StringJoiner
import java.util.concurrent.Executor
/**
- * A [Transitions.TransitionObserver] that observes shell transitions and sends updates to listeners
- * about task stack changes.
+ * A [Transitions.TransitionObserver] that observes shell transitions, tracks the visible tasks
+ * and notifies listeners whenever the visible tasks change (at the start and end of a transition).
*
- * TODO(346588978) Move split/pip signals here as well so that launcher don't need to handle it
+ * This can be replaced once we have a generalized task repository tracking visible tasks.
*/
class TaskStackTransitionObserver(
+ shellInit: ShellInit,
+ private val shellTaskOrganizer: Lazy<ShellTaskOrganizer>,
+ private val shellCommandHandler: ShellCommandHandler,
private val transitions: Lazy<Transitions>,
- shellInit: ShellInit
-) : Transitions.TransitionObserver {
+) : Transitions.TransitionObserver, ShellTaskOrganizer.TaskVanishedListener {
+
+ // List of currently visible tasks sorted in z-order from top-most to bottom-most, only used
+ // when Flags.enableShellTopTaskTracking() is enabled.
+ private var visibleTasks: MutableList<RunningTaskInfo> = mutableListOf()
+ private val pendingCloseTasks: MutableList<RunningTaskInfo> = mutableListOf()
+ // Set of listeners to notify when the visible tasks change
private val taskStackTransitionObserverListeners =
ArrayMap<TaskStackTransitionObserverListener, Executor>()
+ // Used to filter out leaf-tasks
+ private val leafTaskFilter: TransitionUtil.LeafTaskFilter = TransitionUtil.LeafTaskFilter()
init {
shellInit.addInitCallback(::onInit, this)
}
fun onInit() {
+ shellTaskOrganizer.get().addTaskVanishedListener(this)
+ shellCommandHandler.addDumpCallback(::dump, this)
transitions.get().registerObserver(this)
+
+ // TODO(346588978): We need to update the running tasks once the ShellTaskOrganizer is
+ // registered since there is no existing transition (yet) corresponding for the already
+ // visible tasks
+ }
+
+ /**
+ * This method handles transition ready when only
+ * DesktopModeFlags.ENABLE_TASK_STACK_OBSERVER_IN_SHELL is set.
+ */
+ private fun onDesktopOnlyFlagTransitionReady(info: TransitionInfo) {
+ for (change in info.changes) {
+ if (change.flags and TransitionInfo.FLAG_IS_WALLPAPER != 0) {
+ continue
+ }
+
+ val taskInfo = change.taskInfo
+ if (taskInfo == null || taskInfo.taskId == -1) {
+ continue
+ }
+
+ // Find the first task that is opening, this should be the one at the front after
+ // the transition
+ if (TransitionUtil.isOpeningType(change.mode)) {
+ notifyOnTaskMovedToFront(taskInfo)
+ break
+ } else if (change.mode == TRANSIT_CHANGE) {
+ notifyOnTaskChanged(taskInfo)
+ }
+ }
+ }
+
+ /**
+ * This method handles transition ready when Flags.enableShellTopTaskTracking() is set.
+ */
+ private fun onShellTopTaskTrackerFlagTransitionReady(info: TransitionInfo) {
+ ProtoLog.v(WM_SHELL_TASK_OBSERVER, "Transition ready: %d", info.debugId)
+
+ // Filter out non-leaf tasks (we will likely need them later, but visible task tracking
+ // is currently used only for visible leaf tasks)
+ val changesReversed = mutableListOf<TransitionInfo.Change>()
+ for (change in info.changes) {
+ if (!leafTaskFilter.test(change)) {
+ // Not a leaf task
+ continue
+ }
+ changesReversed.add(0, change)
+ }
+
+ // We iterate the change list in reverse order because changes are sorted top to bottom and
+ // we want to update the lists such that the top most tasks are inserted at the front last
+ var notifyChanges = false
+ for (change in changesReversed) {
+ val taskInfo = change.taskInfo
+ if (taskInfo == null || taskInfo.taskId == -1) {
+ // Not a valid task
+ continue
+ }
+
+ if (TransitionUtil.isClosingMode(change.mode)) {
+ ProtoLog.v(WM_SHELL_TASK_OBSERVER, "\tClosing task=%d", taskInfo.taskId)
+
+ // Closing task's visibilities are not committed until after the transition
+ // completes, so track such tasks so that we can notify on finish
+ if (!pendingCloseTasks.any { it.taskId == taskInfo.taskId }) {
+ pendingCloseTasks.add(taskInfo)
+ }
+ } else if (TransitionUtil.isOpeningMode(change.mode)
+ || TransitionUtil.isOrderOnly(change)) {
+ ProtoLog.v(WM_SHELL_TASK_OBSERVER, "\tOpening task=%d", taskInfo.taskId)
+
+ // Remove from pending close tasks list if it's being opened again
+ pendingCloseTasks.removeIf { it.taskId == taskInfo.taskId }
+ // Move the task to the front of the visible tasks list
+ visibleTasks.removeIf { it.taskId == taskInfo.taskId }
+ visibleTasks.add(0, taskInfo)
+ notifyChanges = true
+ }
+ }
+
+ // TODO(346588978): We should verify the task list has actually changed before notifying
+ // (ie. starting an activity that's already top-most would result in no visible change)
+ if (notifyChanges) {
+ updateVisibleTasksList("transition-start")
+ }
+ }
+
+ private fun updateVisibleTasksList(reason: String) {
+ // This simply constructs a list of visible tasks, where the always-on-top tasks are moved
+ // to the front of the list in-order, to ensure that they match the visible z order
+ val orderedVisibleTasks = mutableListOf<RunningTaskInfo>()
+ var numAlwaysOnTop = 0
+ for (info in visibleTasks) {
+ if (info.windowingMode == WINDOWING_MODE_PINNED
+ || info.configuration.windowConfiguration.isAlwaysOnTop) {
+ orderedVisibleTasks.add(numAlwaysOnTop, info)
+ numAlwaysOnTop++
+ } else {
+ orderedVisibleTasks.add(info)
+ }
+ }
+ visibleTasks = orderedVisibleTasks
+
+ dumpVisibleTasks(reason)
+ notifyVisibleTasksChanged(visibleTasks)
}
override fun onTransitionReady(
@@ -56,26 +181,10 @@
startTransaction: SurfaceControl.Transaction,
finishTransaction: SurfaceControl.Transaction
) {
- if (DesktopModeFlags.ENABLE_TASK_STACK_OBSERVER_IN_SHELL.isTrue) {
- for (change in info.changes) {
- if (change.flags and TransitionInfo.FLAG_IS_WALLPAPER != 0) {
- continue
- }
-
- val taskInfo = change.taskInfo
- if (taskInfo == null || taskInfo.taskId == -1) {
- continue
- }
-
- // Find the first task that is opening, this should be the one at the front after
- // the transition
- if (TransitionUtil.isOpeningType(change.mode)) {
- notifyOnTaskMovedToFront(taskInfo)
- break
- } else if (change.mode == TRANSIT_CHANGE) {
- notifyOnTaskChanged(taskInfo)
- }
- }
+ if (enableShellTopTaskTracking()) {
+ onShellTopTaskTrackerFlagTransitionReady(info)
+ } else if (DesktopModeFlags.ENABLE_TASK_STACK_OBSERVER_IN_SHELL.isTrue) {
+ onDesktopOnlyFlagTransitionReady(info)
}
}
@@ -83,8 +192,35 @@
override fun onTransitionMerged(merged: IBinder, playing: IBinder) {}
- override fun onTransitionFinished(transition: IBinder, aborted: Boolean) {}
+ override fun onTransitionFinished(transition: IBinder, aborted: Boolean) {
+ if (enableShellTopTaskTracking()) {
+ if (pendingCloseTasks.isNotEmpty()) {
+ // Update the visible task list based on the pending close tasks
+ for (change in pendingCloseTasks) {
+ visibleTasks.removeIf {
+ it.taskId == change.taskId
+ }
+ }
+ updateVisibleTasksList("transition-finished")
+ }
+ }
+ }
+ override fun onTaskVanished(taskInfo: RunningTaskInfo?) {
+ if (!enableShellTopTaskTracking()) {
+ return
+ }
+ ProtoLog.v(WM_SHELL_TASK_OBSERVER, "Task vanished: task=%d", taskInfo?.taskId)
+ pendingCloseTasks.removeIf { it.taskId == taskInfo?.taskId }
+ if (visibleTasks.any { it.taskId == taskInfo?.taskId }) {
+ visibleTasks.removeIf { it.taskId == taskInfo?.taskId }
+ updateVisibleTasksList("task-vanished")
+ }
+ }
+
+ /**
+ * Adds a new task stack observer.
+ */
fun addTaskStackTransitionObserverListener(
taskStackTransitionObserverListener: TaskStackTransitionObserverListener,
executor: Executor
@@ -92,6 +228,9 @@
taskStackTransitionObserverListeners[taskStackTransitionObserverListener] = executor
}
+ /**
+ * Removes an existing task stack observer.
+ */
fun removeTaskStackTransitionObserverListener(
taskStackTransitionObserverListener: TaskStackTransitionObserverListener
) {
@@ -99,22 +238,66 @@
}
private fun notifyOnTaskMovedToFront(taskInfo: RunningTaskInfo) {
+ if (enableShellTopTaskTracking()) {
+ return
+ }
taskStackTransitionObserverListeners.forEach { (listener, executor) ->
executor.execute { listener.onTaskMovedToFrontThroughTransition(taskInfo) }
}
}
private fun notifyOnTaskChanged(taskInfo: RunningTaskInfo) {
+ if (enableShellTopTaskTracking()) {
+ return
+ }
taskStackTransitionObserverListeners.forEach { (listener, executor) ->
executor.execute { listener.onTaskChangedThroughTransition(taskInfo) }
}
}
+ private fun notifyVisibleTasksChanged(visibleTasks: List<RunningTaskInfo>) {
+ taskStackTransitionObserverListeners.forEach { (listener, executor) ->
+ executor.execute { listener.onVisibleTasksChanged(visibleTasks) }
+ }
+ }
+
+ fun dump(pw: PrintWriter, prefix: String) {
+ pw.println("${prefix}$TAG:")
+
+ if (visibleTasks.isEmpty()) {
+ pw.println("$prefix visibleTasks=[]")
+ } else {
+ val stringJoiner = StringJoiner(",\n\t", "[\n\t", "\n]")
+ visibleTasks.forEach {
+ stringJoiner.add("id=${it.taskId} cmp=${it.baseIntent.component}")
+ }
+ pw.println("$prefix visibleTasks=$stringJoiner")
+ }
+ }
+
+ /** Dumps the set of visible tasks to protolog */
+ private fun dumpVisibleTasks(reason: String) {
+ if (!WM_SHELL_TASK_OBSERVER.isEnabled) {
+ return
+ }
+ ProtoLog.v(WM_SHELL_TASK_OBSERVER, "\tVisible tasks (%s)", reason)
+ for (task in visibleTasks) {
+ ProtoLog.v(WM_SHELL_TASK_OBSERVER, "\t\ttaskId=%d package=%s", task.taskId,
+ task.baseIntent.component?.packageName)
+ }
+ }
+
/** Listener to use to get updates regarding task stack from this observer */
interface TaskStackTransitionObserverListener {
/** Called when a task is moved to front. */
fun onTaskMovedToFrontThroughTransition(taskInfo: RunningTaskInfo) {}
+ /** Called when the set of visible tasks have changed. */
+ fun onVisibleTasksChanged(visibleTasks: List<RunningTaskInfo>) {}
/** Called when a task info has changed. */
fun onTaskChangedThroughTransition(taskInfo: RunningTaskInfo) {}
}
+
+ companion object {
+ const val TAG = "TaskStackTransitionObserver"
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
index 3e6d36c..39ed9ab 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
@@ -54,10 +54,27 @@
*/
int STAGE_TYPE_SIDE = 1;
+ /**
+ * Position independent stage identifier for a given Stage
+ */
+ int STAGE_TYPE_A = 2;
+ /**
+ * Position independent stage identifier for a given Stage
+ */
+ int STAGE_TYPE_B = 3;
+ /**
+ * Position independent stage identifier for a given Stage
+ */
+ int STAGE_TYPE_C = 4;
+
@IntDef(prefix = { "STAGE_TYPE_" }, value = {
STAGE_TYPE_UNDEFINED,
STAGE_TYPE_MAIN,
- STAGE_TYPE_SIDE
+ STAGE_TYPE_SIDE,
+ // Used for flexible split
+ STAGE_TYPE_A,
+ STAGE_TYPE_B,
+ STAGE_TYPE_C
})
@interface StageType {}
@@ -128,6 +145,9 @@
case STAGE_TYPE_UNDEFINED: return "UNDEFINED";
case STAGE_TYPE_MAIN: return "MAIN";
case STAGE_TYPE_SIDE: return "SIDE";
+ case STAGE_TYPE_A: return "STAGE_A";
+ case STAGE_TYPE_B: return "STAGE_B";
+ case STAGE_TYPE_C: return "STAGE_C";
default: return "UNKNOWN(" + stage + ")";
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index 6398d31..4f0f676 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -24,6 +24,7 @@
import static android.content.Intent.FLAG_ACTIVITY_NO_USER_ACTION;
import static android.view.Display.DEFAULT_DISPLAY;
+import static com.android.wm.shell.Flags.enableFlexibleSplit;
import static com.android.wm.shell.common.MultiInstanceHelper.getComponent;
import static com.android.wm.shell.common.MultiInstanceHelper.getShortcutComponent;
import static com.android.wm.shell.common.MultiInstanceHelper.samePackage;
@@ -33,6 +34,9 @@
import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN;
import static com.android.wm.shell.shared.ShellSharedConstants.KEY_EXTRA_SHELL_SPLIT_SCREEN;
import static com.android.wm.shell.shared.split.SplitScreenConstants.KEY_EXTRA_WIDGET_INTENT;
+import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_INDEX_0;
+import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_INDEX_1;
+import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_INDEX_UNDEFINED;
import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED;
@@ -98,6 +102,7 @@
import com.android.wm.shell.shared.TransactionPool;
import com.android.wm.shell.shared.annotations.ExternalThread;
import com.android.wm.shell.shared.split.SplitScreenConstants.PersistentSnapPosition;
+import com.android.wm.shell.shared.split.SplitScreenConstants.SplitIndex;
import com.android.wm.shell.shared.split.SplitScreenConstants.SplitPosition;
import com.android.wm.shell.splitscreen.SplitScreen.StageType;
import com.android.wm.shell.sysui.KeyguardChangeListener;
@@ -325,7 +330,6 @@
/**
* @return an Array of RunningTaskInfo's ordered by leftToRight or topTopBottom
*/
- @Nullable
public ActivityManager.RunningTaskInfo[] getAllTaskInfos() {
// TODO(b/349828130) Add the third stage task info and not rely on positions
ActivityManager.RunningTaskInfo topLeftTask = getTaskInfo(SPLIT_POSITION_TOP_OR_LEFT);
@@ -335,7 +339,7 @@
return new ActivityManager.RunningTaskInfo[]{topLeftTask, bottomRightTask};
}
- return null;
+ return new ActivityManager.RunningTaskInfo[0];
}
/** Check task is under split or not by taskId. */
@@ -405,7 +409,7 @@
public void prepareEnterSplitScreen(WindowContainerTransaction wct,
ActivityManager.RunningTaskInfo taskInfo, int startPosition) {
mStageCoordinator.prepareEnterSplitScreen(wct, taskInfo, startPosition,
- false /* resizeAnim */);
+ false /* resizeAnim */, SPLIT_INDEX_UNDEFINED);
}
/**
@@ -451,6 +455,24 @@
}
}
+ /**
+ * Determines which split index a new instance of a task should take.
+ * @param callingTask The task requesting a new instance.
+ * @return the split index of the new instance
+ */
+ @SplitIndex
+ public int determineNewInstanceIndex(@NonNull ActivityManager.RunningTaskInfo callingTask) {
+ if (!enableFlexibleSplit()) {
+ throw new IllegalStateException("Use determineNewInstancePosition");
+ }
+ if (callingTask.getWindowingMode() == WINDOWING_MODE_FULLSCREEN
+ || getSplitPosition(callingTask.taskId) == SPLIT_POSITION_TOP_OR_LEFT) {
+ return SPLIT_INDEX_1;
+ } else {
+ return SPLIT_INDEX_0;
+ }
+ }
+
public void enterSplitScreen(int taskId, boolean leftOrTop) {
enterSplitScreen(taskId, leftOrTop, new WindowContainerTransaction());
}
@@ -685,7 +707,10 @@
ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "startIntentWithInstanceId: reason=%d",
ENTER_REASON_LAUNCHER);
mStageCoordinator.getLogger().enterRequested(instanceId, ENTER_REASON_LAUNCHER);
- startIntent(intent, userId, fillInIntent, position, options, null /* hideTaskToken */);
+ // TODO(b/349828130) currently pass in index_undefined until we can revisit these
+ // specific cases in the future. Only focusing on parity with starting intent/task
+ startIntent(intent, userId, fillInIntent, position, options, null /* hideTaskToken */,
+ SPLIT_INDEX_UNDEFINED);
}
private void startIntentAndTask(PendingIntent pendingIntent, int userId1,
@@ -775,9 +800,9 @@
@Override
public void startIntent(PendingIntent intent, int userId1, @Nullable Intent fillInIntent,
@SplitPosition int position, @Nullable Bundle options,
- @Nullable WindowContainerToken hideTaskToken) {
+ @Nullable WindowContainerToken hideTaskToken, @SplitIndex int index) {
startIntent(intent, userId1, fillInIntent, position, options, hideTaskToken,
- false /* forceLaunchNewTask */);
+ false /* forceLaunchNewTask */, index);
}
/**
@@ -790,7 +815,8 @@
*/
public void startIntent(PendingIntent intent, int userId1, @Nullable Intent fillInIntent,
@SplitPosition int position, @Nullable Bundle options,
- @Nullable WindowContainerToken hideTaskToken, boolean forceLaunchNewTask) {
+ @Nullable WindowContainerToken hideTaskToken, boolean forceLaunchNewTask,
+ @SplitIndex int index) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
"startIntent(): intent=%s user=%d fillInIntent=%s position=%d", intent, userId1,
fillInIntent, position);
@@ -816,7 +842,7 @@
if (taskInfo != null) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
"Found suitable background task=%s", taskInfo);
- mStageCoordinator.startTask(taskInfo.taskId, position, options, hideTaskToken);
+ mStageCoordinator.startTask(taskInfo.taskId, position, options, hideTaskToken, index);
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Start task in background");
return;
@@ -841,7 +867,8 @@
}
}
- mStageCoordinator.startIntent(intent, fillInIntent, position, options, hideTaskToken);
+ mStageCoordinator.startIntent(intent, fillInIntent, position, options, hideTaskToken,
+ index);
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
index 84004941..3091be5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
@@ -21,6 +21,7 @@
import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_TO_BACK;
+import static com.android.wm.shell.Flags.enableFlexibleSplit;
import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN;
import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TRANSITIONS;
import static com.android.wm.shell.shared.animation.Interpolators.ALPHA_IN;
@@ -55,6 +56,9 @@
import com.android.wm.shell.transition.Transitions;
import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
import java.util.concurrent.Executor;
/** Manages transition animations for split-screen. */
@@ -268,22 +272,21 @@
@NonNull SurfaceControl.Transaction startTransaction,
@NonNull SurfaceControl.Transaction finishTransaction,
@NonNull Transitions.TransitionFinishCallback finishCallback,
- @NonNull WindowContainerToken mainRoot, @NonNull WindowContainerToken sideRoot,
- @NonNull SplitDecorManager mainDecor, @NonNull SplitDecorManager sideDecor) {
+ @NonNull Map<WindowContainerToken, SplitDecorManager> rootDecorMap) {
ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "playResizeAnimation: transition=%d", info.getDebugId());
initTransition(transition, finishTransaction, finishCallback);
+ Set<WindowContainerToken> rootDecorKeys = rootDecorMap.keySet();
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
final TransitionInfo.Change change = info.getChanges().get(i);
- if (mainRoot.equals(change.getContainer()) || sideRoot.equals(change.getContainer())) {
+ if (rootDecorKeys.contains(change.getContainer())) {
final SurfaceControl leash = change.getLeash();
startTransaction.setPosition(leash, change.getEndAbsBounds().left,
change.getEndAbsBounds().top);
startTransaction.setWindowCrop(leash, change.getEndAbsBounds().width(),
change.getEndAbsBounds().height());
- SplitDecorManager decor = mainRoot.equals(change.getContainer())
- ? mainDecor : sideDecor;
+ SplitDecorManager decor = rootDecorMap.get(change.getContainer());
// This is to ensure onFinished be called after all animations ended.
ValueAnimator va = new ValueAnimator();
@@ -433,15 +436,22 @@
Transitions.TransitionHandler handler,
@Nullable TransitionConsumedCallback consumedCallback,
@Nullable TransitionFinishedCallback finishCallback,
- @NonNull SplitDecorManager mainDecor, @NonNull SplitDecorManager sideDecor) {
+ @Nullable SplitDecorManager mainDecor, @Nullable SplitDecorManager sideDecor,
+ @Nullable List<SplitDecorManager> decorManagers) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
" splitTransition deduced Resize split screen.");
ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "setResizeTransition: hasPendingResize=%b",
mPendingResize != null);
if (mPendingResize != null) {
mPendingResize.cancel(null);
- mainDecor.cancelRunningAnimations();
- sideDecor.cancelRunningAnimations();
+ if (enableFlexibleSplit()) {
+ for (SplitDecorManager stage : decorManagers) {
+ stage.cancelRunningAnimations();
+ }
+ } else {
+ mainDecor.cancelRunningAnimations();
+ sideDecor.cancelRunningAnimations();
+ }
mAnimations.clear();
onFinish(null /* wct */);
}
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 cc0e1df..45ecfa9 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
@@ -33,6 +33,7 @@
import static android.window.TransitionInfo.FLAG_IS_DISPLAY;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER;
+import static com.android.wm.shell.Flags.enableFlexibleSplit;
import static com.android.wm.shell.common.split.SplitLayout.PARALLAX_ALIGN_CENTER;
import static com.android.wm.shell.common.split.SplitScreenUtils.isPartiallyOffscreen;
import static com.android.wm.shell.common.split.SplitScreenUtils.reverseSplitPosition;
@@ -43,6 +44,7 @@
import static com.android.wm.shell.shared.TransitionUtil.isOrderOnly;
import static com.android.wm.shell.shared.split.SplitScreenConstants.FLAG_IS_DIVIDER_BAR;
import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_10_90;
+import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_50_50;
import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_90_10;
import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_INDEX_0;
import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_INDEX_1;
@@ -51,11 +53,13 @@
import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED;
import static com.android.wm.shell.shared.split.SplitScreenConstants.splitPositionToString;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_A;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_B;
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_MAIN;
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_SIDE;
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
+import static com.android.wm.shell.splitscreen.SplitScreen.stageTypeToString;
import static com.android.wm.shell.splitscreen.SplitScreenController.ENTER_REASON_LAUNCHER;
-import static com.android.wm.shell.splitscreen.SplitScreenController.ENTER_REASON_MULTI_INSTANCE;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_APP_FINISHED;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_CHILD_TASK_ENTER_PIP;
@@ -143,6 +147,7 @@
import com.android.wm.shell.shared.TransitionUtil;
import com.android.wm.shell.shared.split.SplitBounds;
import com.android.wm.shell.shared.split.SplitScreenConstants.PersistentSnapPosition;
+import com.android.wm.shell.shared.split.SplitScreenConstants.SplitIndex;
import com.android.wm.shell.shared.split.SplitScreenConstants.SplitPosition;
import com.android.wm.shell.splitscreen.SplitScreen.StageType;
import com.android.wm.shell.splitscreen.SplitScreenController.ExitReason;
@@ -154,11 +159,15 @@
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
+import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.Executor;
+import java.util.function.Consumer;
+import java.util.function.Predicate;
/**
* Coordinates the staging (visibility, sizing, ...) of the split-screen stages.
@@ -179,10 +188,11 @@
// entered
private static final int DISABLE_LAUNCH_ADJACENT_AFTER_ENTER_TIMEOUT_MS = 1000;
- private final StageTaskListener mMainStage;
- private final StageTaskListener mSideStage;
+ private StageTaskListener mMainStage;
+ private StageTaskListener mSideStage;
@SplitPosition
private int mSideStagePosition = SPLIT_POSITION_BOTTOM_OR_RIGHT;
+ private StageOrderOperator mStageOrderOperator;
private final int mDisplayId;
private SplitLayout mSplitLayout;
@@ -336,22 +346,32 @@
taskOrganizer.createRootTask(displayId, WINDOWING_MODE_FULLSCREEN, this /* listener */);
ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "Creating main/side root task");
- mMainStage = new StageTaskListener(
- mContext,
- mTaskOrganizer,
- mDisplayId,
- this /*stageListenerCallbacks*/,
- mSyncQueue,
- iconProvider,
- mWindowDecorViewModel);
- mSideStage = new StageTaskListener(
- mContext,
- mTaskOrganizer,
- mDisplayId,
- this /*stageListenerCallbacks*/,
- mSyncQueue,
- iconProvider,
- mWindowDecorViewModel);
+ if (enableFlexibleSplit()) {
+ mStageOrderOperator = new StageOrderOperator(mContext,
+ mTaskOrganizer,
+ mDisplayId,
+ this /*stageListenerCallbacks*/,
+ mSyncQueue,
+ iconProvider,
+ mWindowDecorViewModel);
+ } else {
+ mMainStage = new StageTaskListener(
+ mContext,
+ mTaskOrganizer,
+ mDisplayId,
+ this /*stageListenerCallbacks*/,
+ mSyncQueue,
+ iconProvider,
+ mWindowDecorViewModel, STAGE_TYPE_MAIN);
+ mSideStage = new StageTaskListener(
+ mContext,
+ mTaskOrganizer,
+ mDisplayId,
+ this /*stageListenerCallbacks*/,
+ mSyncQueue,
+ iconProvider,
+ mWindowDecorViewModel, STAGE_TYPE_SIDE);
+ }
mDisplayController = displayController;
mDisplayImeController = displayImeController;
mDisplayInsetsController = displayInsetsController;
@@ -423,24 +443,63 @@
}
public boolean isSplitScreenVisible() {
- return mSideStage.mVisible && mMainStage.mVisible;
+ if (enableFlexibleSplit()) {
+ return runForActiveStagesAllMatch((stage) -> stage.mVisible);
+ } else {
+ return mSideStage.mVisible && mMainStage.mVisible;
+ }
}
- private void activateSplit(WindowContainerTransaction wct, boolean includingTopTask) {
- mMainStage.activate(wct, includingTopTask);
+ /**
+ * @param includingTopTask reparents the current top task into the stage defined by index
+ * (or mainStage in legacy split)
+ * @param index the index to move the current visible task into, if undefined will arbitrarily
+ * choose a stage to launch into
+ */
+ private void activateSplit(WindowContainerTransaction wct, boolean includingTopTask,
+ int index) {
+ if (enableFlexibleSplit()) {
+ mStageOrderOperator.onEnteringSplit(SNAP_TO_2_50_50);
+ if (index == SPLIT_INDEX_UNDEFINED || !includingTopTask) {
+ // If we aren't includingTopTask, then the call to activate on the stage is
+ // effectively a no-op. Previously the stage kept track of the "isActive" state,
+ // but now that gets set in the "onEnteringSplit" call above.
+ //
+ // index == UNDEFINED case might change, but as of now no use case where we activate
+ // without an index specified.
+ return;
+ }
+ @SplitIndex int oppositeIndex = index == SPLIT_INDEX_0 ? SPLIT_INDEX_1 : SPLIT_INDEX_0;
+ StageTaskListener activatingStage = mStageOrderOperator.getStageForIndex(oppositeIndex);
+ activatingStage.activate(wct, includingTopTask);
+ } else {
+ mMainStage.activate(wct, includingTopTask);
+ }
}
public boolean isSplitActive() {
- return mMainStage.isActive();
+ if (enableFlexibleSplit()) {
+ return mStageOrderOperator.isActive();
+ } else {
+ return mMainStage.isActive();
+ }
}
/**
* Deactivates main stage by removing the stage from the top level split root (usually when a
* task underneath gets removed from the stage root).
- * @param reparentToTop whether we want to put the stage root back on top
+ * @param stageToTop stage which we want to put on top
*/
- private void deactivateSplit(WindowContainerTransaction wct, boolean reparentToTop) {
- mMainStage.deactivate(wct, reparentToTop);
+ private void deactivateSplit(WindowContainerTransaction wct, @StageType int stageToTop) {
+ if (enableFlexibleSplit()) {
+ StageTaskListener stageToDeactivate = mStageOrderOperator.getAllStages().stream()
+ .filter(stage -> stage.getId() == stageToTop)
+ .findFirst().orElseThrow();
+ stageToDeactivate.deactivate(wct, true /*toTop*/);
+ mStageOrderOperator.onExitingSplit();
+ } else {
+ mMainStage.deactivate(wct, stageToTop == STAGE_TYPE_MAIN);
+ }
}
/** @return whether this transition-request has the launch-adjacent flag. */
@@ -464,11 +523,12 @@
// If one of the splitting tasks support auto-pip, wm-core might reparent the task to TDA
// and file a TRANSIT_PIP transition when finishing transitions.
// @see com.android.server.wm.RootWindowContainer#moveActivityToPinnedRootTask
- if (mMainStage.getChildCount() == 0 || mSideStage.getChildCount() == 0) {
- return true;
+ if (enableFlexibleSplit()) {
+ return mStageOrderOperator.getActiveStages().stream()
+ .anyMatch(stage -> stage.getChildCount() == 0);
+ } else {
+ return mMainStage.getChildCount() == 0 || mSideStage.getChildCount() == 0;
}
-
- return false;
}
/** Checks if `transition` is a pending enter-split transition. */
@@ -478,10 +538,19 @@
@StageType
int getStageOfTask(int taskId) {
- if (mMainStage.containsTask(taskId)) {
- return STAGE_TYPE_MAIN;
- } else if (mSideStage.containsTask(taskId)) {
- return STAGE_TYPE_SIDE;
+ if (enableFlexibleSplit()) {
+ StageTaskListener stageTaskListener = mStageOrderOperator.getActiveStages().stream()
+ .filter(stage -> stage.containsTask(taskId))
+ .findFirst().orElse(null);
+ if (stageTaskListener != null) {
+ return stageTaskListener.getId();
+ }
+ } else {
+ if (mMainStage.containsTask(taskId)) {
+ return STAGE_TYPE_MAIN;
+ } else if (mSideStage.containsTask(taskId)) {
+ return STAGE_TYPE_SIDE;
+ }
}
return STAGE_TYPE_UNDEFINED;
@@ -491,14 +560,22 @@
if (mRootTaskInfo != null && mRootTaskInfo.taskId == taskId) {
return true;
}
- return mMainStage.isRootTaskId(taskId) || mSideStage.isRootTaskId(taskId);
+ if (enableFlexibleSplit()) {
+ return mStageOrderOperator.getActiveStages().stream()
+ .anyMatch((stage) -> stage.isRootTaskId(taskId));
+ } else {
+ return mMainStage.isRootTaskId(taskId) || mSideStage.isRootTaskId(taskId);
+ }
}
boolean moveToStage(ActivityManager.RunningTaskInfo task, @SplitPosition int stagePosition,
WindowContainerTransaction wct) {
ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "moveToStage: task=%d position=%d", task.taskId,
stagePosition);
- prepareEnterSplitScreen(wct, task, stagePosition, false /* resizeAnim */);
+ // TODO(b/349828130) currently pass in index_undefined until we can revisit these
+ // specific cases in the future. Only focusing on parity with starting intent/task
+ prepareEnterSplitScreen(wct, task, stagePosition, false /* resizeAnim */,
+ SPLIT_INDEX_UNDEFINED);
mSplitTransitions.startEnterTransition(TRANSIT_TO_FRONT, wct,
null, this,
isSplitScreenVisible()
@@ -596,11 +673,14 @@
* same window container transaction as the starting of the intent.
*/
void startTask(int taskId, @SplitPosition int position, @Nullable Bundle options,
- @Nullable WindowContainerToken hideTaskToken) {
- ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "startTask: task=%d position=%d", taskId, position);
+ @Nullable WindowContainerToken hideTaskToken, @SplitIndex int index) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "startTask: task=%d position=%d index=%d",
+ taskId, position, index);
mSplitRequest = new SplitRequest(taskId, position);
final WindowContainerTransaction wct = new WindowContainerTransaction();
- options = resolveStartStage(STAGE_TYPE_UNDEFINED, position, options, null /* wct */);
+ options = enableFlexibleSplit()
+ ? resolveStartStageForIndex(options, null /*wct*/, index)
+ : resolveStartStage(STAGE_TYPE_UNDEFINED, position, options, null /* wct */);
if (hideTaskToken != null) {
ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "Reordering hide-task to bottom");
wct.reorder(hideTaskToken, false /* onTop */);
@@ -624,7 +704,7 @@
// If split screen is not activated, we're expecting to open a pair of apps to split.
final int extraTransitType = isSplitActive()
? TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE : TRANSIT_SPLIT_SCREEN_PAIR_OPEN;
- prepareEnterSplitScreen(wct, null /* taskInfo */, position, !mIsDropEntering);
+ prepareEnterSplitScreen(wct, null /* taskInfo */, position, !mIsDropEntering, index);
mSplitTransitions.startEnterTransition(TRANSIT_TO_FRONT, wct, null, this,
extraTransitType, !mIsDropEntering);
@@ -636,13 +716,16 @@
* same window container transaction as the starting of the intent.
*/
void startIntent(PendingIntent intent, Intent fillInIntent, @SplitPosition int position,
- @Nullable Bundle options, @Nullable WindowContainerToken hideTaskToken) {
+ @Nullable Bundle options, @Nullable WindowContainerToken hideTaskToken,
+ @SplitIndex int index) {
ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "startIntent: intent=%s position=%d", intent.getIntent(),
position);
mSplitRequest = new SplitRequest(intent.getIntent(), position);
final WindowContainerTransaction wct = new WindowContainerTransaction();
- options = resolveStartStage(STAGE_TYPE_UNDEFINED, position, options, null /* wct */);
+ options = enableFlexibleSplit()
+ ? resolveStartStageForIndex(options, null /*wct*/, index)
+ : resolveStartStage(STAGE_TYPE_UNDEFINED, position, options, null /* wct */);
if (hideTaskToken != null) {
ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "Reordering hide-task to bottom");
wct.reorder(hideTaskToken, false /* onTop */);
@@ -667,13 +750,17 @@
// If split screen is not activated, we're expecting to open a pair of apps to split.
final int extraTransitType = isSplitActive()
? TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE : TRANSIT_SPLIT_SCREEN_PAIR_OPEN;
- prepareEnterSplitScreen(wct, null /* taskInfo */, position, !mIsDropEntering);
+ prepareEnterSplitScreen(wct, null /* taskInfo */, position, !mIsDropEntering, index);
mSplitTransitions.startEnterTransition(TRANSIT_TO_FRONT, wct, null, this,
extraTransitType, !mIsDropEntering);
}
- /** Starts 2 tasks in one transition. */
+ /**
+ * Starts 2 tasks in one transition.
+ * @param taskId1 starts in the mSideStage
+ * @param taskId2 starts in the mainStage #startWithTask()
+ */
void startTasks(int taskId1, @Nullable Bundle options1, int taskId2, @Nullable Bundle options2,
@SplitPosition int splitPosition, @PersistentSnapPosition int snapPosition,
@Nullable RemoteTransition remoteTransition, InstanceId instanceId) {
@@ -688,11 +775,19 @@
setSideStagePosition(splitPosition, wct);
options1 = options1 != null ? options1 : new Bundle();
- addActivityOptions(options1, mSideStage);
+ StageTaskListener stageForTask1;
+ if (enableFlexibleSplit()) {
+ stageForTask1 = mStageOrderOperator.getStageForLegacyPosition(splitPosition,
+ true /*checkAllStagesIfNotActive*/);
+ } else {
+ stageForTask1 = mSideStage;
+ }
+ addActivityOptions(options1, stageForTask1);
prepareTasksForSplitScreen(new int[] {taskId1, taskId2}, wct);
wct.startTask(taskId1, options1);
- startWithTask(wct, taskId2, options2, snapPosition, remoteTransition, instanceId);
+ startWithTask(wct, taskId2, options2, snapPosition, remoteTransition, instanceId,
+ splitPosition);
}
/** Start an intent and a task to a split pair in one transition. */
@@ -722,7 +817,8 @@
wct.sendPendingIntent(pendingIntent, fillInIntent, options1);
prepareTasksForSplitScreen(new int[] {taskId}, wct);
- startWithTask(wct, taskId, options2, snapPosition, remoteTransition, instanceId);
+ startWithTask(wct, taskId, options2, snapPosition, remoteTransition, instanceId,
+ splitPosition);
}
/**
@@ -766,7 +862,8 @@
wct.startShortcut(mContext.getPackageName(), shortcutInfo, options1);
prepareTasksForSplitScreen(new int[] {taskId}, wct);
- startWithTask(wct, taskId, options2, snapPosition, remoteTransition, instanceId);
+ startWithTask(wct, taskId, options2, snapPosition, remoteTransition, instanceId,
+ splitPosition);
}
/**
@@ -796,11 +893,14 @@
*/
private void startWithTask(WindowContainerTransaction wct, int mainTaskId,
@Nullable Bundle mainOptions, @PersistentSnapPosition int snapPosition,
- @Nullable RemoteTransition remoteTransition, InstanceId instanceId) {
+ @Nullable RemoteTransition remoteTransition, InstanceId instanceId,
+ @SplitPosition int splitPosition) {
if (!isSplitActive()) {
// Build a request WCT that will launch both apps such that task 0 is on the main stage
// while task 1 is on the side stage.
- activateSplit(wct, false /* reparentToTop */);
+ // TODO(b/349828130) currently pass in index_undefined until we can revisit these
+ // specific cases in the future. Only focusing on parity with starting intent/task
+ activateSplit(wct, false /* reparentToTop */, SPLIT_INDEX_UNDEFINED);
}
mSplitLayout.setDivideRatio(snapPosition);
updateWindowBounds(mSplitLayout, wct);
@@ -808,10 +908,19 @@
wct.setReparentLeafTaskIfRelaunch(mRootTaskInfo.token,
false /* reparentLeafTaskIfRelaunch */);
setRootForceTranslucent(false, wct);
-
+ // All callers of this method set the correct activity options on mSideStage,
+ // so we choose the opposite stage for this method
+ StageTaskListener stage;
+ if (enableFlexibleSplit()) {
+ stage = mStageOrderOperator
+ .getStageForLegacyPosition(reverseSplitPosition(splitPosition),
+ false /*checkAllStagesIfNotActive*/);
+ } else {
+ stage = mMainStage;
+ }
// Make sure the launch options will put tasks in the corresponding split roots
mainOptions = mainOptions != null ? mainOptions : new Bundle();
- addActivityOptions(mainOptions, mMainStage);
+ addActivityOptions(mainOptions, stage);
// Add task launch requests
wct.startTask(mainTaskId, mainOptions);
@@ -867,7 +976,9 @@
if (!isSplitActive()) {
// Build a request WCT that will launch both apps such that task 0 is on the main stage
// while task 1 is on the side stage.
- activateSplit(wct, false /* reparentToTop */);
+ // TODO(b/349828130) currently pass in index_undefined until we can revisit these
+ // specific cases in the future. Only focusing on parity with starting intent/task
+ activateSplit(wct, false /* reparentToTop */, SPLIT_INDEX_UNDEFINED);
}
setSideStagePosition(splitPosition, wct);
@@ -975,6 +1086,31 @@
mSideStage.evictInvisibleChildren(wct);
}
+ /**
+ * @param index for the new stage that will be opening. Ex. if app is dragged to
+ * index=1, then this will tell the stage at index=1 to launch the task
+ * in the wct in that stage. This doesn't verify that the non-specified
+ * indices' stages have their tasks correctly set/re-parented.
+ */
+ Bundle resolveStartStageForIndex(@Nullable Bundle options,
+ @Nullable WindowContainerTransaction wct,
+ @SplitIndex int index) {
+ StageTaskListener oppositeStage;
+ if (index == SPLIT_INDEX_UNDEFINED) {
+ // Arbitrarily choose a stage
+ oppositeStage = mStageOrderOperator.getStageForIndex(SPLIT_INDEX_1);
+ } else {
+ oppositeStage = mStageOrderOperator.getStageForIndex(index);
+ }
+ if (options == null) {
+ options = new Bundle();
+ }
+ updateStageWindowBoundsForIndex(wct, index);
+ addActivityOptions(options, oppositeStage);
+
+ return options;
+ }
+
Bundle resolveStartStage(@StageType int stage, @SplitPosition int position,
@Nullable Bundle options, @Nullable WindowContainerTransaction wct) {
switch (stage) {
@@ -1042,20 +1178,35 @@
return INVALID_TASK_ID;
}
- return mSideStagePosition == splitPosition
- ? mSideStage.getTopVisibleChildTaskId()
- : mMainStage.getTopVisibleChildTaskId();
+ if (enableFlexibleSplit()) {
+ StageTaskListener stage = mStageOrderOperator.getStageForLegacyPosition(splitPosition,
+ true /*checkAllStagesIfNotActive*/);
+ return stage != null ? stage.getTopVisibleChildTaskId() : INVALID_TASK_ID;
+ } else {
+ return mSideStagePosition == splitPosition
+ ? mSideStage.getTopVisibleChildTaskId()
+ : mMainStage.getTopVisibleChildTaskId();
+ }
}
void switchSplitPosition(String reason) {
ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "switchSplitPosition");
final SurfaceControl.Transaction t = mTransactionPool.acquire();
mTempRect1.setEmpty();
- final StageTaskListener topLeftStage =
- mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mSideStage : mMainStage;
- final StageTaskListener bottomRightStage =
- mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mMainStage : mSideStage;
-
+ final StageTaskListener topLeftStage;
+ final StageTaskListener bottomRightStage;
+ if (enableFlexibleSplit()) {
+ topLeftStage = mStageOrderOperator.getStageForLegacyPosition(SPLIT_POSITION_TOP_OR_LEFT,
+ false /*checkAllStagesIfNotActive*/);
+ bottomRightStage = mStageOrderOperator
+ .getStageForLegacyPosition(SPLIT_POSITION_BOTTOM_OR_RIGHT,
+ false /*checkAllStagesIfNotActive*/);
+ } else {
+ topLeftStage =
+ mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mSideStage : mMainStage;
+ bottomRightStage =
+ mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mMainStage : mSideStage;
+ }
// Don't allow windows or divider to be focused during animation (mRootTaskInfo is the
// parent of all 3 leaves). We don't want the user to be able to tap and focus a window
// while it is moving across the screen, because granting focus also recalculates the
@@ -1092,23 +1243,40 @@
});
ProtoLog.v(WM_SHELL_SPLIT_SCREEN, "Switch split position: %s", reason);
- mLogger.logSwap(getMainStagePosition(), mMainStage.getTopChildTaskUid(),
- getSideStagePosition(), mSideStage.getTopChildTaskUid(),
- mSplitLayout.isLeftRightSplit());
+ if (enableFlexibleSplit()) {
+ // TODO(b/374825718) update logging for 2+ apps
+ } else {
+ mLogger.logSwap(getMainStagePosition(), mMainStage.getTopChildTaskUid(),
+ getSideStagePosition(), mSideStage.getTopChildTaskUid(),
+ mSplitLayout.isLeftRightSplit());
+ }
}
void setSideStagePosition(@SplitPosition int sideStagePosition,
@Nullable WindowContainerTransaction wct) {
- setSideStagePosition(sideStagePosition, true /* updateBounds */, wct);
- }
-
- private void setSideStagePosition(@SplitPosition int sideStagePosition, boolean updateBounds,
- @Nullable WindowContainerTransaction wct) {
if (mSideStagePosition == sideStagePosition) return;
mSideStagePosition = sideStagePosition;
sendOnStagePositionChanged();
+ StageTaskListener stage = enableFlexibleSplit()
+ ? mStageOrderOperator.getStageForLegacyPosition(mSideStagePosition,
+ true /*checkAllStagesIfNotActive*/)
+ : mSideStage;
- if (mSideStage.mVisible && updateBounds) {
+ if (stage.mVisible) {
+ if (wct == null) {
+ // onLayoutChanged builds/applies a wct with the contents of updateWindowBounds.
+ onLayoutSizeChanged(mSplitLayout);
+ } else {
+ updateWindowBounds(mSplitLayout, wct);
+ sendOnBoundsChanged();
+ }
+ }
+ }
+
+ private void updateStageWindowBoundsForIndex(@Nullable WindowContainerTransaction wct,
+ @SplitIndex int index) {
+ StageTaskListener stage = mStageOrderOperator.getStageForIndex(index);
+ if (stage.mVisible) {
if (wct == null) {
// onLayoutChanged builds/applies a wct with the contents of updateWindowBounds.
onLayoutSizeChanged(mSplitLayout);
@@ -1158,10 +1326,17 @@
void recordLastActiveStage() {
if (!isSplitActive() || !isSplitScreenVisible()) {
mLastActiveStage = STAGE_TYPE_UNDEFINED;
- } else if (mMainStage.isFocused()) {
- mLastActiveStage = STAGE_TYPE_MAIN;
- } else if (mSideStage.isFocused()) {
- mLastActiveStage = STAGE_TYPE_SIDE;
+ } else if (enableFlexibleSplit()) {
+ mStageOrderOperator.getActiveStages().stream()
+ .filter(StageTaskListener::isFocused)
+ .findFirst()
+ .ifPresent(stage -> mLastActiveStage = stage.getId());
+ } else {
+ if (mMainStage.isFocused()) {
+ mLastActiveStage = STAGE_TYPE_MAIN;
+ } else if (mSideStage.isFocused()) {
+ mLastActiveStage = STAGE_TYPE_SIDE;
+ }
}
}
@@ -1218,7 +1393,7 @@
mSplitLayout.getInvisibleBounds(mTempRect1);
if (childrenToTop == null || childrenToTop.getTopVisibleChildTaskId() == INVALID_TASK_ID) {
mSideStage.removeAllTasks(wct, false /* toTop */);
- deactivateSplit(wct, false /* reparentToTop */);
+ deactivateSplit(wct, STAGE_TYPE_UNDEFINED);
wct.reorder(mRootTaskInfo.token, false /* onTop */);
setRootForceTranslucent(true, wct);
wct.setBounds(mSideStage.mRootTaskInfo.token, mTempRect1);
@@ -1247,7 +1422,7 @@
childrenToTop.fadeOutDecor(() -> {
WindowContainerTransaction finishedWCT = new WindowContainerTransaction();
mIsExiting = false;
- deactivateSplit(finishedWCT, childrenToTop == mMainStage /* reparentToTop */);
+ deactivateSplit(finishedWCT, childrenToTop.getId());
mSideStage.removeAllTasks(finishedWCT, childrenToTop == mSideStage /* toTop */);
finishedWCT.reorder(mRootTaskInfo.token, false /* toTop */);
setRootForceTranslucent(true, finishedWCT);
@@ -1275,6 +1450,8 @@
final WindowContainerTransaction wct = new WindowContainerTransaction();
prepareExitSplitScreen(stage, wct);
mSplitTransitions.startDismissTransition(wct, this, stage, exitReason);
+ // reset stages to their default sides.
+ setSideStagePosition(SPLIT_POSITION_BOTTOM_OR_RIGHT, null);
logExit(exitReason);
}
@@ -1362,15 +1539,24 @@
}
void clearSplitPairedInRecents(@ExitReason int exitReason) {
- if (!shouldBreakPairedTaskInRecents(exitReason) || !mShouldUpdateRecents) return;
+ if (!shouldBreakPairedTaskInRecents(exitReason) || !mShouldUpdateRecents) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "clearSplitPairedInRecents: skipping reason=%s",
+ !mShouldUpdateRecents ? "shouldn't update" : exitReasonToString(exitReason));
+ return;
+ }
ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "clearSplitPairedInRecents: reason=%s",
exitReasonToString(exitReason));
mRecentTasks.ifPresent(recentTasks -> {
// Notify recents if we are exiting in a way that breaks the pair, and disable further
// updates to splits in the recents until we enter split again
- mMainStage.doForAllChildTasks(taskId -> recentTasks.removeSplitPair(taskId));
- mSideStage.doForAllChildTasks(taskId -> recentTasks.removeSplitPair(taskId));
+ if (enableFlexibleSplit()) {
+ runForActiveStages((stage) ->
+ stage.doForAllChildTasks(taskId -> recentTasks.removeSplitPair(taskId)));
+ } else {
+ mMainStage.doForAllChildTasks(taskId -> recentTasks.removeSplitPair(taskId));
+ mSideStage.doForAllChildTasks(taskId -> recentTasks.removeSplitPair(taskId));
+ }
});
logExit(exitReason);
}
@@ -1383,15 +1569,22 @@
void prepareExitSplitScreen(@StageType int stageToTop,
@NonNull WindowContainerTransaction wct) {
if (!isSplitActive()) return;
- ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "prepareExitSplitScreen: stageToTop=%d", stageToTop);
- mSideStage.removeAllTasks(wct, stageToTop == STAGE_TYPE_SIDE);
- deactivateSplit(wct, stageToTop == STAGE_TYPE_MAIN);
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "prepareExitSplitScreen: stageToTop=%s",
+ stageTypeToString(stageToTop));
+ if (enableFlexibleSplit()) {
+ mStageOrderOperator.getActiveStages().stream()
+ .filter(stage -> stage.getId() != stageToTop)
+ .forEach(stage -> stage.removeAllTasks(wct, false /*toTop*/));
+ } else {
+ mSideStage.removeAllTasks(wct, stageToTop == STAGE_TYPE_SIDE);
+ }
+ deactivateSplit(wct, stageToTop);
}
private void prepareEnterSplitScreen(WindowContainerTransaction wct) {
ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "prepareEnterSplitScreen");
prepareEnterSplitScreen(wct, null /* taskInfo */, SPLIT_POSITION_UNDEFINED,
- !mIsDropEntering);
+ !mIsDropEntering, SPLIT_INDEX_UNDEFINED);
}
/**
@@ -1400,7 +1593,7 @@
*/
void prepareEnterSplitScreen(WindowContainerTransaction wct,
@Nullable ActivityManager.RunningTaskInfo taskInfo, @SplitPosition int startPosition,
- boolean resizeAnim) {
+ boolean resizeAnim, @SplitIndex int index) {
ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "prepareEnterSplitScreen: position=%d resize=%b",
startPosition, resizeAnim);
onSplitScreenEnter();
@@ -1412,7 +1605,7 @@
if (isSplitActive()) {
prepareBringSplit(wct, taskInfo, startPosition, resizeAnim);
} else {
- prepareActiveSplit(wct, taskInfo, startPosition, resizeAnim);
+ prepareActiveSplit(wct, taskInfo, startPosition, resizeAnim, index);
}
}
@@ -1433,14 +1626,22 @@
if (!mSkipEvictingMainStageChildren) {
mMainStage.evictAllChildren(wct);
}
- mMainStage.reparentTopTask(wct);
+ // TODO(b/349828130) revisit bring split from BG to FG scenarios
+ if (enableFlexibleSplit()) {
+ runForActiveStages(stage -> stage.reparentTopTask(wct));
+ } else {
+ mMainStage.reparentTopTask(wct);
+ }
prepareSplitLayout(wct, resizeAnim);
}
}
+ /**
+ * @param index The index that has already been assigned a stage
+ */
private void prepareActiveSplit(WindowContainerTransaction wct,
@Nullable ActivityManager.RunningTaskInfo taskInfo, @SplitPosition int startPosition,
- boolean resizeAnim) {
+ boolean resizeAnim, @SplitIndex int index) {
ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "prepareActiveSplit: task=%d isSplitVisible=%b",
taskInfo != null ? taskInfo.taskId : -1, isSplitScreenVisible());
// We handle split visibility itself on shell transition, but sometimes we didn't
@@ -1450,7 +1651,7 @@
setSideStagePosition(startPosition, wct);
mSideStage.addTask(taskInfo, wct);
}
- activateSplit(wct, true /* reparentToTop */);
+ activateSplit(wct, true /* reparentToTop */, index);
prepareSplitLayout(wct, resizeAnim);
}
@@ -1477,8 +1678,13 @@
void finishEnterSplitScreen(SurfaceControl.Transaction finishT) {
ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "finishEnterSplitScreen");
mSplitLayout.update(null, true /* resetImePosition */);
- mMainStage.getSplitDecorManager().inflate(mContext, mMainStage.mRootLeash);
- mSideStage.getSplitDecorManager().inflate(mContext, mSideStage.mRootLeash);
+ if (enableFlexibleSplit()) {
+ runForActiveStages((stage) ->
+ stage.getSplitDecorManager().inflate(mContext, stage.mRootLeash));
+ } else {
+ mMainStage.getSplitDecorManager().inflate(mContext, mMainStage.mRootLeash);
+ mSideStage.getSplitDecorManager().inflate(mContext, mSideStage.mRootLeash);
+ }
setDividerVisibility(true, finishT);
// Ensure divider surface are re-parented back into the hierarchy at the end of the
// transition. See Transition#buildFinishTransaction for more detail.
@@ -1492,6 +1698,10 @@
mSplitRequest = null;
updateRecentTasksSplitPair();
+ if (enableFlexibleSplit()) {
+ // TODO(b/374825718) log 2+ apps
+ return;
+ }
mLogger.logEnter(mSplitLayout.getDividerPositionAsFraction(),
getMainStagePosition(), mMainStage.getTopChildTaskUid(),
getSideStagePosition(), mSideStage.getTopChildTaskUid(),
@@ -1499,8 +1709,17 @@
}
void getStageBounds(Rect outTopOrLeftBounds, Rect outBottomOrRightBounds) {
- outTopOrLeftBounds.set(mSplitLayout.getBounds1());
- outBottomOrRightBounds.set(mSplitLayout.getBounds2());
+ outTopOrLeftBounds.set(mSplitLayout.getTopLeftBounds());
+ outBottomOrRightBounds.set(mSplitLayout.getBottomRightBounds());
+ }
+
+ private void runForActiveStages(Consumer<StageTaskListener> consumer) {
+ mStageOrderOperator.getActiveStages().forEach(consumer);
+ }
+
+ private boolean runForActiveStagesAllMatch(Predicate<StageTaskListener> predicate) {
+ List<StageTaskListener> activeStages = mStageOrderOperator.getActiveStages();
+ return !activeStages.isEmpty() && activeStages.stream().allMatch(predicate);
}
@SplitPosition
@@ -1516,6 +1735,9 @@
private void addActivityOptions(Bundle opts, @Nullable StageTaskListener launchTarget) {
ActivityOptions options = ActivityOptions.fromBundle(opts);
if (launchTarget != null) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN,
+ "addActivityOptions setting launch root for stage=%s",
+ stageTypeToString(launchTarget.getId()));
options.setLaunchRootTask(launchTarget.mRootTaskInfo.token);
}
// Put BAL flags to avoid activity start aborted. Otherwise, flows like shortcut to split
@@ -1559,8 +1781,15 @@
listener.onSplitBoundsChanged(mSplitLayout.getRootBounds(), getMainStageBounds(),
getSideStageBounds());
}
- mSideStage.onSplitScreenListenerRegistered(listener, STAGE_TYPE_SIDE);
- mMainStage.onSplitScreenListenerRegistered(listener, STAGE_TYPE_MAIN);
+ if (enableFlexibleSplit()) {
+ // TODO(b/349828130) replace w/ stageID
+ mStageOrderOperator.getAllStages().forEach(
+ stage -> stage.onSplitScreenListenerRegistered(listener, STAGE_TYPE_UNDEFINED)
+ );
+ } else {
+ mSideStage.onSplitScreenListenerRegistered(listener, STAGE_TYPE_SIDE);
+ mMainStage.onSplitScreenListenerRegistered(listener, STAGE_TYPE_MAIN);
+ }
}
private void sendOnStagePositionChanged() {
@@ -1584,20 +1813,35 @@
boolean present, boolean visible) {
int stage;
if (present) {
- stage = stageListener == mSideStage ? STAGE_TYPE_SIDE : STAGE_TYPE_MAIN;
+ if (enableFlexibleSplit()) {
+ stage = stageListener.getId();
+ } else {
+ stage = stageListener == mSideStage ? STAGE_TYPE_SIDE : STAGE_TYPE_MAIN;
+ }
} else {
// No longer on any stage
stage = STAGE_TYPE_UNDEFINED;
}
- if (stage == STAGE_TYPE_MAIN) {
- mLogger.logMainStageAppChange(getMainStagePosition(), mMainStage.getTopChildTaskUid(),
- mSplitLayout.isLeftRightSplit());
- } else if (stage == STAGE_TYPE_SIDE) {
- mLogger.logSideStageAppChange(getSideStagePosition(), mSideStage.getTopChildTaskUid(),
- mSplitLayout.isLeftRightSplit());
+ if (!enableFlexibleSplit()) {
+ if (stage == STAGE_TYPE_MAIN) {
+ mLogger.logMainStageAppChange(getMainStagePosition(),
+ mMainStage.getTopChildTaskUid(),
+ mSplitLayout.isLeftRightSplit());
+ } else if (stage == STAGE_TYPE_SIDE) {
+ mLogger.logSideStageAppChange(getSideStagePosition(),
+ mSideStage.getTopChildTaskUid(),
+ mSplitLayout.isLeftRightSplit());
+ }
}
if (present) {
updateRecentTasksSplitPair();
+ } else if (mMainStage.getChildCount() == 0 && mSideStage.getChildCount() == 0) {
+ mRecentTasks.ifPresent(recentTasks -> {
+ // remove the split pair mapping from recentTasks, and disable further updates
+ // to splits in the recents until we enter split again.
+ recentTasks.removeSplitPair(taskId);
+ });
+ dismissSplitScreen(-1, EXIT_REASON_ROOT_TASK_VANISHED);
}
for (int i = mListeners.size() - 1; i >= 0; --i) {
@@ -1608,22 +1852,45 @@
private void updateRecentTasksSplitPair() {
// Preventing from single task update while processing recents.
if (!mShouldUpdateRecents || !mPausingTasks.isEmpty()) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "updateRecentTasksSplitPair: skipping reason=%s",
+ !mShouldUpdateRecents ? "shouldn't update" : "no pausing tasks");
return;
}
mRecentTasks.ifPresent(recentTasks -> {
- Rect topLeftBounds = mSplitLayout.getBounds1();
- Rect bottomRightBounds = mSplitLayout.getBounds2();
- int mainStageTopTaskId = mMainStage.getTopVisibleChildTaskId();
- int sideStageTopTaskId = mSideStage.getTopVisibleChildTaskId();
+ Rect topLeftBounds = new Rect();
+ mSplitLayout.copyTopLeftBounds(topLeftBounds);
+ Rect bottomRightBounds = new Rect();
+ mSplitLayout.copyBottomRightBounds(bottomRightBounds);
+
+ int sideStageTopTaskId;
+ int mainStageTopTaskId;
+ if (enableFlexibleSplit()) {
+ List<StageTaskListener> activeStages = mStageOrderOperator.getActiveStages();
+ if (activeStages.size() != 2) {
+ sideStageTopTaskId = mainStageTopTaskId = INVALID_TASK_ID;
+ } else {
+ // doesn't matter which one we assign to? What matters is the order of 0 and 1?
+ mainStageTopTaskId = activeStages.get(0).getTopVisibleChildTaskId();
+ sideStageTopTaskId = activeStages.get(1).getTopVisibleChildTaskId();
+ }
+ } else {
+ mainStageTopTaskId = mMainStage.getTopVisibleChildTaskId();
+ sideStageTopTaskId= mSideStage.getTopVisibleChildTaskId();
+ }
boolean sideStageTopLeft = mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT;
int leftTopTaskId;
int rightBottomTaskId;
- if (sideStageTopLeft) {
- leftTopTaskId = sideStageTopTaskId;
- rightBottomTaskId = mainStageTopTaskId;
- } else {
+ if (enableFlexibleSplit()) {
leftTopTaskId = mainStageTopTaskId;
rightBottomTaskId = sideStageTopTaskId;
+ } else {
+ if (sideStageTopLeft) {
+ leftTopTaskId = sideStageTopTaskId;
+ rightBottomTaskId = mainStageTopTaskId;
+ } else {
+ leftTopTaskId = mainStageTopTaskId;
+ rightBottomTaskId = sideStageTopTaskId;
+ }
}
if (Flags.enableFlexibleTwoAppSplit()) {
@@ -1732,29 +1999,59 @@
@VisibleForTesting
@Override
public void onRootTaskAppeared() {
- ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onRootTaskAppeared: rootTask=%s mainRoot=%b sideRoot=%b",
- mRootTaskInfo, mMainStage.mHasRootTask, mSideStage.mHasRootTask);
+ if (enableFlexibleSplit()) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onRootTaskAppeared: rootTask=%s",
+ mRootTaskInfo);
+ mStageOrderOperator.getAllStages().forEach(stage -> {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN,
+ " onRootStageAppeared stageId=%s hasRoot=%b",
+ stageTypeToString(stage.getId()), stage.mHasRootTask);
+ });
+ } else {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN,
+ "onRootTaskAppeared: rootTask=%s mainRoot=%b sideRoot=%b",
+ mRootTaskInfo, mMainStage.mHasRootTask, mSideStage.mHasRootTask);
+ }
+ boolean notAllStagesHaveRootTask;
+ if (enableFlexibleSplit()) {
+ notAllStagesHaveRootTask = mStageOrderOperator.getAllStages().stream()
+ .anyMatch((stage) -> !stage.mHasRootTask);
+ } else {
+ notAllStagesHaveRootTask = !mMainStage.mHasRootTask
+ || !mSideStage.mHasRootTask;
+ }
// Wait unit all root tasks appeared.
- if (mRootTaskInfo == null
- || !mMainStage.mHasRootTask
- || !mSideStage.mHasRootTask) {
+ if (mRootTaskInfo == null || notAllStagesHaveRootTask) {
return;
}
final WindowContainerTransaction wct = new WindowContainerTransaction();
- wct.reparent(mMainStage.mRootTaskInfo.token, mRootTaskInfo.token, true);
- wct.reparent(mSideStage.mRootTaskInfo.token, mRootTaskInfo.token, true);
+ if (enableFlexibleSplit()) {
+ mStageOrderOperator.getAllStages().forEach(stage ->
+ wct.reparent(stage.mRootTaskInfo.token, mRootTaskInfo.token, true));
+ } else {
+ wct.reparent(mMainStage.mRootTaskInfo.token, mRootTaskInfo.token, true);
+ wct.reparent(mSideStage.mRootTaskInfo.token, mRootTaskInfo.token, true);
+ }
- // Make the stages adjacent to each other so they occlude what's behind them.
- wct.setAdjacentRoots(mMainStage.mRootTaskInfo.token, mSideStage.mRootTaskInfo.token);
setRootForceTranslucent(true, wct);
- mSplitLayout.getInvisibleBounds(mTempRect1);
- wct.setBounds(mSideStage.mRootTaskInfo.token, mTempRect1);
+ if (!enableFlexibleSplit()) {
+ //TODO(b/373709676) Need to figure out how adjacentRoots work for flex split
+
+ // Make the stages adjacent to each other so they occlude what's behind them.
+ wct.setAdjacentRoots(mMainStage.mRootTaskInfo.token, mSideStage.mRootTaskInfo.token);
+ mSplitLayout.getInvisibleBounds(mTempRect1);
+ wct.setBounds(mSideStage.mRootTaskInfo.token, mTempRect1);
+ }
mSyncQueue.queue(wct);
- mSyncQueue.runInSync(t -> {
- t.setPosition(mSideStage.mRootLeash, mTempRect1.left, mTempRect1.top);
- });
- mLaunchAdjacentController.setLaunchAdjacentRoot(mSideStage.mRootTaskInfo.token);
+ if (!enableFlexibleSplit()) {
+ mSyncQueue.runInSync(t -> {
+ t.setPosition(mSideStage.mRootLeash, mTempRect1.left, mTempRect1.top);
+ });
+ mLaunchAdjacentController.setLaunchAdjacentRoot(mSideStage.mRootTaskInfo.token);
+ } else {
+ // TODO(b/373709676) Need to figure out how adjacentRoots work for flex split
+ }
}
@Override
@@ -1949,15 +2246,21 @@
}
@Override
- public void onSnappedToDismiss(boolean bottomOrRight, @ExitReason int exitReason) {
+ public void onSnappedToDismiss(boolean closedBottomRightStage, @ExitReason int exitReason) {
ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onSnappedToDismiss: bottomOrRight=%b reason=%s",
- bottomOrRight, exitReasonToString(exitReason));
- final boolean mainStageToTop =
- bottomOrRight ? mSideStagePosition == SPLIT_POSITION_BOTTOM_OR_RIGHT
+ closedBottomRightStage, exitReasonToString(exitReason));
+ boolean mainStageToTop =
+ closedBottomRightStage ? mSideStagePosition == SPLIT_POSITION_BOTTOM_OR_RIGHT
: mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT;
- final StageTaskListener toTopStage = mainStageToTop ? mMainStage : mSideStage;
-
- final int dismissTop = mainStageToTop ? STAGE_TYPE_MAIN : STAGE_TYPE_SIDE;
+ StageTaskListener toTopStage = mainStageToTop ? mMainStage : mSideStage;
+ int dismissTop = mainStageToTop ? STAGE_TYPE_MAIN : STAGE_TYPE_SIDE;
+ if (enableFlexibleSplit()) {
+ toTopStage = mStageOrderOperator.getStageForLegacyPosition(closedBottomRightStage
+ ? SPLIT_POSITION_TOP_OR_LEFT
+ : SPLIT_POSITION_BOTTOM_OR_RIGHT,
+ false /*checkAllStagesIfNotActive*/);
+ dismissTop = toTopStage.getId();
+ }
final WindowContainerTransaction wct = new WindowContainerTransaction();
toTopStage.resetBounds(wct);
prepareExitSplitScreen(dismissTop, wct);
@@ -1989,8 +2292,21 @@
updateSurfaceBounds(layout, t, shouldUseParallaxEffect);
getMainStageBounds(mTempRect1);
getSideStageBounds(mTempRect2);
- mMainStage.onResizing(mTempRect1, mTempRect2, t, offsetX, offsetY, mShowDecorImmediately);
- mSideStage.onResizing(mTempRect2, mTempRect1, t, offsetX, offsetY, mShowDecorImmediately);
+ if (enableFlexibleSplit()) {
+ StageTaskListener ltStage =
+ mStageOrderOperator.getStageForLegacyPosition(SPLIT_POSITION_TOP_OR_LEFT,
+ false /*checkAllStagesIfNotActive*/);
+ StageTaskListener brStage =
+ mStageOrderOperator.getStageForLegacyPosition(SPLIT_POSITION_BOTTOM_OR_RIGHT,
+ false /*checkAllStagesIfNotActive*/);
+ ltStage.onResizing(mTempRect1, mTempRect2, t, offsetX, offsetY, mShowDecorImmediately);
+ brStage.onResizing(mTempRect2, mTempRect1, t, offsetX, offsetY, mShowDecorImmediately);
+ } else {
+ mMainStage.onResizing(mTempRect1, mTempRect2, t, offsetX, offsetY,
+ mShowDecorImmediately);
+ mSideStage.onResizing(mTempRect2, mTempRect1, t, offsetX, offsetY,
+ mShowDecorImmediately);
+ }
t.apply();
mTransactionPool.release(t);
}
@@ -2006,19 +2322,33 @@
if (!sizeChanged) {
// We still need to resize on decor for ensure all current status clear.
final SurfaceControl.Transaction t = mTransactionPool.acquire();
- mMainStage.onResized(t);
- mSideStage.onResized(t);
+ if (enableFlexibleSplit()) {
+ runForActiveStages(stage -> stage.onResized(t));
+ } else {
+ mMainStage.onResized(t);
+ mSideStage.onResized(t);
+ }
mTransactionPool.release(t);
return;
}
-
+ List<SplitDecorManager> decorManagers = new ArrayList<>();
+ SplitDecorManager mainDecor = null;
+ SplitDecorManager sideDecor = null;
+ if (enableFlexibleSplit()) {
+ decorManagers = mStageOrderOperator.getActiveStages().stream()
+ .map(StageTaskListener::getSplitDecorManager)
+ .toList();
+ } else {
+ mainDecor = mMainStage.getSplitDecorManager();
+ sideDecor = mSideStage.getSplitDecorManager();
+ }
sendOnBoundsChanged();
mSplitLayout.setDividerInteractive(false, false, "onSplitResizeStart");
mSplitTransitions.startResizeTransition(wct, this, (aborted) -> {
mSplitLayout.setDividerInteractive(true, false, "onSplitResizeConsumed");
}, (finishWct, t) -> {
mSplitLayout.setDividerInteractive(true, false, "onSplitResizeFinish");
- }, mMainStage.getSplitDecorManager(), mSideStage.getSplitDecorManager());
+ }, mainDecor, sideDecor, decorManagers);
if (Flags.enableFlexibleTwoAppSplit()) {
switch (layout.calculateCurrentSnapPosition()) {
@@ -2045,29 +2375,55 @@
* @return true if stage bounds actually .
*/
private boolean updateWindowBounds(SplitLayout layout, WindowContainerTransaction wct) {
- final StageTaskListener topLeftStage =
- mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mSideStage : mMainStage;
- final StageTaskListener bottomRightStage =
- mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mMainStage : mSideStage;
+ final StageTaskListener topLeftStage;
+ final StageTaskListener bottomRightStage;
+ if (enableFlexibleSplit()) {
+ topLeftStage = mStageOrderOperator
+ .getStageForLegacyPosition(SPLIT_POSITION_TOP_OR_LEFT,
+ true /*checkAllStagesIfNotActive*/);
+ bottomRightStage = mStageOrderOperator
+ .getStageForLegacyPosition(SPLIT_POSITION_BOTTOM_OR_RIGHT,
+ true /*checkAllStagesIfNotActive*/);
+ } else {
+ topLeftStage = mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT
+ ? mSideStage
+ : mMainStage;
+ bottomRightStage = mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT
+ ? mMainStage
+ : mSideStage;
+ }
boolean updated = layout.applyTaskChanges(wct, topLeftStage.mRootTaskInfo,
bottomRightStage.mRootTaskInfo);
ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "updateWindowBounds: topLeftStage=%s bottomRightStage=%s",
- layout.getBounds1(), layout.getBounds2());
+ layout.getTopLeftBounds(), layout.getBottomRightBounds());
return updated;
}
void updateSurfaceBounds(@Nullable SplitLayout layout, @NonNull SurfaceControl.Transaction t,
boolean applyResizingOffset) {
- final StageTaskListener topLeftStage =
- mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mSideStage : mMainStage;
- final StageTaskListener bottomRightStage =
- mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mMainStage : mSideStage;
+ final StageTaskListener topLeftStage;
+ final StageTaskListener bottomRightStage;
+ if (enableFlexibleSplit()) {
+ topLeftStage = mStageOrderOperator
+ .getStageForLegacyPosition(SPLIT_POSITION_TOP_OR_LEFT,
+ true /*checkAllStagesIfNotActive*/);
+ bottomRightStage = mStageOrderOperator
+ .getStageForLegacyPosition(SPLIT_POSITION_BOTTOM_OR_RIGHT,
+ true /*checkAllStagesIfNotActive*/);
+ } else {
+ topLeftStage = mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT
+ ? mSideStage
+ : mMainStage;
+ bottomRightStage = mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT
+ ? mMainStage
+ : mSideStage;
+ }
(layout != null ? layout : mSplitLayout).applySurfaceChanges(t, topLeftStage.mRootLeash,
bottomRightStage.mRootLeash, topLeftStage.mDimLayer, bottomRightStage.mDimLayer,
applyResizingOffset);
ProtoLog.d(WM_SHELL_SPLIT_SCREEN,
"updateSurfaceBounds: topLeftStage=%s bottomRightStage=%s",
- layout.getBounds1(), layout.getBounds2());
+ layout.getTopLeftBounds(), layout.getBottomRightBounds());
}
@Override
@@ -2076,10 +2432,22 @@
return SPLIT_POSITION_UNDEFINED;
}
- if (mMainStage.containsToken(token)) {
- return getMainStagePosition();
- } else if (mSideStage.containsToken(token)) {
- return getSideStagePosition();
+ if (enableFlexibleSplit()) {
+ // We could migrate to/return the new INDEX enums here since most callers just care that
+ // this value isn't SPLIT_POSITION_UNDEFINED, but
+ // ImePositionProcessor#getImeTargetPosition actually uses the leftTop/bottomRight value
+ StageTaskListener stageForToken = mStageOrderOperator.getAllStages().stream()
+ .filter(stage -> stage.containsToken(token))
+ .findFirst().orElse(null);
+ return stageForToken == null
+ ? SPLIT_POSITION_UNDEFINED
+ : mStageOrderOperator.getLegacyPositionForStage(stageForToken);
+ } else {
+ if (mMainStage.containsToken(token)) {
+ return getMainStagePosition();
+ } else if (mSideStage.containsToken(token)) {
+ return getSideStagePosition();
+ }
}
return SPLIT_POSITION_UNDEFINED;
@@ -2176,27 +2544,49 @@
private Rect getSideStageBounds() {
return mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT
- ? mSplitLayout.getBounds1() : mSplitLayout.getBounds2();
+ ? mSplitLayout.getTopLeftBounds() : mSplitLayout.getBottomRightBounds();
}
private Rect getMainStageBounds() {
return mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT
- ? mSplitLayout.getBounds2() : mSplitLayout.getBounds1();
+ ? mSplitLayout.getBottomRightBounds() : mSplitLayout.getTopLeftBounds();
}
+ /**
+ * TODO(b/349828130) Currently the way this is being used is only to to get the bottomRight
+ * stage. Eventually we'll need to rename and for now we'll repurpose the method to return
+ * the bottomRight bounds under the flex split flag
+ */
private void getSideStageBounds(Rect rect) {
- if (mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT) {
- mSplitLayout.getBounds1(rect);
+ if (enableFlexibleSplit()) {
+ // Split Layout doesn't actually keep track of the bounds based on the stage,
+ // it only knows that bounds1 is leftTop position and bounds2 is bottomRight position
+ // We'll then assume this method is to get bounds of bottomRight stage
+ mSplitLayout.copyBottomRightBounds(rect);
+ } else if (mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT) {
+ mSplitLayout.copyTopLeftBounds(rect);
} else {
- mSplitLayout.getBounds2(rect);
+ mSplitLayout.copyBottomRightBounds(rect);
}
}
+ /**
+ * TODO(b/349828130) Currently the way this is being used is only to to get the leftTop
+ * stage. Eventually we'll need to rename and for now we'll repurpose the method to return
+ * the leftTop bounds under the flex split flag
+ */
private void getMainStageBounds(Rect rect) {
- if (mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT) {
- mSplitLayout.getBounds2(rect);
+ if (enableFlexibleSplit()) {
+ // Split Layout doesn't actually keep track of the bounds based on the stage,
+ // it only knows that bounds1 is leftTop position and bounds2 is bottomRight position
+ // We'll then assume this method is to get bounds of topLeft stage
+ mSplitLayout.copyTopLeftBounds(rect);
} else {
- mSplitLayout.getBounds1(rect);
+ if (mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT) {
+ mSplitLayout.copyBottomRightBounds(rect);
+ } else {
+ mSplitLayout.copyTopLeftBounds(rect);
+ }
}
}
@@ -2205,14 +2595,23 @@
* this task (yet) so this can also be used to identify which stage to put a task into.
*/
private StageTaskListener getStageOfTask(ActivityManager.RunningTaskInfo taskInfo) {
- // TODO(b/184679596): Find a way to either include task-org information in the transition,
- // or synchronize task-org callbacks so we can use stage.containsTask
- if (mMainStage.mRootTaskInfo != null
- && taskInfo.parentTaskId == mMainStage.mRootTaskInfo.taskId) {
- return mMainStage;
- } else if (mSideStage.mRootTaskInfo != null
- && taskInfo.parentTaskId == mSideStage.mRootTaskInfo.taskId) {
- return mSideStage;
+ if (enableFlexibleSplit()) {
+ return mStageOrderOperator.getActiveStages().stream()
+ .filter((stage) -> stage.mRootTaskInfo != null &&
+ taskInfo.parentTaskId == stage.mRootTaskInfo.taskId
+ )
+ .findFirst()
+ .orElse(null);
+ } else {
+ // TODO(b/184679596): Find a way to either include task-org information in the
+ // transition, or synchronize task-org callbacks so we can use stage.containsTask
+ if (mMainStage.mRootTaskInfo != null
+ && taskInfo.parentTaskId == mMainStage.mRootTaskInfo.taskId) {
+ return mMainStage;
+ } else if (mSideStage.mRootTaskInfo != null
+ && taskInfo.parentTaskId == mSideStage.mRootTaskInfo.taskId) {
+ return mSideStage;
+ }
}
return null;
}
@@ -2220,7 +2619,11 @@
@StageType
private int getStageType(StageTaskListener stage) {
if (stage == null) return STAGE_TYPE_UNDEFINED;
- return stage == mMainStage ? STAGE_TYPE_MAIN : STAGE_TYPE_SIDE;
+ if (enableFlexibleSplit()) {
+ return stage.getId();
+ } else {
+ return stage == mMainStage ? STAGE_TYPE_MAIN : STAGE_TYPE_SIDE;
+ }
}
@Override
@@ -2267,11 +2670,17 @@
if (isSplitActive()) {
ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "handleRequest: transition=%d split active",
request.getDebugId());
+ StageTaskListener primaryStage = enableFlexibleSplit()
+ ? mStageOrderOperator.getActiveStages().get(0)
+ : mMainStage;
+ StageTaskListener secondaryStage = enableFlexibleSplit()
+ ? mStageOrderOperator.getActiveStages().get(1)
+ : mSideStage;
// Try to handle everything while in split-screen, so return a WCT even if it's empty.
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " split is active so using split"
+ "Transition to handle request. triggerTask=%d type=%s mainChildren=%d"
+ " sideChildren=%d", triggerTask.taskId, transitTypeToString(type),
- mMainStage.getChildCount(), mSideStage.getChildCount());
+ primaryStage.getChildCount(), secondaryStage.getChildCount());
out = new WindowContainerTransaction();
if (stage != null) {
if (isClosingType(type) && stage.getChildCount() == 1) {
@@ -2311,11 +2720,21 @@
// the remote handler.
return null;
}
-
- if ((mMainStage.containsTask(triggerTask.taskId)
- && mMainStage.getChildCount() == 1)
- || (mSideStage.containsTask(triggerTask.taskId)
- && mSideStage.getChildCount() == 1)) {
+ boolean anyStageContainsSingleFullscreenTask;
+ if (enableFlexibleSplit()) {
+ anyStageContainsSingleFullscreenTask =
+ mStageOrderOperator.getActiveStages().stream()
+ .anyMatch(stageListener ->
+ stageListener.containsTask(triggerTask.taskId)
+ && stageListener.getChildCount() == 1);
+ } else {
+ anyStageContainsSingleFullscreenTask =
+ (mMainStage.containsTask(triggerTask.taskId)
+ && mMainStage.getChildCount() == 1)
+ || (mSideStage.containsTask(triggerTask.taskId)
+ && mSideStage.getChildCount() == 1);
+ }
+ if (anyStageContainsSingleFullscreenTask) {
// A splitting task is opening to fullscreen causes one side of the split empty,
// so appends operations to exit split.
prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, out);
@@ -2335,11 +2754,19 @@
// One of the cases above handled it
return out;
} else if (isSplitScreenVisible()) {
+ boolean allStagesHaveChildren;
+ if (enableFlexibleSplit()) {
+ allStagesHaveChildren = runForActiveStagesAllMatch(stageTaskListener ->
+ stageTaskListener.getChildCount() != 0);
+ } else {
+ allStagesHaveChildren = mMainStage.getChildCount() != 0
+ && mSideStage.getChildCount() != 0;
+ }
// 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;
+ && allStagesHaveChildren;
return !deferTransition ? out : null;
}
// Don't intercept the transition if we are not handling it as a part of one of the
@@ -2579,8 +3006,15 @@
}
final ArraySet<StageTaskListener> dismissStages = record.getShouldDismissedStage();
- if (mMainStage.getChildCount() == 0 || mSideStage.getChildCount() == 0
- || dismissStages.size() == 1) {
+ boolean anyStageHasNoChildren;
+ if (enableFlexibleSplit()) {
+ anyStageHasNoChildren = mStageOrderOperator.getActiveStages().stream()
+ .anyMatch(stage -> stage.getChildCount() == 0);
+ } else {
+ anyStageHasNoChildren = mMainStage.getChildCount() == 0
+ || mSideStage.getChildCount() == 0;
+ }
+ if (anyStageHasNoChildren || dismissStages.size() == 1) {
// If the size of dismissStages == 1, one of the task is closed without prepare
// pending transition, which could happen if all activities were finished after
// finish top activity in a task, so the trigger task is null when handleRequest.
@@ -2726,24 +3160,48 @@
shouldAnimate = startPendingDismissAnimation(
dismiss, info, startTransaction, finishTransaction);
if (shouldAnimate && dismiss.mReason == EXIT_REASON_DRAG_DIVIDER) {
- final StageTaskListener toTopStage =
- dismiss.mDismissTop == STAGE_TYPE_MAIN ? mMainStage : mSideStage;
+ StageTaskListener toTopStage;
+ if (enableFlexibleSplit()) {
+ toTopStage = mStageOrderOperator.getAllStages().stream()
+ .filter(stage -> stage.getId() == dismiss.mDismissTop)
+ .findFirst().orElseThrow();
+ } else {
+ toTopStage = dismiss.mDismissTop == STAGE_TYPE_MAIN ? mMainStage : mSideStage;
+ }
mSplitTransitions.playDragDismissAnimation(transition, info, startTransaction,
finishTransaction, finishCallback, toTopStage.mRootTaskInfo.token,
toTopStage.getSplitDecorManager(), mRootTaskInfo.token);
return true;
}
} else if (mSplitTransitions.isPendingResize(transition)) {
+ Map<WindowContainerToken, SplitDecorManager> tokenDecorMap = new HashMap<>();
+ if (enableFlexibleSplit()) {
+ runForActiveStages(stageTaskListener ->
+ tokenDecorMap.put(stageTaskListener.mRootTaskInfo.getToken(),
+ stageTaskListener.getSplitDecorManager()));
+ } else {
+ tokenDecorMap.put(mMainStage.mRootTaskInfo.getToken(),
+ mMainStage.getSplitDecorManager());
+ tokenDecorMap.put(mSideStage.mRootTaskInfo.getToken(),
+ mSideStage.getSplitDecorManager());
+ }
mSplitTransitions.playResizeAnimation(transition, info, startTransaction,
- finishTransaction, finishCallback, mMainStage.mRootTaskInfo.token,
- mSideStage.mRootTaskInfo.token, mMainStage.getSplitDecorManager(),
- mSideStage.getSplitDecorManager());
+ finishTransaction, finishCallback, tokenDecorMap);
return true;
}
if (!shouldAnimate) return false;
+ WindowContainerToken mainToken;
+ WindowContainerToken sideToken;
+ if (enableFlexibleSplit()) {
+ mainToken = mStageOrderOperator.getActiveStages().get(0).mRootTaskInfo.token;
+ sideToken = mStageOrderOperator.getActiveStages().get(1).mRootTaskInfo.token;
+ } else {
+ mainToken = mMainStage.mRootTaskInfo.token;
+ sideToken = mSideStage.mRootTaskInfo.token;
+ }
mSplitTransitions.playAnimation(transition, info, startTransaction, finishTransaction,
- finishCallback, mMainStage.mRootTaskInfo.token, mSideStage.mRootTaskInfo.token,
+ finishCallback, mainToken, sideToken,
mRootTaskInfo.token);
return true;
}
@@ -2768,6 +3226,8 @@
// First, verify that we actually have opened apps in both splits.
TransitionInfo.Change mainChild = null;
TransitionInfo.Change sideChild = null;
+ StageTaskListener firstAppStage = null;
+ StageTaskListener secondAppStage = null;
final WindowContainerTransaction evictWct = new WindowContainerTransaction();
for (int iC = 0; iC < info.getChanges().size(); ++iC) {
final TransitionInfo.Change change = info.getChanges().get(iC);
@@ -2776,14 +3236,19 @@
if (mPausingTasks.contains(taskInfo.taskId)) {
continue;
}
- final @StageType int stageType = getStageType(getStageOfTask(taskInfo));
- if (mainChild == null && stageType == STAGE_TYPE_MAIN
+ StageTaskListener stage = getStageOfTask(taskInfo);
+ final @StageType int stageType = getStageType(stage);
+ if (mainChild == null
+ && stageType == (enableFlexibleSplit() ? STAGE_TYPE_A : STAGE_TYPE_MAIN)
&& (isOpeningType(change.getMode()) || change.getMode() == TRANSIT_CHANGE)) {
// Includes TRANSIT_CHANGE to cover reparenting top-most task to split.
mainChild = change;
- } else if (sideChild == null && stageType == STAGE_TYPE_SIDE
+ firstAppStage = getStageOfTask(taskInfo);
+ } else if (sideChild == null
+ && stageType == (enableFlexibleSplit() ? STAGE_TYPE_B : STAGE_TYPE_SIDE)
&& (isOpeningType(change.getMode()) || change.getMode() == TRANSIT_CHANGE)) {
sideChild = change;
+ secondAppStage = stage;
} else if (stageType != STAGE_TYPE_UNDEFINED && change.getMode() == TRANSIT_TO_BACK) {
// Collect all to back task's and evict them when transition finished.
evictWct.reparent(taskInfo.token, null /* parent */, false /* onTop */);
@@ -2839,9 +3304,9 @@
// TODO(b/184679596): Find a way to either include task-org information in
// the transition, or synchronize task-org callbacks.
final boolean mainNotContainOpenTask =
- mainChild != null && !mMainStage.containsTask(mainChild.getTaskInfo().taskId);
+ mainChild != null && !firstAppStage.containsTask(mainChild.getTaskInfo().taskId);
final boolean sideNotContainOpenTask =
- sideChild != null && !mSideStage.containsTask(sideChild.getTaskInfo().taskId);
+ sideChild != null && !secondAppStage.containsTask(sideChild.getTaskInfo().taskId);
if (mainNotContainOpenTask) {
Log.w(TAG, "Expected onTaskAppeared on " + mMainStage
+ " to have been called with " + mainChild.getTaskInfo().taskId
@@ -2854,6 +3319,8 @@
}
final TransitionInfo.Change finalMainChild = mainChild;
final TransitionInfo.Change finalSideChild = sideChild;
+ final StageTaskListener finalFirstAppStage = firstAppStage;
+ final StageTaskListener finalSecondAppStage = secondAppStage;
enterTransition.setFinishedCallback((callbackWct, callbackT) -> {
if (!enterTransition.mResizeAnim) {
// If resizing, we'll call notify at the end of the resizing animation (below)
@@ -2861,16 +3328,18 @@
}
if (finalMainChild != null) {
if (!mainNotContainOpenTask) {
- mMainStage.evictOtherChildren(callbackWct, finalMainChild.getTaskInfo().taskId);
+ finalFirstAppStage.evictOtherChildren(callbackWct,
+ finalMainChild.getTaskInfo().taskId);
} else {
- mMainStage.evictInvisibleChildren(callbackWct);
+ finalFirstAppStage.evictInvisibleChildren(callbackWct);
}
}
if (finalSideChild != null) {
if (!sideNotContainOpenTask) {
- mSideStage.evictOtherChildren(callbackWct, finalSideChild.getTaskInfo().taskId);
+ finalSecondAppStage.evictOtherChildren(callbackWct,
+ finalSideChild.getTaskInfo().taskId);
} else {
- mSideStage.evictInvisibleChildren(callbackWct);
+ finalSecondAppStage.evictInvisibleChildren(callbackWct);
}
}
if (!evictWct.isEmpty()) {
@@ -2952,8 +3421,10 @@
public void onPipExpandToSplit(WindowContainerTransaction wct,
ActivityManager.RunningTaskInfo taskInfo) {
ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onPipExpandToSplit: task=%s", taskInfo);
+ // TODO(b/349828130) currently pass in index_undefined until we can revisit these
+ // flex split + pip interactions in the future
prepareEnterSplitScreen(wct, taskInfo, getActivateSplitPosition(taskInfo),
- false /*resizeAnim*/);
+ false /*resizeAnim*/, SPLIT_INDEX_UNDEFINED);
if (!isSplitScreenVisible() || mSplitRequest == null) {
return;
@@ -3058,13 +3529,28 @@
// Wait until after animation to update divider
// Reset crops so they don't interfere with subsequent launches
- t.setCrop(mMainStage.mRootLeash, null);
- t.setCrop(mSideStage.mRootLeash, null);
+ if (enableFlexibleSplit()) {
+ runForActiveStages(stage -> t.setCrop(stage.mRootLeash, null /*crop*/));
+ } else {
+ t.setCrop(mMainStage.mRootLeash, null);
+ t.setCrop(mSideStage.mRootLeash, null);
+ }
// Hide the non-top stage and set the top one to the fullscreen position.
if (toStage != STAGE_TYPE_UNDEFINED) {
- t.hide(toStage == STAGE_TYPE_MAIN ? mSideStage.mRootLeash : mMainStage.mRootLeash);
- t.setPosition(toStage == STAGE_TYPE_MAIN
- ? mMainStage.mRootLeash : mSideStage.mRootLeash, 0, 0);
+ if (enableFlexibleSplit()) {
+ StageTaskListener stageToKeep = mStageOrderOperator.getAllStages().stream()
+ .filter(stage -> stage.getId() == toStage)
+ .findFirst().orElseThrow();
+ List<StageTaskListener> stagesToHide = mStageOrderOperator.getAllStages().stream()
+ .filter(stage -> stage.getId() != toStage)
+ .toList();
+ stagesToHide.forEach(stage -> t.hide(stage.mRootLeash));
+ t.setPosition(stageToKeep.mRootLeash, 0, 0);
+ } else {
+ t.hide(toStage == STAGE_TYPE_MAIN ? mSideStage.mRootLeash : mMainStage.mRootLeash);
+ t.setPosition(toStage == STAGE_TYPE_MAIN
+ ? mMainStage.mRootLeash : mSideStage.mRootLeash, 0, 0);
+ }
} else {
for (int i = dismissingTasks.keySet().size() - 1; i >= 0; --i) {
finishT.hide(dismissingTasks.valueAt(i));
@@ -3079,8 +3565,12 @@
// Hide divider and dim layer on transition finished.
setDividerVisibility(false, t);
- finishT.hide(mMainStage.mDimLayer);
- finishT.hide(mSideStage.mDimLayer);
+ if (enableFlexibleSplit()) {
+ runForActiveStages(stage -> finishT.hide(stage.mRootLeash));
+ } else {
+ finishT.hide(mMainStage.mDimLayer);
+ finishT.hide(mSideStage.mDimLayer);
+ }
}
private boolean startPendingDismissAnimation(
@@ -3101,8 +3591,12 @@
return false;
}
dismissTransition.setFinishedCallback((callbackWct, callbackT) -> {
- mMainStage.getSplitDecorManager().release(callbackT);
- mSideStage.getSplitDecorManager().release(callbackT);
+ if (enableFlexibleSplit()) {
+ runForActiveStages(stage -> stage.getSplitDecorManager().release(callbackT));
+ } else {
+ mMainStage.getSplitDecorManager().release(callbackT);
+ mSideStage.getSplitDecorManager().release(callbackT);
+ }
callbackWct.setReparentLeafTaskIfRelaunch(mRootTaskInfo.token, false);
});
return true;
@@ -3119,8 +3613,15 @@
if (TransitionUtil.isClosingType(change.getMode())
&& change.getTaskInfo() != null) {
final int taskId = change.getTaskInfo().taskId;
- if (mMainStage.getTopVisibleChildTaskId() == taskId
- || mSideStage.getTopVisibleChildTaskId() == taskId) {
+ boolean anyStagesHaveTask;
+ if (enableFlexibleSplit()) {
+ anyStagesHaveTask = mStageOrderOperator.getActiveStages().stream()
+ .anyMatch(stage -> stage.getTopVisibleChildTaskId() == taskId);
+ } else {
+ anyStagesHaveTask = mMainStage.getTopVisibleChildTaskId() == taskId
+ || mSideStage.getTopVisibleChildTaskId() == taskId;
+ }
+ if (anyStagesHaveTask) {
mPausingTasks.add(taskId);
}
}
@@ -3152,9 +3653,16 @@
final WindowContainerTransaction.HierarchyOp op =
finishWct.getHierarchyOps().get(i);
final IBinder container = op.getContainer();
+ boolean anyStageContainsContainer;
+ if (enableFlexibleSplit()) {
+ anyStageContainsContainer = mStageOrderOperator.getActiveStages().stream()
+ .anyMatch(stage -> stage.containsContainer(container));
+ } else {
+ anyStageContainsContainer = mMainStage.containsContainer(container)
+ || mSideStage.containsContainer(container);
+ }
if (op.getType() == HIERARCHY_OP_TYPE_REORDER && op.getToTop()
- && (mMainStage.containsContainer(container)
- || mSideStage.containsContainer(container))) {
+ && anyStageContainsContainer) {
updateSurfaceBounds(mSplitLayout, finishT,
false /* applyResizingOffset */);
finishT.reparent(mSplitLayout.getDividerLeash(), mRootTaskLeash);
@@ -3181,10 +3689,18 @@
// user entering recents.
for (int i = mPausingTasks.size() - 1; i >= 0; --i) {
final int taskId = mPausingTasks.get(i);
- if (mMainStage.containsTask(taskId)) {
- mMainStage.evictChildren(finishWct, taskId);
- } else if (mSideStage.containsTask(taskId)) {
- mSideStage.evictChildren(finishWct, taskId);
+ if (enableFlexibleSplit()) {
+ mStageOrderOperator.getActiveStages().stream()
+ .filter(stage -> stage.containsTask(taskId))
+ .findFirst()
+ .ifPresent(stageToEvict ->
+ stageToEvict.evictChild(finishWct, taskId, "recentsPairToPair"));
+ } else {
+ if (mMainStage.containsTask(taskId)) {
+ mMainStage.evictChild(finishWct, taskId, "recentsPairToPair");
+ } else if (mSideStage.containsTask(taskId)) {
+ mSideStage.evictChild(finishWct, taskId, "recentsPairToPair");
+ }
}
}
// If pending enter hasn't consumed, the mix handler will invoke start pending
@@ -3247,8 +3763,15 @@
*/
private void setSplitsVisible(boolean visible) {
ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "setSplitsVisible: visible=%b", visible);
- mMainStage.mVisible = mSideStage.mVisible = visible;
- mMainStage.mHasChildren = mSideStage.mHasChildren = visible;
+ if (enableFlexibleSplit()) {
+ runForActiveStages(stage -> {
+ stage.mVisible = visible;
+ stage.mHasChildren = visible;
+ });
+ } else {
+ mMainStage.mVisible = mSideStage.mVisible = visible;
+ mMainStage.mHasChildren = mSideStage.mHasChildren = visible;
+ }
}
/**
@@ -3291,6 +3814,10 @@
* executed.
*/
private void logExitToStage(@ExitReason int exitReason, boolean toMainStage) {
+ if (enableFlexibleSplit()) {
+ // TODO(b/374825718) update logging for 2+ apps
+ return;
+ }
mLogger.logExit(exitReason,
toMainStage ? getMainStagePosition() : SPLIT_POSITION_UNDEFINED,
toMainStage ? mMainStage.getTopChildTaskUid() : 0 /* mainStageUid */,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageOrderOperator.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageOrderOperator.kt
new file mode 100644
index 0000000..b7b3c9b
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageOrderOperator.kt
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.splitscreen
+
+import android.content.Context
+import com.android.internal.protolog.ProtoLog
+import com.android.launcher3.icons.IconProvider
+import com.android.wm.shell.ShellTaskOrganizer
+import com.android.wm.shell.common.SyncTransactionQueue
+import com.android.wm.shell.protolog.ShellProtoLogGroup
+import com.android.wm.shell.shared.split.SplitScreenConstants
+import com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_NONE
+import com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_INDEX_0
+import com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_INDEX_1
+import com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_INDEX_2
+import com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT
+import com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT
+import com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED
+import com.android.wm.shell.shared.split.SplitScreenConstants.SnapPosition
+import com.android.wm.shell.shared.split.SplitScreenConstants.SplitIndex
+import com.android.wm.shell.shared.split.SplitScreenConstants.SplitPosition
+import com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_A
+import com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_B
+import com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_C
+import com.android.wm.shell.splitscreen.SplitScreen.stageTypeToString
+import com.android.wm.shell.windowdecor.WindowDecorViewModel
+import java.util.Optional
+
+/**
+ * Responsible for creating [StageTaskListener]s and maintaining their ordering on screen.
+ * Must be notified whenever stages positions change via swapping or starting/ending tasks
+ */
+class StageOrderOperator (
+ context: Context,
+ taskOrganizer: ShellTaskOrganizer,
+ displayId: Int,
+ stageCallbacks: StageTaskListener.StageListenerCallbacks,
+ syncQueue: SyncTransactionQueue,
+ iconProvider: IconProvider,
+ windowDecorViewModel: Optional<WindowDecorViewModel>
+ ) {
+
+ private val MAX_STAGES = 3
+ /**
+ * This somewhat acts as a replacement to stageTypes in the intermediary, so we want to start
+ * it after the @StageType constant values just to be safe and avoid potentially subtle bugs.
+ */
+ private var stageIds = listOf(STAGE_TYPE_A, STAGE_TYPE_B, STAGE_TYPE_C)
+
+ /**
+ * Active Stages, this list represent the current, ordered list of stages that are
+ * currently visible to the user. This map should be empty if the user is currently
+ * not in split screen. Note that this is different than if split screen is visible, which
+ * is determined by [StageListenerImpl.mVisible].
+ * Split stages can be active and in the background
+ */
+ val activeStages = mutableListOf<StageTaskListener>()
+ val allStages = mutableListOf<StageTaskListener>()
+ var isActive: Boolean = false
+ var isVisible: Boolean = false
+ @SnapPosition private var currentLayout: Int = SNAP_TO_NONE
+
+ init {
+ for(i in 0 until MAX_STAGES) {
+ allStages.add(StageTaskListener(context,
+ taskOrganizer,
+ displayId,
+ stageCallbacks,
+ syncQueue,
+ iconProvider,
+ windowDecorViewModel,
+ stageIds[i])
+ )
+ }
+ }
+
+ /**
+ * Updates internal state to keep record of "active" stages. Note that this does NOT call
+ * [StageTaskListener.activate] on the stages.
+ */
+ fun onEnteringSplit(@SnapPosition goingToLayout: Int) {
+ if (goingToLayout == currentLayout) {
+ // Add protolog here. Return for now, but maybe we want to handle swap case, TBD
+ return
+ }
+ val freeStages: List<StageTaskListener> =
+ allStages.filterNot { activeStages.contains(it) }
+ when(goingToLayout) {
+ SplitScreenConstants.SNAP_TO_2_50_50 -> {
+ if (activeStages.size < 2) {
+ // take from allStages and add into activeStages
+ for (i in 0 until (2 - activeStages.size)) {
+ val stage = freeStages[i]
+ activeStages.add(stage)
+ }
+ }
+ }
+ }
+ ProtoLog.d(
+ ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
+ "Activated stages: %d ids=%s",
+ activeStages.size,
+ activeStages.joinToString(",") { stageTypeToString(it.id) }
+ )
+ isActive = true
+ }
+
+ fun onExitingSplit() {
+ activeStages.clear()
+ isActive = false
+ }
+
+ /**
+ * Given a legacy [SplitPosition] returns one of the stages from the actives stages.
+ * If there are no active stages and [checkAllStagesIfNotActive] is not true, then will return
+ * null
+ */
+ fun getStageForLegacyPosition(@SplitPosition position: Int,
+ checkAllStagesIfNotActive : Boolean = false) :
+ StageTaskListener? {
+ if (activeStages.size != 2 && !checkAllStagesIfNotActive) {
+ return null
+ }
+ val listToCheck = if (activeStages.isEmpty() and checkAllStagesIfNotActive)
+ allStages else
+ activeStages
+ if (position == SPLIT_POSITION_TOP_OR_LEFT) {
+ return listToCheck[0]
+ } else if (position == SPLIT_POSITION_BOTTOM_OR_RIGHT) {
+ return listToCheck[1]
+ } else {
+ throw IllegalArgumentException("No stage for invalid position")
+ }
+ }
+
+ /**
+ * Returns a legacy split position for the given stage. If no stages are active then this will
+ * return [SPLIT_POSITION_UNDEFINED]
+ */
+ @SplitPosition
+ fun getLegacyPositionForStage(stage: StageTaskListener) : Int {
+ if (allStages[0] == stage) {
+ return SPLIT_POSITION_TOP_OR_LEFT
+ } else if (allStages[1] == stage) {
+ return SPLIT_POSITION_BOTTOM_OR_RIGHT
+ } else {
+ return SPLIT_POSITION_UNDEFINED
+ }
+ }
+
+ /**
+ * Returns the stageId from a given splitIndex. This will default to checking from all stages if
+ * [isActive] is false, otherwise will only check active stages.
+ */
+ fun getStageForIndex(@SplitIndex splitIndex: Int) : StageTaskListener {
+ // Probably should do a check for index to be w/in the bounds of the current split layout
+ // that we're currently in
+ val listToCheck = if (isActive) activeStages else allStages
+ if (splitIndex == SPLIT_INDEX_0) {
+ return listToCheck[0]
+ } else if (splitIndex == SPLIT_INDEX_1) {
+ return listToCheck[1]
+ } else if (splitIndex == SPLIT_INDEX_2) {
+ return listToCheck[2]
+ } else {
+ // Though I guess what if we're adding to the end? Maybe that indexing needs to be
+ // resolved elsewhere
+ throw IllegalStateException("No stage for the given splitIndex")
+ }
+ }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
index 4407e5b..4a37169 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
@@ -22,14 +22,17 @@
import static android.content.res.Configuration.SMALLEST_SCREEN_WIDTH_DP_UNDEFINED;
import static android.view.RemoteAnimationTarget.MODE_OPENING;
+import static com.android.wm.shell.Flags.enableFlexibleSplit;
import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN;
import static com.android.wm.shell.shared.split.SplitScreenConstants.CONTROLLED_ACTIVITY_TYPES;
import static com.android.wm.shell.shared.split.SplitScreenConstants.CONTROLLED_WINDOWING_MODES;
import static com.android.wm.shell.shared.split.SplitScreenConstants.CONTROLLED_WINDOWING_MODES_WHEN_ACTIVE;
+import static com.android.wm.shell.splitscreen.SplitScreen.stageTypeToString;
import android.annotation.CallSuper;
import android.annotation.Nullable;
import android.app.ActivityManager;
+import android.app.TaskInfo;
import android.content.Context;
import android.graphics.Rect;
import android.os.IBinder;
@@ -71,6 +74,8 @@
// No current way to enforce this but if enableFlexibleSplit() is enabled, then only 1 of the
// stages should have this be set/being used
private boolean mIsActive;
+ /** Unique identifier for this state, > 0 */
+ @StageType private final int mId;
/** Callback interface for listening to changes in a split-screen stage. */
public interface StageListenerCallbacks {
void onRootTaskAppeared();
@@ -109,13 +114,14 @@
StageTaskListener(Context context, ShellTaskOrganizer taskOrganizer, int displayId,
StageListenerCallbacks callbacks, SyncTransactionQueue syncQueue,
IconProvider iconProvider,
- Optional<WindowDecorViewModel> windowDecorViewModel) {
+ Optional<WindowDecorViewModel> windowDecorViewModel, int id) {
mContext = context;
mCallbacks = callbacks;
mSyncQueue = syncQueue;
mIconProvider = iconProvider;
mWindowDecorViewModel = windowDecorViewModel;
taskOrganizer.createRootTask(displayId, WINDOWING_MODE_MULTI_WINDOW, this);
+ mId = id;
}
int getChildCount() {
@@ -138,6 +144,8 @@
* Returns the top visible child task's id.
*/
int getTopVisibleChildTaskId() {
+ // TODO(b/378601156): This doesn't get the top task (translucent tasks are also
+ // visible-requested)
final ActivityManager.RunningTaskInfo taskInfo = getChildTaskInfo(t -> t.isVisible
&& t.isVisibleRequested);
return taskInfo != null ? taskInfo.taskId : INVALID_TASK_ID;
@@ -147,6 +155,7 @@
* Returns the top activity uid for the top child task.
*/
int getTopChildTaskUid() {
+ // TODO(b/378601156): This doesn't get the top task
final ActivityManager.RunningTaskInfo taskInfo =
getChildTaskInfo(t -> t.topActivityInfo != null);
return taskInfo != null ? taskInfo.topActivityInfo.applicationInfo.uid : 0;
@@ -157,6 +166,11 @@
return contains(t -> t.isFocused);
}
+ @StageType
+ int getId() {
+ return mId;
+ }
+
private boolean contains(Predicate<ActivityManager.RunningTaskInfo> predicate) {
if (mRootTaskInfo != null && predicate.test(mRootTaskInfo)) {
return true;
@@ -193,10 +207,10 @@
@CallSuper
public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) {
ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onTaskAppeared: taskId=%d taskParent=%d rootTask=%d "
- + "taskActivity=%s",
+ + "stageId=%s taskActivity=%s",
taskInfo.taskId, taskInfo.parentTaskId,
mRootTaskInfo != null ? mRootTaskInfo.taskId : -1,
- taskInfo.baseActivity);
+ stageTypeToString(mId), taskInfo.baseActivity);
if (mRootTaskInfo == null) {
mRootLeash = leash;
mRootTaskInfo = taskInfo;
@@ -226,8 +240,9 @@
@Override
@CallSuper
public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) {
- ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onTaskInfoChanged: taskId=%d taskAct=%s",
- taskInfo.taskId, taskInfo.baseActivity);
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onTaskInfoChanged: taskId=%d taskAct=%s "
+ + "stageId=%s",
+ taskInfo.taskId, taskInfo.baseActivity, stageTypeToString(mId));
mWindowDecorViewModel.ifPresent(viewModel -> viewModel.onTaskInfoChanged(taskInfo));
if (mRootTaskInfo.taskId == taskInfo.taskId) {
mRootTaskInfo = taskInfo;
@@ -257,7 +272,8 @@
@Override
@CallSuper
public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) {
- ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onTaskVanished: task=%d", taskInfo.taskId);
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onTaskVanished: task=%d stageId=%s",
+ taskInfo.taskId, stageTypeToString(mId));
final int taskId = taskInfo.taskId;
mWindowDecorViewModel.ifPresent(vm -> vm.onTaskVanished(taskInfo));
if (mRootTaskInfo.taskId == taskId) {
@@ -379,10 +395,9 @@
/** Collects all the current child tasks and prepares transaction to evict them to display. */
void evictAllChildren(WindowContainerTransaction wct) {
- ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "Evicting all children");
for (int i = mChildrenTaskInfo.size() - 1; i >= 0; i--) {
final ActivityManager.RunningTaskInfo taskInfo = mChildrenTaskInfo.valueAt(i);
- wct.reparent(taskInfo.token, null /* parent */, false /* onTop */);
+ evictChild(wct, taskInfo, "all");
}
}
@@ -390,13 +405,11 @@
for (int i = mChildrenTaskInfo.size() - 1; i >= 0; i--) {
final ActivityManager.RunningTaskInfo taskInfo = mChildrenTaskInfo.valueAt(i);
if (taskId == taskInfo.taskId) continue;
- ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "Evict other child: task=%d", taskId);
- wct.reparent(taskInfo.token, null /* parent */, false /* onTop */);
+ evictChild(wct, taskInfo, "other");
}
}
void evictNonOpeningChildren(RemoteAnimationTarget[] apps, WindowContainerTransaction wct) {
- ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "evictNonOpeningChildren");
final SparseArray<ActivityManager.RunningTaskInfo> toBeEvict = mChildrenTaskInfo.clone();
for (int i = 0; i < apps.length; i++) {
if (apps[i].mode == MODE_OPENING) {
@@ -405,8 +418,7 @@
}
for (int i = toBeEvict.size() - 1; i >= 0; i--) {
final ActivityManager.RunningTaskInfo taskInfo = toBeEvict.valueAt(i);
- ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "Evict non-opening child: task=%d", taskInfo.taskId);
- wct.reparent(taskInfo.token, null /* parent */, false /* onTop */);
+ evictChild(wct, taskInfo, "non-opening");
}
}
@@ -414,21 +426,30 @@
for (int i = mChildrenTaskInfo.size() - 1; i >= 0; i--) {
final ActivityManager.RunningTaskInfo taskInfo = mChildrenTaskInfo.valueAt(i);
if (!taskInfo.isVisible) {
- ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "Evict invisible child: task=%d",
- taskInfo.taskId);
- wct.reparent(taskInfo.token, null /* parent */, false /* onTop */);
+ evictChild(wct, taskInfo, "invisible");
}
}
}
- void evictChildren(WindowContainerTransaction wct, int taskId) {
- ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "Evict child: task=%d", taskId);
+ void evictChild(WindowContainerTransaction wct, int taskId, String reason) {
final ActivityManager.RunningTaskInfo taskInfo = mChildrenTaskInfo.get(taskId);
if (taskInfo != null) {
- wct.reparent(taskInfo.token, null /* parent */, false /* onTop */);
+ evictChild(wct, taskInfo, reason);
}
}
+ private void evictChild(@NonNull WindowContainerTransaction wct, @NonNull TaskInfo taskInfo,
+ @NonNull String reason) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "Evict child: task=%d reason=%s", taskInfo.taskId,
+ reason);
+ // We are reparenting the task, but not removing the task from mChildrenTaskInfo, so to
+ // prevent this task from being considered as a top task for the roots, we need to override
+ // the visibility of the soon-to-be-hidden task
+ taskInfo.isVisible = false;
+ taskInfo.isVisibleRequested = false;
+ wct.reparent(taskInfo.token, null /* parent */, false /* onTop */);
+ }
+
void reparentTopTask(WindowContainerTransaction wct) {
wct.reparentTasks(null /* currentParent */, mRootTaskInfo.token,
CONTROLLED_WINDOWING_MODES, CONTROLLED_ACTIVITY_TYPES,
@@ -457,14 +478,17 @@
}
void activate(WindowContainerTransaction wct, boolean includingTopTask) {
- if (mIsActive) return;
- ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "activate: includingTopTask=%b",
- includingTopTask);
+ if (mIsActive && !enableFlexibleSplit()) return;
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "activate: includingTopTask=%b stage=%s",
+ includingTopTask, stageTypeToString(mId));
if (includingTopTask) {
reparentTopTask(wct);
}
+ if (enableFlexibleSplit()) {
+ return;
+ }
mIsActive = true;
}
@@ -472,11 +496,14 @@
deactivate(wct, false /* toTop */);
}
- void deactivate(WindowContainerTransaction wct, boolean toTop) {
- if (!mIsActive) return;
- ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "deactivate: toTop=%b rootTaskInfo=%s",
- toTop, mRootTaskInfo);
- mIsActive = false;
+ void deactivate(WindowContainerTransaction wct, boolean reparentTasksToTop) {
+ if (!mIsActive && !enableFlexibleSplit()) return;
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "deactivate: reparentTasksToTop=%b "
+ + "rootTaskInfo=%s stage=%s",
+ reparentTasksToTop, mRootTaskInfo, stageTypeToString(mId));
+ if (!enableFlexibleSplit()) {
+ mIsActive = false;
+ }
if (mRootTaskInfo == null) return;
final WindowContainerToken rootToken = mRootTaskInfo.token;
@@ -485,14 +512,15 @@
null /* newParent */,
null /* windowingModes */,
null /* activityTypes */,
- toTop);
+ reparentTasksToTop);
}
// --------
- // Previously only used in SideStage
+ // Previously only used in SideStage. With flexible split this is called for all stages
boolean removeAllTasks(WindowContainerTransaction wct, boolean toTop) {
- ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "remove all side stage tasks: childCount=%d toTop=%b",
- mChildrenTaskInfo.size(), toTop);
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "remove all side stage tasks: childCount=%d toTop=%b "
+ + " stageI=%s",
+ mChildrenTaskInfo.size(), toTop, stageTypeToString(mId));
if (mChildrenTaskInfo.size() == 0) return false;
wct.reparentTasks(
mRootTaskInfo.token,
@@ -513,6 +541,15 @@
}
@Override
+ public String toString() {
+ return "mId: " + stageTypeToString(mId)
+ + " mVisible: " + mVisible
+ + " mActive: " + mIsActive
+ + " mHasRootTask: " + mHasRootTask
+ + " childSize: " + mChildrenTaskInfo.size();
+ }
+
+ @Override
@CallSuper
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 17483dd..92d1f9c 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
@@ -370,7 +370,8 @@
if (mRecentsHandler != null) {
if (mSplitHandler.isSplitScreenVisible()) {
return this::setRecentsTransitionDuringSplit;
- } else if (mKeyguardHandler.isKeyguardShowing()) {
+ } else if (mKeyguardHandler.isKeyguardShowing()
+ && !mKeyguardHandler.isKeyguardAnimating()) {
return this::setRecentsTransitionDuringKeyguard;
} else if (mDesktopTasksController != null
// Check on the default display. Recents/gesture nav is only available there
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RecentsMixedTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RecentsMixedTransition.java
index fd4d568..8cdbe26 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RecentsMixedTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RecentsMixedTransition.java
@@ -117,6 +117,11 @@
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Mixed transition for Recents during"
+ " Keyguard #%d", info.getDebugId());
+ if (!mKeyguardHandler.isKeyguardShowing() || mKeyguardHandler.isKeyguardAnimating()) {
+ ProtoLog.w(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Cancel mixed transition because "
+ + "keyguard state was changed #%d", info.getDebugId());
+ return false;
+ }
if (mInfo == null) {
mInfo = info;
mFinishT = finishTransaction;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/animation/SplitTaskUnfoldAnimator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/animation/SplitTaskUnfoldAnimator.java
index d28287d..32f3cd8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/animation/SplitTaskUnfoldAnimator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/animation/SplitTaskUnfoldAnimator.java
@@ -334,8 +334,8 @@
// Sides adjacent to split bar or task bar are not be animated.
Insets margins;
- final boolean isLandscape = mRootStageBounds.width() > mRootStageBounds.height();
- if (isLandscape) { // Left and right splits.
+ final boolean isLeftRightSplit = mSplitScreenController.get().get().isLeftRightSplit();
+ if (isLeftRightSplit) {
margins = getLandscapeMargins(margin, taskbarExpanded);
} else { // Top and bottom splits.
margins = getPortraitMargins(margin, taskbarExpanded);
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 d71e61a..f89b0d1 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
@@ -78,7 +78,6 @@
import android.view.SurfaceControl.Transaction;
import android.view.View;
import android.view.ViewConfiguration;
-import android.widget.Toast;
import android.window.DesktopModeFlags;
import android.window.TaskSnapshot;
import android.window.WindowContainerToken;
@@ -572,9 +571,6 @@
if (decoration == null) {
return;
}
- mDesktopModeEventLogger.logTaskResizingStarted(resizeTrigger, motionEvent,
- decoration.mTaskInfo,
- mDisplayController, /* displayLayoutSize= */ null);
mInteractionJankMonitor.begin(
decoration.mTaskSurface, mContext, mMainHandler,
Cuj.CUJ_DESKTOP_MODE_MAXIMIZE_WINDOW, source);
@@ -593,33 +589,20 @@
decoration.closeMaximizeMenu();
}
- private void onSnapResize(int taskId, boolean left, MotionEvent motionEvent) {
+ public void onSnapResize(int taskId, boolean left, @Nullable MotionEvent motionEvent) {
final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(taskId);
if (decoration == null) {
return;
}
- if (!decoration.mTaskInfo.isResizeable
- && DesktopModeFlags.DISABLE_NON_RESIZABLE_APP_SNAP_RESIZE.isTrue()) {
- Toast.makeText(mContext,
- R.string.desktop_mode_non_resizable_snap_text, Toast.LENGTH_SHORT).show();
- } else {
- ResizeTrigger resizeTrigger =
- left ? ResizeTrigger.SNAP_LEFT_MENU : ResizeTrigger.SNAP_RIGHT_MENU;
- mDesktopModeEventLogger.logTaskResizingStarted(resizeTrigger, motionEvent,
- decoration.mTaskInfo,
- mDisplayController, /* displayLayoutSize= */ null);
- mInteractionJankMonitor.begin(decoration.mTaskSurface, mContext, mMainHandler,
- Cuj.CUJ_DESKTOP_MODE_SNAP_RESIZE, "maximize_menu_resizable");
- mDesktopTasksController.snapToHalfScreen(
- decoration.mTaskInfo,
- decoration.mTaskSurface,
- decoration.mTaskInfo.configuration.windowConfiguration.getBounds(),
- left ? SnapPosition.LEFT : SnapPosition.RIGHT,
- resizeTrigger,
- motionEvent,
- mWindowDecorByTaskId.get(taskId));
- }
+ mInteractionJankMonitor.begin(decoration.mTaskSurface, mContext, mMainHandler,
+ Cuj.CUJ_DESKTOP_MODE_SNAP_RESIZE, "maximize_menu_resizable");
+ mDesktopTasksController.handleInstantSnapResizingTask(
+ decoration.mTaskInfo,
+ left ? SnapPosition.LEFT : SnapPosition.RIGHT,
+ left ? ResizeTrigger.SNAP_LEFT_MENU : ResizeTrigger.SNAP_RIGHT_MENU,
+ motionEvent,
+ decoration);
decoration.closeHandleMenu();
decoration.closeMaximizeMenu();
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 cdcf14e..723bbd3 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
@@ -81,7 +81,6 @@
import android.window.WindowContainerTransaction;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.policy.ScreenDecorationsUtils;
import com.android.launcher3.icons.BaseIconFactory;
import com.android.launcher3.icons.IconProvider;
import com.android.window.flags.Flags;
@@ -207,7 +206,7 @@
private final WindowDecorCaptionHandleRepository mWindowDecorCaptionHandleRepository;
private final DesktopRepository mDesktopRepository;
- DesktopModeWindowDecoration(
+ public DesktopModeWindowDecoration(
Context context,
@NonNull Context userContext,
DisplayController displayController,
@@ -1008,8 +1007,10 @@
relayoutParams.mWindowDecorConfig = windowDecorConfig;
if (DesktopModeStatus.useRoundedCorners()) {
- relayoutParams.mCornerRadius =
- (int) ScreenDecorationsUtils.getWindowCornerRadius(context);
+ relayoutParams.mCornerRadius = taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM
+ ? loadDimensionPixelSize(context.getResources(),
+ R.dimen.desktop_windowing_freeform_rounded_corner_radius)
+ : INVALID_CORNER_RADIUS;
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt
index c98c6a3..456f2c0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt
@@ -238,8 +238,12 @@
val taskBounds = taskInfo.getConfiguration().windowConfiguration.bounds
updateGlobalMenuPosition(taskBounds, captionX, captionY)
if (layoutResId == R.layout.desktop_mode_app_header) {
- // Align the handle menu to the left side of the caption.
- menuX = marginMenuStart
+ // Align the handle menu to the start of the header.
+ menuX = if (context.isRtl()) {
+ taskBounds.width() - menuWidth - marginMenuStart
+ } else {
+ marginMenuStart
+ }
menuY = captionY + marginMenuTop
} else {
if (DesktopModeFlags.ENABLE_HANDLE_INPUT_FIX.isTrue()) {
@@ -261,10 +265,17 @@
val nonFreeformX = captionX + (captionWidth / 2) - (menuWidth / 2)
when {
taskInfo.isFreeform -> {
- globalMenuPosition.set(
- /* x = */ taskBounds.left + marginMenuStart,
- /* y = */ taskBounds.top + captionY + marginMenuTop
- )
+ if (context.isRtl()) {
+ globalMenuPosition.set(
+ /* x= */ taskBounds.right - menuWidth - marginMenuStart,
+ /* y= */ taskBounds.top + captionY + marginMenuTop
+ )
+ } else {
+ globalMenuPosition.set(
+ /* x= */ taskBounds.left + marginMenuStart,
+ /* y= */ taskBounds.top + captionY + marginMenuTop
+ )
+ }
}
taskInfo.isFullscreen -> {
globalMenuPosition.set(
@@ -430,6 +441,9 @@
return context.resources.getDimensionPixelSize(resourceId)
}
+ private fun Context.isRtl() =
+ resources.configuration.layoutDirection == View.LAYOUT_DIRECTION_RTL
+
fun close() {
handleMenuView?.animateCloseMenu {
handleMenuViewContainer?.releaseView()
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeMenu.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeMenu.kt
index 4bb1e7b..11a7cf8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeMenu.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeMenu.kt
@@ -70,6 +70,7 @@
import com.android.wm.shell.windowdecor.common.DecorThemeUtil
import com.android.wm.shell.windowdecor.common.OPACITY_12
import com.android.wm.shell.windowdecor.common.OPACITY_40
+import com.android.wm.shell.windowdecor.common.OPACITY_60
import com.android.wm.shell.windowdecor.common.withAlpha
import java.util.function.Supplier
@@ -310,8 +311,6 @@
.desktop_mode_maximize_menu_immersive_button_fill_padding)
private val maximizeFillPaddingDefault = context.resources.getDimensionPixelSize(R.dimen
.desktop_mode_maximize_menu_snap_and_maximize_buttons_fill_padding)
- private val maximizeFillPaddingBottom = context.resources.getDimensionPixelSize(R.dimen
- .desktop_mode_maximize_menu_snap_and_maximize_buttons_fill_padding_bottom)
private val maximizeRestoreFillPaddingVertical = context.resources.getDimensionPixelSize(
R.dimen.desktop_mode_maximize_menu_restore_button_fill_vertical_padding)
private val maximizeRestoreFillPaddingHorizontal = context.resources.getDimensionPixelSize(
@@ -320,7 +319,7 @@
maximizeFillPaddingDefault,
maximizeFillPaddingDefault,
maximizeFillPaddingDefault,
- maximizeFillPaddingBottom
+ maximizeFillPaddingDefault
)
private val maximizeRestoreFillPaddingRect = Rect(
maximizeRestoreFillPaddingHorizontal,
@@ -684,7 +683,7 @@
inactiveSnapSideColor = colorScheme.outlineVariant.toArgb(),
semiActiveSnapSideColor = colorScheme.primary.toArgb().withAlpha(OPACITY_40),
activeSnapSideColor = colorScheme.primary.toArgb(),
- inactiveStrokeColor = colorScheme.outlineVariant.toArgb(),
+ inactiveStrokeColor = colorScheme.outlineVariant.toArgb().withAlpha(OPACITY_60),
activeStrokeColor = colorScheme.primary.toArgb(),
inactiveBackgroundColor = menuBackgroundColor,
activeBackgroundColor = colorScheme.primary.toArgb().withAlpha(OPACITY_12)
@@ -753,7 +752,8 @@
val activeStrokeAndFill = colorScheme.primary.toArgb()
val activeBackground = colorScheme.primary.toArgb().withAlpha(OPACITY_12)
val activeDrawable = createMaximizeOrImmersiveButtonDrawable(
- strokeAndFillColor = activeStrokeAndFill,
+ strokeColor = activeStrokeAndFill,
+ fillColor = activeStrokeAndFill,
backgroundColor = activeBackground,
// Add a mask with the menu background's color because the active background color is
// semi transparent, otherwise the transparency will reveal the stroke/fill color
@@ -770,7 +770,8 @@
addState(
StateSet.WILD_CARD,
createMaximizeOrImmersiveButtonDrawable(
- strokeAndFillColor = colorScheme.outlineVariant.toArgb(),
+ strokeColor = colorScheme.outlineVariant.toArgb().withAlpha(OPACITY_60),
+ fillColor = colorScheme.outlineVariant.toArgb(),
backgroundColor = colorScheme.surfaceContainerLow.toArgb(),
backgroundMask = null, // not needed because the bg color is fully opaque
fillPadding = fillPadding,
@@ -780,7 +781,8 @@
}
private fun createMaximizeOrImmersiveButtonDrawable(
- @ColorInt strokeAndFillColor: Int,
+ @ColorInt strokeColor: Int,
+ @ColorInt fillColor: Int,
@ColorInt backgroundColor: Int,
@ColorInt backgroundMask: Int?,
fillPadding: Rect,
@@ -794,7 +796,7 @@
null /* inset */,
null /* innerRadii */
)
- paint.color = strokeAndFillColor
+ paint.color = strokeColor
paint.style = Paint.Style.FILL
})
// Second layer, a mask for the next (background) layer if needed because of
@@ -829,7 +831,7 @@
null /* inset */,
null /* innerRadii */
)
- paint.color = strokeAndFillColor
+ paint.color = fillColor
paint.style = Paint.Style.FILL
})
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 a3c75bf..852eee5f 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
@@ -107,6 +107,11 @@
static final int INPUT_SINK_Z_ORDER = -2;
/**
+ * Invalid corner radius that signifies that corner radius should not be set.
+ */
+ static final int INVALID_CORNER_RADIUS = -1;
+
+ /**
* System-wide context. Only used to create context with overridden configurations.
*/
final Context mContext;
@@ -449,20 +454,22 @@
startT.show(mTaskSurface);
}
- if (mTaskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) {
- if (!DesktopModeStatus.isVeiledResizeEnabled()) {
- // When fluid resize is enabled, add a background to freeform tasks
- int backgroundColorInt = mTaskInfo.taskDescription.getBackgroundColor();
- mTmpColor[0] = (float) Color.red(backgroundColorInt) / 255.f;
- mTmpColor[1] = (float) Color.green(backgroundColorInt) / 255.f;
- mTmpColor[2] = (float) Color.blue(backgroundColorInt) / 255.f;
- startT.setColor(mTaskSurface, mTmpColor);
- }
- startT.setCornerRadius(mTaskSurface, params.mCornerRadius);
- finishT.setCornerRadius(mTaskSurface, params.mCornerRadius);
+ if (mTaskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM
+ && !DesktopModeStatus.isVeiledResizeEnabled()) {
+ // When fluid resize is enabled, add a background to freeform tasks
+ int backgroundColorInt = mTaskInfo.taskDescription.getBackgroundColor();
+ mTmpColor[0] = (float) Color.red(backgroundColorInt) / 255.f;
+ mTmpColor[1] = (float) Color.green(backgroundColorInt) / 255.f;
+ mTmpColor[2] = (float) Color.blue(backgroundColorInt) / 255.f;
+ startT.setColor(mTaskSurface, mTmpColor);
} else if (!DesktopModeStatus.isVeiledResizeEnabled()) {
startT.unsetColor(mTaskSurface);
}
+
+ if (params.mCornerRadius != INVALID_CORNER_RADIUS) {
+ startT.setCornerRadius(mTaskSurface, params.mCornerRadius);
+ finishT.setCornerRadius(mTaskSurface, params.mCornerRadius);
+ }
}
/**
@@ -593,13 +600,25 @@
private Rect calculateBoundingRectLocal(@NonNull OccludingCaptionElement element,
int elementWidthPx, @NonNull Rect captionRect) {
+ final boolean isRtl =
+ mDecorWindowContext.getResources().getConfiguration().getLayoutDirection()
+ == View.LAYOUT_DIRECTION_RTL;
switch (element.mAlignment) {
case START -> {
- return new Rect(0, 0, elementWidthPx, captionRect.height());
+ if (isRtl) {
+ return new Rect(captionRect.width() - elementWidthPx, 0,
+ captionRect.width(), captionRect.height());
+ } else {
+ return new Rect(0, 0, elementWidthPx, captionRect.height());
+ }
}
case END -> {
- return new Rect(captionRect.width() - elementWidthPx, 0,
- captionRect.width(), captionRect.height());
+ if (isRtl) {
+ return new Rect(0, 0, elementWidthPx, captionRect.height());
+ } else {
+ return new Rect(captionRect.width() - elementWidthPx, 0,
+ captionRect.width(), captionRect.height());
+ }
}
}
throw new IllegalArgumentException("Unexpected alignment " + element.mAlignment);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/ThemeUtils.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/ThemeUtils.kt
index f7cfbfa..c5057aa 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/ThemeUtils.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/ThemeUtils.kt
@@ -52,6 +52,7 @@
const val OPACITY_15 = 38
const val OPACITY_40 = 102
const val OPACITY_55 = 140
+const val OPACITY_60 = 153
const val OPACITY_65 = 166
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt
index 503ad92..5f25f42 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt
@@ -114,7 +114,7 @@
// If handle is not in status bar region(i.e., bottom stage in vertical split),
// do not create an input layer
if (position.y >= SystemBarUtils.getStatusBarHeight(context)) return
- if (!isCaptionVisible && statusBarInputLayerExists) {
+ if (!isCaptionVisible) {
disposeStatusBarInputLayer()
return
}
@@ -227,6 +227,7 @@
* is not visible.
*/
fun disposeStatusBarInputLayer() {
+ if (!statusBarInputLayerExists) return
statusBarInputLayerExists = false
handler.post {
statusBarInputLayer?.releaseView()
diff --git a/libs/WindowManager/Shell/tests/OWNERS b/libs/WindowManager/Shell/tests/OWNERS
index 65e50f8..19829e7 100644
--- a/libs/WindowManager/Shell/tests/OWNERS
+++ b/libs/WindowManager/Shell/tests/OWNERS
@@ -6,6 +6,7 @@
lbill@google.com
madym@google.com
hwwang@google.com
+gabiyev@google.com
chenghsiuchang@google.com
atsjenk@google.com
jorgegil@google.com
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/BringDesktopAppsToFrontLandscape.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/BringDesktopAppsToFrontLandscape.kt
new file mode 100644
index 0000000..898964f
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/BringDesktopAppsToFrontLandscape.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.wm.shell.flicker
+
+import android.tools.Rotation.ROTATION_90
+import android.tools.flicker.FlickerConfig
+import android.tools.flicker.annotation.ExpectedScenarios
+import android.tools.flicker.annotation.FlickerConfigProvider
+import android.tools.flicker.config.FlickerConfig
+import android.tools.flicker.config.FlickerServiceConfig
+import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.BRING_APPS_TO_FRONT
+import com.android.wm.shell.scenarios.BringDesktopAppsToFront
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Bring apps to front by clicking on the App Header.
+ *
+ * Assert that the app windows move to front.
+ */
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class BringDesktopAppsToFrontLandscape : BringDesktopAppsToFront(rotation = ROTATION_90) {
+
+ @ExpectedScenarios(["BRING_APPS_TO_FRONT"])
+ @Test
+ override fun bringDesktopAppsToFront() = super.bringDesktopAppsToFront()
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig()
+ .use(FlickerServiceConfig.DEFAULT)
+ .use(BRING_APPS_TO_FRONT)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/BringDesktopAppsToFrontPortrait.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/BringDesktopAppsToFrontPortrait.kt
new file mode 100644
index 0000000..b123d7d
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/BringDesktopAppsToFrontPortrait.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.wm.shell.flicker
+
+import android.tools.Rotation.ROTATION_0
+import android.tools.flicker.FlickerConfig
+import android.tools.flicker.annotation.ExpectedScenarios
+import android.tools.flicker.annotation.FlickerConfigProvider
+import android.tools.flicker.config.FlickerConfig
+import android.tools.flicker.config.FlickerServiceConfig
+import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.BRING_APPS_TO_FRONT
+import com.android.wm.shell.scenarios.BringDesktopAppsToFront
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Bring apps to front by clicking on the App Header.
+ *
+ * Assert that the app windows move to front.
+ */
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class BringDesktopAppsToFrontPortrait : BringDesktopAppsToFront(rotation = ROTATION_0) {
+
+ @ExpectedScenarios(["BRING_APPS_TO_FRONT"])
+ @Test
+ override fun bringDesktopAppsToFront() = super.bringDesktopAppsToFront()
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig()
+ .use(FlickerServiceConfig.DEFAULT)
+ .use(BRING_APPS_TO_FRONT)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/DesktopModeFlickerScenarios.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/DesktopModeFlickerScenarios.kt
index 4cddf31..0cc8b0c 100644
--- a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/DesktopModeFlickerScenarios.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/DesktopModeFlickerScenarios.kt
@@ -25,6 +25,7 @@
import android.tools.flicker.assertors.assertions.AppLayerIsVisibleAtStart
import android.tools.flicker.assertors.assertions.AppWindowAlignsWithOnlyOneDisplayCornerAtEnd
import android.tools.flicker.assertors.assertions.AppWindowBecomesInvisible
+import android.tools.flicker.assertors.assertions.AppWindowBecomesTopWindow
import android.tools.flicker.assertors.assertions.AppWindowBecomesVisible
import android.tools.flicker.assertors.assertions.AppWindowCoversLeftHalfScreenAtEnd
import android.tools.flicker.assertors.assertions.AppWindowCoversRightHalfScreenAtEnd
@@ -49,6 +50,7 @@
import android.tools.flicker.config.desktopmode.Components.DESKTOP_MODE_APP
import android.tools.flicker.config.desktopmode.Components.DESKTOP_WALLPAPER
import android.tools.flicker.config.desktopmode.Components.NON_RESIZABLE_APP
+import android.tools.flicker.config.desktopmode.Components.SIMPLE_APP
import android.tools.flicker.extractors.ITransitionMatcher
import android.tools.flicker.extractors.ShellTransitionScenarioExtractor
import android.tools.flicker.extractors.TaggedCujTransitionMatcher
@@ -345,6 +347,30 @@
).associateBy({ it }, { AssertionInvocationGroup.BLOCKING }),
)
+ val BRING_APPS_TO_FRONT =
+ FlickerConfigEntry(
+ scenarioId = ScenarioId("BRING_APPS_TO_FRONT"),
+ extractor =
+ ShellTransitionScenarioExtractor(
+ transitionMatcher =
+ object : ITransitionMatcher {
+ override fun findAll(
+ transitions: Collection<Transition>
+ ): Collection<Transition> {
+ return transitions.filter {
+ it.type == TransitionType.TO_FRONT
+ }
+ }
+ }
+ ),
+ assertions =
+ AssertionTemplates.COMMON_ASSERTIONS +
+ listOf(
+ AppWindowBecomesTopWindow(DESKTOP_MODE_APP),
+ AppWindowOnTopAtEnd(DESKTOP_MODE_APP),
+ ).associateBy({ it }, { AssertionInvocationGroup.BLOCKING })
+ )
+
val CASCADE_APP =
FlickerConfigEntry(
scenarioId = ScenarioId("CASCADE_APP"),
@@ -419,5 +445,25 @@
AppWindowOnTopAtEnd(LAUNCHER),
).associateBy({ it }, { AssertionInvocationGroup.BLOCKING })
)
+ val OPEN_UNLIMITED_APPS =
+ FlickerConfigEntry(
+ scenarioId = ScenarioId("OPEN_UNLIMITED_APPS"),
+ extractor =
+ ShellTransitionScenarioExtractor(
+ transitionMatcher =
+ object : ITransitionMatcher {
+ override fun findAll(
+ transitions: Collection<Transition>
+ ): Collection<Transition> {
+ return transitions.filter { it.type == TransitionType.OPEN }
+ }
+ }
+ ),
+ assertions =
+ listOf(
+ AppWindowBecomesVisible(DESKTOP_MODE_APP),
+ AppWindowIsVisibleAlways(SIMPLE_APP)
+ ).associateBy({ it }, { AssertionInvocationGroup.BLOCKING }),
+ )
}
}
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/MaximizeAppWindowWithDragToTopDragZoneLandscape.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/MaximizeAppWindowWithDragToTopDragZoneLandscape.kt
new file mode 100644
index 0000000..e1120bd
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/MaximizeAppWindowWithDragToTopDragZoneLandscape.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker
+
+import android.tools.Rotation.ROTATION_90
+import android.tools.flicker.FlickerConfig
+import android.tools.flicker.annotation.ExpectedScenarios
+import android.tools.flicker.annotation.FlickerConfigProvider
+import android.tools.flicker.config.FlickerConfig
+import android.tools.flicker.config.FlickerServiceConfig
+import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.MAXIMIZE_APP
+import com.android.wm.shell.scenarios.MaximizeAppWindowWithDragToTopDragZone
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Maximize app window by dragging it to the top drag zone.
+ *
+ * Assert that the app window keeps the same increases in size, filling the vertical and horizontal
+ * stable display bounds.
+ */
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class MaximizeAppWindowWithDragToTopDragZoneLandscape : MaximizeAppWindowWithDragToTopDragZone(
+ rotation = ROTATION_90
+) {
+ @ExpectedScenarios(["MAXIMIZE_APP"])
+ @Test
+ override fun maximizeAppWithDragToTopDragZone() = super.maximizeAppWithDragToTopDragZone()
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig().use(FlickerServiceConfig.DEFAULT).use(MAXIMIZE_APP)
+ }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/MaximizeAppWindowWithDragToTopDragZonePortrait.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/MaximizeAppWindowWithDragToTopDragZonePortrait.kt
new file mode 100644
index 0000000..fb910c7
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/MaximizeAppWindowWithDragToTopDragZonePortrait.kt
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker
+
+import android.tools.flicker.FlickerConfig
+import android.tools.flicker.annotation.ExpectedScenarios
+import android.tools.flicker.annotation.FlickerConfigProvider
+import android.tools.flicker.config.FlickerConfig
+import android.tools.flicker.config.FlickerServiceConfig
+import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.MAXIMIZE_APP
+import com.android.wm.shell.scenarios.MaximizeAppWindowWithDragToTopDragZone
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Maximize app window by dragging it to the top drag zone.
+ *
+ * Assert that the app window keeps the same increases in size, filling the vertical and horizontal
+ * stable display bounds.
+ */
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class MaximizeAppWindowWithDragToTopDragZonePortrait : MaximizeAppWindowWithDragToTopDragZone() {
+ @ExpectedScenarios(["MAXIMIZE_APP"])
+ @Test
+ override fun maximizeAppWithDragToTopDragZone() = super.maximizeAppWithDragToTopDragZone()
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig().use(FlickerServiceConfig.DEFAULT).use(MAXIMIZE_APP)
+ }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/OpenUnlimitedApps.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/OpenUnlimitedApps.kt
new file mode 100644
index 0000000..0a39846
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/OpenUnlimitedApps.kt
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker
+
+import android.tools.flicker.FlickerConfig
+import android.tools.flicker.annotation.ExpectedScenarios
+import android.tools.flicker.annotation.FlickerConfigProvider
+import android.tools.flicker.config.FlickerConfig
+import android.tools.flicker.config.FlickerServiceConfig
+import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.OPEN_UNLIMITED_APPS
+import com.android.wm.shell.scenarios.OpenUnlimitedApps
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Open many apps on the device without the window limit.
+ *
+ * Assert that the desktop task limit is not triggered.
+ */
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class OpenUnlimitedApps : OpenUnlimitedApps() {
+ @ExpectedScenarios(["OPEN_UNLIMITED_APPS"])
+ @Test
+ override fun openUnlimitedApps() = super.openUnlimitedApps()
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig().use(FlickerServiceConfig.DEFAULT).use(OPEN_UNLIMITED_APPS)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/BringDesktopAppsToFrontTest.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/BringDesktopAppsToFrontTest.kt
new file mode 100644
index 0000000..6c8cc68
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/BringDesktopAppsToFrontTest.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.wm.shell.functional
+
+import android.platform.test.annotations.Postsubmit
+import com.android.wm.shell.scenarios.BringDesktopAppsToFront
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
+
+/** Functional test for [BringDesktopAppsToFront]. */
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+class BringDesktopAppsToFrontTest : BringDesktopAppsToFront()
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/MinimizeAutoPipAppWindowTest.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/MinimizeAutoPipAppWindowTest.kt
new file mode 100644
index 0000000..48befc0
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/MinimizeAutoPipAppWindowTest.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.functional
+
+import android.platform.test.annotations.Postsubmit
+import com.android.wm.shell.scenarios.MinimizeAutoPipAppWindow
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
+
+/* Functional test for [MinimizeAutoPipAppWindow]. */
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+class MinimizeAutoPipAppWindowTest : MinimizeAutoPipAppWindow()
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/OpenUnlimitedAppsTest.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/OpenUnlimitedAppsTest.kt
new file mode 100644
index 0000000..9462f15
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/OpenUnlimitedAppsTest.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.functional
+
+import android.platform.test.annotations.Postsubmit
+import com.android.wm.shell.scenarios.OpenUnlimitedApps
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
+
+/* Functional test for [OpenUnlimitedApps]. */
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+class OpenUnlimitedAppsTest : OpenUnlimitedApps()
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/BringDesktopAppsToFront.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/BringDesktopAppsToFront.kt
new file mode 100644
index 0000000..1db22eb
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/BringDesktopAppsToFront.kt
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.wm.shell.scenarios
+
+import android.tools.NavBar
+import android.tools.Rotation
+import android.tools.flicker.rules.ChangeDisplayOrientationRule
+import com.android.server.wm.flicker.helpers.DesktopModeAppHelper
+import com.android.server.wm.flicker.helpers.MailAppHelper
+import com.android.window.flags.Flags
+import com.android.wm.shell.Utils
+import org.junit.After
+import org.junit.Assume
+import org.junit.Before
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+
+@Ignore("Test Base Class")
+abstract class BringDesktopAppsToFront(
+ val rotation: Rotation = Rotation.ROTATION_0,
+ isResizable: Boolean = true,
+ isLandscapeApp: Boolean = true,
+) : DesktopScenarioCustomAppTestBase(isResizable, isLandscapeApp) {
+
+ private val mailApp = DesktopModeAppHelper(MailAppHelper(instrumentation))
+
+ @Rule
+ @JvmField val testSetupRule = Utils.testSetupRule(NavBar.MODE_GESTURAL, rotation)
+
+ @Before
+ fun setup() {
+ Assume.assumeTrue(Flags.enableDesktopWindowingMode() && tapl.isTablet)
+ tapl.setEnableRotation(true)
+ tapl.setExpectedRotation(rotation.value)
+ ChangeDisplayOrientationRule.setRotation(rotation)
+ tapl.enableTransientTaskbar(false)
+ // Launch a first app and snap it to left side so that it doesn't overlap too much with
+ // the next launching app, and their headers are visible enough to switch focus by tapping
+ // on them.
+ testApp.enterDesktopMode(wmHelper, device)
+ testApp.snapResizeDesktopApp(wmHelper, device, instrumentation.context, toLeft = true)
+ mailApp.launchViaIntent(wmHelper)
+ }
+
+ @Test
+ open fun bringDesktopAppsToFront() {
+ testApp.bringToFront(wmHelper, device)
+ mailApp.bringToFront(wmHelper, device)
+ testApp.bringToFront(wmHelper, device)
+ mailApp.bringToFront(wmHelper, device)
+ }
+
+ @After
+ fun teardown() {
+ mailApp.exit(wmHelper)
+ testApp.exit(wmHelper)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/CloseAllAppsWithAppHeaderExit.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/CloseAllAppsWithAppHeaderExit.kt
index 351a700..f9bf49e 100644
--- a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/CloseAllAppsWithAppHeaderExit.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/CloseAllAppsWithAppHeaderExit.kt
@@ -57,7 +57,7 @@
Assume.assumeTrue(Flags.enableDesktopWindowingMode() && tapl.isTablet)
tapl.setEnableRotation(true)
tapl.setExpectedRotation(rotation.value)
- testApp.enterDesktopWithDrag(wmHelper, device)
+ testApp.enterDesktopMode(wmHelper, device)
mailApp.launchViaIntent(wmHelper)
nonResizeableApp.launchViaIntent(wmHelper)
}
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/DragAppWindowMultiWindow.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/DragAppWindowMultiWindow.kt
index 3f9927f..16e5373 100644
--- a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/DragAppWindowMultiWindow.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/DragAppWindowMultiWindow.kt
@@ -40,7 +40,7 @@
@Before
fun setup() {
Assume.assumeTrue(Flags.enableDesktopWindowingMode() && tapl.isTablet)
- testApp.enterDesktopWithDrag(wmHelper, device)
+ testApp.enterDesktopMode(wmHelper, device)
mailApp.launchViaIntent(wmHelper)
newTasksApp.launchViaIntent(wmHelper)
imeApp.launchViaIntent(wmHelper)
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/DragAppWindowMultiWindowAndPip.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/DragAppWindowMultiWindowAndPip.kt
index 6d52a11..c43a575 100644
--- a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/DragAppWindowMultiWindowAndPip.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/DragAppWindowMultiWindowAndPip.kt
@@ -47,7 +47,7 @@
Assume.assumeTrue(Flags.enableDesktopWindowingMode() && tapl.isTablet)
// Set string extra to ensure the app is on PiP mode at launch
pipApp.launchViaIntentAndWaitForPip(wmHelper, stringExtras = mapOf("enter_pip" to "true"))
- testApp.enterDesktopWithDrag(wmHelper, device)
+ testApp.enterDesktopMode(wmHelper, device)
mailApp.launchViaIntent(wmHelper)
newTasksApp.launchViaIntent(wmHelper)
imeApp.launchViaIntent(wmHelper)
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/DragAppWindowSingleWindow.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/DragAppWindowSingleWindow.kt
index 91cfd17..786a8b7 100644
--- a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/DragAppWindowSingleWindow.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/DragAppWindowSingleWindow.kt
@@ -36,7 +36,7 @@
@Before
fun setup() {
Assume.assumeTrue(Flags.enableDesktopWindowingMode() && tapl.isTablet)
- testApp.enterDesktopWithDrag(wmHelper, device)
+ testApp.enterDesktopMode(wmHelper, device)
}
@Test
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/EnterDesktopWithDrag.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/EnterDesktopWithDrag.kt
index 967bd29..8d04749 100644
--- a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/EnterDesktopWithDrag.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/EnterDesktopWithDrag.kt
@@ -49,7 +49,8 @@
@Test
open fun enterDesktopWithDrag() {
- testApp.enterDesktopWithDrag(wmHelper, device)
+ // By default this method uses drag to desktop
+ testApp.enterDesktopMode(wmHelper, device)
}
@After
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ExitDesktopWithDragToTopDragZone.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ExitDesktopWithDragToTopDragZone.kt
index f442fdb..2800839 100644
--- a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ExitDesktopWithDragToTopDragZone.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ExitDesktopWithDragToTopDragZone.kt
@@ -46,7 +46,7 @@
instrumentation.context.resources.getBoolean(R.bool.config_dragToMaximizeInDesktopMode))
tapl.setEnableRotation(true)
tapl.setExpectedRotation(rotation.value)
- testApp.enterDesktopWithDrag(wmHelper, device)
+ testApp.enterDesktopMode(wmHelper, device)
}
@Test
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MaximiseAppWithCornerResize.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MaximiseAppWithCornerResize.kt
index 6637b01..5cf51e3 100644
--- a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MaximiseAppWithCornerResize.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MaximiseAppWithCornerResize.kt
@@ -66,7 +66,7 @@
tapl.setEnableRotation(true)
tapl.setExpectedRotation(rotation.value)
ChangeDisplayOrientationRule.setRotation(rotation)
- testApp.enterDesktopWithDrag(wmHelper, device)
+ testApp.enterDesktopMode(wmHelper, device)
testApp.cornerResize(
wmHelper,
device,
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MaximizeAppWindow.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MaximizeAppWindow.kt
index a54d497..d2be494 100644
--- a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MaximizeAppWindow.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MaximizeAppWindow.kt
@@ -58,7 +58,7 @@
tapl.setEnableRotation(true)
tapl.setExpectedRotation(rotation.value)
ChangeDisplayOrientationRule.setRotation(rotation)
- testApp.enterDesktopWithDrag(wmHelper, device)
+ testApp.enterDesktopMode(wmHelper, device)
}
@Test
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MaximizeAppWindowWithDragToTopDragZone.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MaximizeAppWindowWithDragToTopDragZone.kt
index a2b88f2..60a0fb5 100644
--- a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MaximizeAppWindowWithDragToTopDragZone.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MaximizeAppWindowWithDragToTopDragZone.kt
@@ -59,7 +59,7 @@
tapl.setEnableRotation(true)
tapl.setExpectedRotation(rotation.value)
ChangeDisplayOrientationRule.setRotation(rotation)
- testApp.enterDesktopWithDrag(wmHelper, device)
+ testApp.enterDesktopMode(wmHelper, device)
}
@Test
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MinimizeAppWindows.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MinimizeAppWindows.kt
index b548363..971637b 100644
--- a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MinimizeAppWindows.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MinimizeAppWindows.kt
@@ -61,7 +61,7 @@
tapl.setEnableRotation(true)
tapl.setExpectedRotation(rotation.value)
ChangeDisplayOrientationRule.setRotation(rotation)
- testApp1.enterDesktopWithDrag(wmHelper, device)
+ testApp1.enterDesktopMode(wmHelper, device)
testApp2.launchViaIntent(wmHelper)
testApp3.launchViaIntent(wmHelper)
}
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MinimizeAutoPipAppWindow.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MinimizeAutoPipAppWindow.kt
new file mode 100644
index 0000000..d6c3266
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MinimizeAutoPipAppWindow.kt
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.scenarios
+
+import android.app.Instrumentation
+import android.tools.NavBar
+import android.tools.Rotation
+import android.tools.traces.parsers.WindowManagerStateHelper
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
+import com.android.launcher3.tapl.LauncherInstrumentation
+import com.android.server.wm.flicker.helpers.DesktopModeAppHelper
+import com.android.server.wm.flicker.helpers.PipAppHelper
+import com.android.server.wm.flicker.helpers.SimpleAppHelper
+import com.android.window.flags.Flags
+import com.android.wm.shell.Utils
+import org.junit.After
+import org.junit.Assume
+import org.junit.Before
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+
+/** Base scenario test for minimizing the app entering pip on leave automatically */
+@Ignore("Test Base Class")
+abstract class MinimizeAutoPipAppWindow {
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val tapl = LauncherInstrumentation()
+ private val wmHelper = WindowManagerStateHelper(instrumentation)
+ private val device = UiDevice.getInstance(instrumentation)
+ private val testApp = DesktopModeAppHelper(SimpleAppHelper(instrumentation))
+ private val pipApp = PipAppHelper(instrumentation)
+ private val pipAppDesktopMode = DesktopModeAppHelper(pipApp)
+
+ @Rule
+ @JvmField
+ val testSetupRule = Utils.testSetupRule(NavBar.MODE_GESTURAL, Rotation.ROTATION_0)
+
+ @Before
+ fun setup() {
+ Assume.assumeTrue(Flags.enableDesktopWindowingMode() && tapl.isTablet)
+ Assume.assumeTrue(Flags.enableMinimizeButton())
+ testApp.enterDesktopMode(wmHelper, device)
+ pipApp.launchViaIntent(wmHelper)
+ pipApp.enableAutoEnterForPipActivity()
+ }
+
+ @Test
+ open fun minimizePipAppWindow() {
+ pipAppDesktopMode.minimizeDesktopApp(wmHelper, device, isPip = true)
+ }
+
+ @After
+ fun teardown() {
+ pipApp.exit(wmHelper)
+ testApp.exit(wmHelper)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MinimizeWindowOnAppOpen.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MinimizeWindowOnAppOpen.kt
index b86765e..a246326 100644
--- a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MinimizeWindowOnAppOpen.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MinimizeWindowOnAppOpen.kt
@@ -23,12 +23,10 @@
import androidx.test.uiautomator.UiDevice
import com.android.launcher3.tapl.LauncherInstrumentation
import com.android.server.wm.flicker.helpers.DesktopModeAppHelper
-import com.android.server.wm.flicker.helpers.ImeAppHelper
-import com.android.server.wm.flicker.helpers.LetterboxAppHelper
import com.android.server.wm.flicker.helpers.MailAppHelper
-import com.android.server.wm.flicker.helpers.NewTasksAppHelper
import com.android.server.wm.flicker.helpers.SimpleAppHelper
import com.android.window.flags.Flags
+import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
import org.junit.After
import org.junit.Assume
import org.junit.Before
@@ -51,32 +49,30 @@
private val testApp = DesktopModeAppHelper(SimpleAppHelper(instrumentation))
private val mailApp = DesktopModeAppHelper(MailAppHelper(instrumentation))
- private val newTasksApp = DesktopModeAppHelper(NewTasksAppHelper(instrumentation))
- private val imeApp = DesktopModeAppHelper(ImeAppHelper(instrumentation))
- private val letterboxAppHelper = DesktopModeAppHelper(LetterboxAppHelper(instrumentation))
+
+ private val maxNum = DesktopModeStatus.getMaxTaskLimit(instrumentation.context)
@Before
fun setup() {
Assume.assumeTrue(Flags.enableDesktopWindowingMode() && tapl.isTablet)
- testApp.enterDesktopWithDrag(wmHelper, device)
- mailApp.launchViaIntent(wmHelper)
- newTasksApp.launchViaIntent(wmHelper)
- imeApp.launchViaIntent(wmHelper)
+ Assume.assumeTrue(maxNum > 0)
+ testApp.enterDesktopMode(wmHelper, device)
+ // Launch new [maxNum-1] tasks, which ends up opening [maxNum] tasks in total.
+ for (i in 1..maxNum - 1) {
+ mailApp.launchViaIntent(wmHelper)
+ }
}
@Test
open fun openAppToMinimizeWindow() {
- // Launch a new app while 4 apps are already open on desktop. This should result in the
- // first app we opened to be minimized.
- letterboxAppHelper.launchViaIntent(wmHelper)
+ // Launch a new tasks, which ends up opening [maxNum]+1 tasks in total. This should
+ // result in the first app we opened to be minimized.
+ mailApp.launchViaIntent(wmHelper)
}
@After
fun teardown() {
testApp.exit(wmHelper)
mailApp.exit(wmHelper)
- newTasksApp.exit(wmHelper)
- imeApp.exit(wmHelper)
- letterboxAppHelper.exit(wmHelper)
}
}
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/OpenAppsInDesktopMode.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/OpenAppsInDesktopMode.kt
index aad266f..6ce36f5 100644
--- a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/OpenAppsInDesktopMode.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/OpenAppsInDesktopMode.kt
@@ -61,7 +61,7 @@
tapl.setExpectedRotation(rotation.value)
tapl.enableTransientTaskbar(false)
ChangeDisplayOrientationRule.setRotation(rotation)
- firstApp.enterDesktopWithDrag(wmHelper, device)
+ firstApp.enterDesktopMode(wmHelper, device)
}
@Test
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/OpenUnlimitedApps.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/OpenUnlimitedApps.kt
new file mode 100644
index 0000000..367c4a4
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/OpenUnlimitedApps.kt
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.scenarios
+
+import android.app.Instrumentation
+import android.tools.traces.parsers.WindowManagerStateHelper
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
+import com.android.launcher3.tapl.LauncherInstrumentation
+import com.android.server.wm.flicker.helpers.DesktopModeAppHelper
+import com.android.server.wm.flicker.helpers.MailAppHelper
+import com.android.server.wm.flicker.helpers.SimpleAppHelper
+import com.android.window.flags.Flags
+import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
+import org.junit.After
+import org.junit.Assume
+import org.junit.Before
+import org.junit.Ignore
+import org.junit.Test
+/**
+ * Base scenario test for opening many apps on the device without the window limit.
+ */
+@Ignore("Test Base Class")
+abstract class OpenUnlimitedApps()
+{
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val tapl = LauncherInstrumentation()
+ private val wmHelper = WindowManagerStateHelper(instrumentation)
+ private val device = UiDevice.getInstance(instrumentation)
+
+ private val testApp = DesktopModeAppHelper(SimpleAppHelper(instrumentation))
+ private val mailApp = DesktopModeAppHelper(MailAppHelper(instrumentation))
+
+ private val maxNum = DesktopModeStatus.getMaxTaskLimit(instrumentation.context)
+
+ @Before
+ fun setup() {
+ Assume.assumeTrue(Flags.enableDesktopWindowingMode() && tapl.isTablet)
+ Assume.assumeTrue(maxNum == 0)
+ testApp.enterDesktopMode(wmHelper, device)
+ }
+
+ @Test
+ open fun openUnlimitedApps() {
+ // The maximum number of active tasks is infinite. We here use 12 as a large enough number.
+ val openTaskNum = 12
+
+ // Launch new [openTaskNum] tasks.
+ for (i in 1..openTaskNum) {
+ mailApp.launchViaIntent(wmHelper)
+ }
+ }
+
+ @After
+ fun teardown() {
+ testApp.exit(wmHelper)
+ mailApp.exit(wmHelper)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppCornerMultiWindow.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppCornerMultiWindow.kt
index bfee318..eefa0bb 100644
--- a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppCornerMultiWindow.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppCornerMultiWindow.kt
@@ -61,7 +61,7 @@
Assume.assumeTrue(Flags.enableDesktopWindowingMode() && tapl.isTablet)
tapl.setEnableRotation(true)
tapl.setExpectedRotation(rotation.value)
- testApp.enterDesktopWithDrag(wmHelper, device)
+ testApp.enterDesktopMode(wmHelper, device)
mailApp.launchViaIntent(wmHelper)
newTasksApp.launchViaIntent(wmHelper)
imeApp.launchViaIntent(wmHelper)
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppCornerMultiWindowAndPip.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppCornerMultiWindowAndPip.kt
index 5b1b64e..0226eb3 100644
--- a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppCornerMultiWindowAndPip.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppCornerMultiWindowAndPip.kt
@@ -65,7 +65,7 @@
tapl.setExpectedRotation(rotation.value)
// Set string extra to ensure the app is on PiP mode at launch
pipApp.launchViaIntentAndWaitForPip(wmHelper, stringExtras = mapOf("enter_pip" to "true"))
- testApp.enterDesktopWithDrag(wmHelper, device)
+ testApp.enterDesktopMode(wmHelper, device)
mailApp.launchViaIntent(wmHelper)
newTasksApp.launchViaIntent(wmHelper)
imeApp.launchViaIntent(wmHelper)
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppWithCornerResize.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppWithCornerResize.kt
index a7cebf4..6463623 100644
--- a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppWithCornerResize.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppWithCornerResize.kt
@@ -67,7 +67,7 @@
tapl.setEnableRotation(true)
ChangeDisplayOrientationRule.setRotation(rotation)
tapl.setExpectedRotation(rotation.value)
- testApp.enterDesktopWithDrag(wmHelper, device)
+ testApp.enterDesktopMode(wmHelper, device)
}
@Test
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppWithEdgeResize.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppWithEdgeResize.kt
index 6780238..f198cfe 100644
--- a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppWithEdgeResize.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppWithEdgeResize.kt
@@ -60,7 +60,7 @@
)
tapl.setEnableRotation(true)
tapl.setExpectedRotation(rotation.value)
- testApp.enterDesktopWithDrag(wmHelper, device)
+ testApp.enterDesktopMode(wmHelper, device)
}
@Test
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/SnapResizeAppWindowWithButton.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/SnapResizeAppWindowWithButton.kt
index 2b40497..fd4c243 100644
--- a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/SnapResizeAppWindowWithButton.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/SnapResizeAppWindowWithButton.kt
@@ -56,7 +56,7 @@
@Before
fun setup() {
Assume.assumeTrue(Flags.enableDesktopWindowingMode() && tapl.isTablet)
- testApp.enterDesktopWithDrag(wmHelper, device)
+ testApp.enterDesktopMode(wmHelper, device)
}
@Test
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/SnapResizeAppWindowWithDrag.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/SnapResizeAppWindowWithDrag.kt
index b4bd7e1..62e860e 100644
--- a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/SnapResizeAppWindowWithDrag.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/SnapResizeAppWindowWithDrag.kt
@@ -56,7 +56,7 @@
@Before
fun setup() {
Assume.assumeTrue(Flags.enableDesktopWindowingMode() && tapl.isTablet)
- testApp.enterDesktopWithDrag(wmHelper, device)
+ testApp.enterDesktopMode(wmHelper, device)
}
@Test
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/StartAppMediaProjectionResizeAndDrag.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/StartAppMediaProjectionResizeAndDrag.kt
index f08e50e..de330e0 100644
--- a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/StartAppMediaProjectionResizeAndDrag.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/StartAppMediaProjectionResizeAndDrag.kt
@@ -59,7 +59,7 @@
Assume.assumeTrue(Flags.enableDesktopWindowingMode() && tapl.isTablet)
tapl.setEnableRotation(true)
tapl.setExpectedRotation(0)
- testApp.enterDesktopWithDrag(wmHelper, device)
+ testApp.enterDesktopMode(wmHelper, device)
}
@Test
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/StartAppMediaProjectionWithMaxDesktopWindows.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/StartAppMediaProjectionWithMaxDesktopWindows.kt
index ce235d4..4b3f15f 100644
--- a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/StartAppMediaProjectionWithMaxDesktopWindows.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/StartAppMediaProjectionWithMaxDesktopWindows.kt
@@ -67,7 +67,7 @@
Assume.assumeTrue(Flags.enableDesktopWindowingMode() && tapl.isTablet)
tapl.setEnableRotation(true)
tapl.setExpectedRotation(0)
- testApp.enterDesktopWithDrag(wmHelper, device)
+ testApp.enterDesktopMode(wmHelper, device)
}
@Test
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/StartScreenMediaProjectionWithMaxDesktopWindows.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/StartScreenMediaProjectionWithMaxDesktopWindows.kt
index 0051952..a108367 100644
--- a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/StartScreenMediaProjectionWithMaxDesktopWindows.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/StartScreenMediaProjectionWithMaxDesktopWindows.kt
@@ -62,7 +62,7 @@
@Before
fun setup() {
Assume.assumeTrue(Flags.enableDesktopWindowingMode() && tapl.isTablet)
- testApp.enterDesktopWithDrag(wmHelper, device)
+ testApp.enterDesktopMode(wmHelper, device)
}
@Test
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/SwitchToOverviewFromDesktop.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/SwitchToOverviewFromDesktop.kt
index dad2eb6..1455bd1 100644
--- a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/SwitchToOverviewFromDesktop.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/SwitchToOverviewFromDesktop.kt
@@ -54,7 +54,7 @@
@Before
fun setup() {
Assume.assumeTrue(Flags.enableDesktopWindowingMode() && tapl.isTablet)
- testApp.enterDesktopWithDrag(wmHelper, device)
+ testApp.enterDesktopMode(wmHelper, device)
}
@Test
diff --git a/libs/WindowManager/Shell/tests/e2e/mediaprojection/flicker-service/Android.bp b/libs/WindowManager/Shell/tests/e2e/mediaprojection/flicker-service/Android.bp
deleted file mode 100644
index 85e6a8d..0000000
--- a/libs/WindowManager/Shell/tests/e2e/mediaprojection/flicker-service/Android.bp
+++ /dev/null
@@ -1,38 +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 {
- // 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"],
-}
-
-android_test {
- name: "WMShellFlickerTestsMediaProjection",
- defaults: ["WMShellFlickerTestsDefault"],
- manifest: "AndroidManifest.xml",
- test_config_template: "AndroidTestTemplate.xml",
- srcs: ["src/**/*.kt"],
- static_libs: [
- "WMShellFlickerTestsBase",
- "WMShellScenariosMediaProjection",
- "WMShellTestUtils",
- ],
- data: ["trace_config/*"],
-}
diff --git a/libs/WindowManager/Shell/tests/e2e/mediaprojection/flicker-service/AndroidManifest.xml b/libs/WindowManager/Shell/tests/e2e/mediaprojection/flicker-service/AndroidManifest.xml
deleted file mode 100644
index 74b0daf..0000000
--- a/libs/WindowManager/Shell/tests/e2e/mediaprojection/flicker-service/AndroidManifest.xml
+++ /dev/null
@@ -1,85 +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.
- -->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- package="com.android.wm.shell.flicker">
-
- <uses-sdk android:minSdkVersion="29" android:targetSdkVersion="29"/>
- <!-- Read and write traces from external storage -->
- <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
- <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
- <!-- Allow the test to write directly to /sdcard/ -->
- <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
- <!-- Write secure settings -->
- <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
- <!-- Capture screen contents -->
- <uses-permission android:name="android.permission.ACCESS_SURFACE_FLINGER" />
- <!-- Enable / Disable tracing !-->
- <uses-permission android:name="android.permission.DUMP" />
- <!-- Run layers trace -->
- <uses-permission android:name="android.permission.HARDWARE_TEST"/>
- <!-- Capture screen recording -->
- <uses-permission android:name="android.permission.CAPTURE_VIDEO_OUTPUT"/>
- <!-- Workaround grant runtime permission exception from b/152733071 -->
- <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS"/>
- <uses-permission android:name="android.permission.READ_LOGS"/>
- <!-- Force-stop test apps -->
- <uses-permission android:name="android.permission.FORCE_STOP_PACKAGES"/>
- <!-- Control test app's media session -->
- <uses-permission android:name="android.permission.MEDIA_CONTENT_CONTROL"/>
- <!-- ATM.removeRootTasksWithActivityTypes() -->
- <uses-permission android:name="android.permission.MANAGE_ACTIVITY_TASKS" />
- <!-- Enable bubble notification-->
- <uses-permission android:name="android.permission.STATUS_BAR_SERVICE" />
- <!-- Allow the test to connect to perfetto trace processor -->
- <uses-permission android:name="android.permission.INTERNET"/>
- <uses-permission android:name="android.permission.READ_COMPAT_CHANGE_CONFIG" />
- <uses-permission android:name="android.permission.MANAGE_MEDIA_PROJECTION" />
-
- <!-- Allow the test to write directly to /sdcard/ and connect to trace processor -->
- <application android:requestLegacyExternalStorage="true"
- android:networkSecurityConfig="@xml/network_security_config"
- android:largeHeap="true">
- <uses-library android:name="android.test.runner"/>
-
- <service android:name=".NotificationListener"
- android:exported="true"
- android:label="WMShellTestsNotificationListenerService"
- android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
- <intent-filter>
- <action android:name="android.service.notification.NotificationListenerService" />
- </intent-filter>
- </service>
-
- <service android:name="com.android.wm.shell.flicker.utils.MediaProjectionService"
- android:foregroundServiceType="mediaProjection"
- android:label="WMShellTestsMediaProjectionService"
- android:enabled="true">
- </service>
-
- <!-- (b/197936012) Remove startup provider due to test timeout issue -->
- <provider
- android:name="androidx.startup.InitializationProvider"
- android:authorities="${applicationId}.androidx-startup"
- tools:node="remove" />
- </application>
-
- <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
- android:targetPackage="com.android.wm.shell.flicker"
- android:label="WindowManager Shell Flicker Tests">
- </instrumentation>
-</manifest>
diff --git a/libs/WindowManager/Shell/tests/e2e/mediaprojection/flicker-service/AndroidTestTemplate.xml b/libs/WindowManager/Shell/tests/e2e/mediaprojection/flicker-service/AndroidTestTemplate.xml
deleted file mode 100644
index 40dbbac..0000000
--- a/libs/WindowManager/Shell/tests/e2e/mediaprojection/flicker-service/AndroidTestTemplate.xml
+++ /dev/null
@@ -1,97 +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.
- -->
-<configuration description="Runs WindowManager Shell Flicker Tests {MODULE}">
- <option name="test-tag" value="FlickerTests"/>
- <!-- Needed for storing the perfetto trace files in the sdcard/test_results-->
- <option name="isolated-storage" value="false"/>
-
- <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
- <!-- disable DeprecatedTargetSdk warning -->
- <option name="run-command" value="setprop debug.wm.disable_deprecated_target_sdk_dialog 1"/>
- <!-- keeps the screen on during tests -->
- <option name="screen-always-on" value="on"/>
- <!-- prevents the phone from restarting -->
- <option name="force-skip-system-props" value="true"/>
- <!-- set WM tracing verbose level to all -->
- <option name="run-command" value="cmd window tracing level all"/>
- <!-- set WM tracing to frame (avoid incomplete states) -->
- <option name="run-command" value="cmd window tracing frame"/>
- <!-- disable betterbug as it's log collection dialogues cause flakes in e2e tests -->
- <option name="run-command" value="pm disable com.google.android.internal.betterbug"/>
- <!-- ensure lock screen mode is swipe -->
- <option name="run-command" value="locksettings set-disabled false"/>
- <!-- restart launcher to activate TAPL -->
- <option name="run-command"
- value="setprop ro.test_harness 1 ; am force-stop com.google.android.apps.nexuslauncher"/>
- <!-- Increase trace size: 20mb for WM and 80mb for SF -->
- <option name="run-command" value="cmd window tracing size 20480"/>
- <option name="run-command" value="su root service call SurfaceFlinger 1029 i32 81920"/>
- </target_preparer>
- <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
- <option name="test-user-token" value="%TEST_USER%"/>
- <option name="run-command" value="rm -rf /data/user/%TEST_USER%/files/*"/>
- <option name="run-command" value="settings put secure show_ime_with_hard_keyboard 1"/>
- <option name="run-command" value="settings put system show_touches 1"/>
- <option name="run-command" value="settings put system pointer_location 1"/>
- <option name="teardown-command"
- value="settings delete secure show_ime_with_hard_keyboard"/>
- <option name="teardown-command" value="settings delete system show_touches"/>
- <option name="teardown-command" value="settings delete system pointer_location"/>
- <option name="teardown-command"
- value="cmd overlay enable com.android.internal.systemui.navbar.gestural"/>
- </target_preparer>
- <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
- <option name="cleanup-apks" value="true"/>
- <option name="test-file-name" value="{MODULE}.apk"/>
- <option name="test-file-name" value="FlickerTestApp.apk"/>
- </target_preparer>
-
- <!-- Needed for pushing the trace config file -->
- <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
- <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
- <option name="push-file"
- key="trace_config.textproto"
- value="/data/misc/perfetto-traces/trace_config.textproto"
- />
- <!--Install the content provider automatically when we push some file in sdcard folder.-->
- <!--Needed to avoid the installation during the test suite.-->
- <option name="push-file" key="trace_config.textproto" value="/sdcard/sample.textproto"/>
- </target_preparer>
- <test class="com.android.tradefed.testtype.AndroidJUnitTest">
- <option name="package" value="{PACKAGE}"/>
- <option name="shell-timeout" value="6600s"/>
- <option name="test-timeout" value="6000s"/>
- <option name="hidden-api-checks" value="false"/>
- <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"
- key="perfetto_config_file"
- value="trace_config.textproto"
- />
- <option name="instrumentation-arg" key="per_run" value="true"/>
- <option name="instrumentation-arg" key="perfetto_persist_pid_track" value="true"/>
- </test>
- <!-- Needed for pulling the collected trace config on to the host -->
- <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
- <option name="pull-pattern-keys" value="perfetto_file_path"/>
- <option name="directory-keys"
- value="/data/user/0/com.android.wm.shell.flicker/files"/>
- <option name="collect-on-run-ended-only" value="true"/>
- <option name="clean-up" value="true"/>
- </metrics_collector>
-</configuration>
diff --git a/libs/WindowManager/Shell/tests/e2e/mediaprojection/flicker-service/trace_config/trace_config.textproto b/libs/WindowManager/Shell/tests/e2e/mediaprojection/flicker-service/trace_config/trace_config.textproto
deleted file mode 100644
index 9f2e497..0000000
--- a/libs/WindowManager/Shell/tests/e2e/mediaprojection/flicker-service/trace_config/trace_config.textproto
+++ /dev/null
@@ -1,71 +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.
-
-# proto-message: TraceConfig
-
-# Enable periodic flushing of the trace buffer into the output file.
-write_into_file: true
-
-# Writes the userspace buffer into the file every 1s.
-file_write_period_ms: 2500
-
-# See b/126487238 - we need to guarantee ordering of events.
-flush_period_ms: 30000
-
-# The trace buffers needs to be big enough to hold |file_write_period_ms| of
-# trace data. The trace buffer sizing depends on the number of trace categories
-# enabled and the device activity.
-
-# RSS events
-buffers: {
- size_kb: 63488
- fill_policy: RING_BUFFER
-}
-
-data_sources {
- config {
- name: "linux.process_stats"
- target_buffer: 0
- # polled per-process memory counters and process/thread names.
- # If you don't want the polled counters, remove the "process_stats_config"
- # section, but keep the data source itself as it still provides on-demand
- # thread/process naming for ftrace data below.
- process_stats_config {
- scan_all_processes_on_start: true
- }
- }
-}
-
-data_sources: {
- config {
- name: "linux.ftrace"
- ftrace_config {
- ftrace_events: "ftrace/print"
- ftrace_events: "task/task_newtask"
- ftrace_events: "task/task_rename"
- atrace_categories: "ss"
- atrace_categories: "wm"
- atrace_categories: "am"
- atrace_categories: "aidl"
- atrace_categories: "input"
- atrace_categories: "binder_driver"
- atrace_categories: "sched_process_exit"
- atrace_apps: "com.android.server.wm.flicker.testapp"
- atrace_apps: "com.android.systemui"
- atrace_apps: "com.android.wm.shell.flicker.service"
- atrace_apps: "com.google.android.apps.nexuslauncher"
- }
- }
-}
-
diff --git a/libs/WindowManager/Shell/tests/e2e/mediaprojection/scenarios/src/com/android/wm/shell/functional/StartAppMediaProjectionFromSplitScreenTest.kt b/libs/WindowManager/Shell/tests/e2e/mediaprojection/scenarios/src/com/android/wm/shell/functional/StartAppMediaProjectionFromSplitScreenTest.kt
new file mode 100644
index 0000000..2b9772d
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/mediaprojection/scenarios/src/com/android/wm/shell/functional/StartAppMediaProjectionFromSplitScreenTest.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.functional
+
+import android.platform.test.annotations.Postsubmit
+import com.android.wm.shell.scenarios.StartAppMediaProjectionFromSplitScreen
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
+
+/* Functional test for [StartAppMediaProjectionFromSplitScreen]. */
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+class StartAppMediaProjectionFromSplitScreenTest() : StartAppMediaProjectionFromSplitScreen()
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/e2e/mediaprojection/scenarios/src/com/android/wm/shell/functional/StartAppMediaProjectionInSplitScreenTest.kt b/libs/WindowManager/Shell/tests/e2e/mediaprojection/scenarios/src/com/android/wm/shell/functional/StartAppMediaProjectionInSplitScreenTest.kt
new file mode 100644
index 0000000..e92297b
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/mediaprojection/scenarios/src/com/android/wm/shell/functional/StartAppMediaProjectionInSplitScreenTest.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.functional
+
+import android.platform.test.annotations.Postsubmit
+import com.android.wm.shell.scenarios.StartAppMediaProjectionInSplitScreen
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
+
+/* Functional test for [StartAppMediaProjectionInSplitScreen]. */
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+class StartAppMediaProjectionInSplitScreenTest() : StartAppMediaProjectionInSplitScreen()
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/e2e/mediaprojection/scenarios/src/com/android/wm/shell/functional/StartAppMediaProjectionTest.kt b/libs/WindowManager/Shell/tests/e2e/mediaprojection/scenarios/src/com/android/wm/shell/functional/StartAppMediaProjectionTest.kt
new file mode 100644
index 0000000..3f810759
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/mediaprojection/scenarios/src/com/android/wm/shell/functional/StartAppMediaProjectionTest.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.functional
+
+import android.platform.test.annotations.Postsubmit
+import com.android.wm.shell.scenarios.StartAppMediaProjection
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
+
+/* Functional test for [StartAppMediaProjection]. */
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+class StartAppMediaProjectionTest() : StartAppMediaProjection()
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/e2e/mediaprojection/scenarios/src/com/android/wm/shell/functional/StartAppMediaProjectionWithExtraIntentTest.kt b/libs/WindowManager/Shell/tests/e2e/mediaprojection/scenarios/src/com/android/wm/shell/functional/StartAppMediaProjectionWithExtraIntentTest.kt
new file mode 100644
index 0000000..1975cc7
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/mediaprojection/scenarios/src/com/android/wm/shell/functional/StartAppMediaProjectionWithExtraIntentTest.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.functional
+
+import android.platform.test.annotations.Postsubmit
+import com.android.wm.shell.scenarios.StartAppMediaProjectionWithExtraIntent
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
+
+/* Functional test for [StartAppMediaProjectionWithExtraIntent]. */
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+class StartAppMediaProjectionWithExtraIntentTest : StartAppMediaProjectionWithExtraIntent()
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/e2e/mediaprojection/scenarios/src/com/android/wm/shell/functional/StartRecentAppMediaProjectionFromSplitScreenTest.kt b/libs/WindowManager/Shell/tests/e2e/mediaprojection/scenarios/src/com/android/wm/shell/functional/StartRecentAppMediaProjectionFromSplitScreenTest.kt
new file mode 100644
index 0000000..943033c
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/mediaprojection/scenarios/src/com/android/wm/shell/functional/StartRecentAppMediaProjectionFromSplitScreenTest.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.functional
+
+import android.platform.test.annotations.Postsubmit
+import com.android.wm.shell.scenarios.StartRecentAppMediaProjectionFromSplitScreen
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
+
+/* Functional test for [StartRecentAppMediaProjectionFromSplitScreen]. */
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+class StartRecentAppMediaProjectionFromSplitScreenTest() : StartRecentAppMediaProjectionFromSplitScreen()
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/e2e/mediaprojection/scenarios/src/com/android/wm/shell/functional/StartRecentAppMediaProjectionInSplitScreenTest.kt b/libs/WindowManager/Shell/tests/e2e/mediaprojection/scenarios/src/com/android/wm/shell/functional/StartRecentAppMediaProjectionInSplitScreenTest.kt
new file mode 100644
index 0000000..6facfd5
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/mediaprojection/scenarios/src/com/android/wm/shell/functional/StartRecentAppMediaProjectionInSplitScreenTest.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.functional
+
+import android.platform.test.annotations.Postsubmit
+import com.android.wm.shell.scenarios.StartRecentAppMediaProjectionInSplitScreen
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
+
+/* Functional test for [StartRecentAppMediaProjectionInSplitScreen]. */
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+class StartRecentAppMediaProjectionInSplitScreenTest() : StartRecentAppMediaProjectionInSplitScreen()
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/e2e/mediaprojection/scenarios/src/com/android/wm/shell/functional/StartRecentAppMediaProjectionTest.kt b/libs/WindowManager/Shell/tests/e2e/mediaprojection/scenarios/src/com/android/wm/shell/functional/StartRecentAppMediaProjectionTest.kt
new file mode 100644
index 0000000..bab0905
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/mediaprojection/scenarios/src/com/android/wm/shell/functional/StartRecentAppMediaProjectionTest.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.functional
+
+import android.platform.test.annotations.Postsubmit
+import com.android.wm.shell.scenarios.StartRecentAppMediaProjection
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
+
+/* Functional test for [StartRecentAppMediaProjection]. */
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+class StartRecentAppMediaProjectionTest() : StartRecentAppMediaProjection()
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/e2e/mediaprojection/scenarios/src/com/android/wm/shell/scenarios/StartAppMediaProjection.kt b/libs/WindowManager/Shell/tests/e2e/mediaprojection/scenarios/src/com/android/wm/shell/scenarios/StartAppMediaProjection.kt
new file mode 100644
index 0000000..fe2c578
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/mediaprojection/scenarios/src/com/android/wm/shell/scenarios/StartAppMediaProjection.kt
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.scenarios
+
+import android.app.Instrumentation
+import android.platform.test.annotations.Postsubmit
+import android.tools.NavBar
+import android.tools.Rotation
+import android.tools.device.apphelpers.CalculatorAppHelper
+import android.tools.traces.parsers.WindowManagerStateHelper
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
+import com.android.launcher3.tapl.LauncherInstrumentation
+import com.android.server.wm.flicker.helpers.StartMediaProjectionAppHelper
+import com.android.wm.shell.Utils
+import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
+
+/**
+ * Test scenario which requests an a single-app MediaProjection session.
+ *
+ * This is for testing that the requested app is opened as expected upon selecting it from the app
+ * selector, so capture can proceed as expected.
+ */
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+open class StartAppMediaProjection {
+
+ val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ val tapl = LauncherInstrumentation()
+ val wmHelper = WindowManagerStateHelper(instrumentation)
+ val device = UiDevice.getInstance(instrumentation)
+
+ private val initialRotation = Rotation.ROTATION_0
+ private val targetApp = CalculatorAppHelper(instrumentation)
+ private val testApp = StartMediaProjectionAppHelper(instrumentation)
+
+ @Rule
+ @JvmField
+ val testSetupRule = Utils.testSetupRule(NavBar.MODE_GESTURAL, initialRotation)
+
+ @Before
+ fun setup() {
+ tapl.setEnableRotation(true)
+ tapl.setExpectedRotation(initialRotation.value)
+ testApp.launchViaIntent(wmHelper)
+ }
+
+ @Test
+ open fun startMediaProjection() {
+ testApp.startSingleAppMediaProjection(wmHelper, targetApp)
+ }
+
+ @After
+ fun teardown() {
+ testApp.exit(wmHelper)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/mediaprojection/scenarios/src/com/android/wm/shell/scenarios/StartAppMediaProjectionFromSplitScreen.kt b/libs/WindowManager/Shell/tests/e2e/mediaprojection/scenarios/src/com/android/wm/shell/scenarios/StartAppMediaProjectionFromSplitScreen.kt
new file mode 100644
index 0000000..3beece8
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/mediaprojection/scenarios/src/com/android/wm/shell/scenarios/StartAppMediaProjectionFromSplitScreen.kt
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.scenarios
+
+import android.app.Instrumentation
+import android.platform.test.annotations.Postsubmit
+import android.tools.NavBar
+import android.tools.Rotation
+import android.tools.device.apphelpers.CalculatorAppHelper
+import android.tools.traces.parsers.WindowManagerStateHelper
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
+import com.android.launcher3.tapl.LauncherInstrumentation
+import com.android.server.wm.flicker.helpers.SimpleAppHelper
+import com.android.server.wm.flicker.helpers.StartMediaProjectionAppHelper
+import com.android.wm.shell.Utils
+import com.android.wm.shell.flicker.utils.SplitScreenUtils
+import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
+
+/**
+ * Test scenario which requests an a single-app MediaProjection session, while the HOST app is in
+ * split screen
+ *
+ * This is for testing that the requested app is opened as expected upon selecting it from the app
+ * selector, so capture can proceed as expected.
+ */
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+open class StartAppMediaProjectionFromSplitScreen {
+
+ val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ val tapl = LauncherInstrumentation()
+ val wmHelper = WindowManagerStateHelper(instrumentation)
+ val device = UiDevice.getInstance(instrumentation)
+
+ private val initialRotation = Rotation.ROTATION_0
+ private val targetApp = CalculatorAppHelper(instrumentation)
+ private val simpleApp = SimpleAppHelper(instrumentation)
+ private val testApp = StartMediaProjectionAppHelper(instrumentation)
+
+ @Rule
+ @JvmField
+ val testSetupRule = Utils.testSetupRule(NavBar.MODE_GESTURAL, initialRotation)
+
+ @Before
+ fun setup() {
+ tapl.workspace.switchToOverview().dismissAllTasks()
+
+ tapl.setEnableRotation(true)
+ tapl.setExpectedRotation(initialRotation.value)
+ SplitScreenUtils.enterSplit(wmHelper, tapl, device, simpleApp, testApp, initialRotation)
+ SplitScreenUtils.waitForSplitComplete(wmHelper, simpleApp, testApp)
+ }
+
+ @Test
+ open fun startMediaProjection() {
+ testApp.startSingleAppMediaProjection(wmHelper, targetApp)
+ }
+
+ @After
+ fun teardown() {
+ testApp.exit(wmHelper)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/mediaprojection/scenarios/src/com/android/wm/shell/scenarios/StartAppMediaProjectionInSplitScreen.kt b/libs/WindowManager/Shell/tests/e2e/mediaprojection/scenarios/src/com/android/wm/shell/scenarios/StartAppMediaProjectionInSplitScreen.kt
new file mode 100644
index 0000000..d3186ae
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/mediaprojection/scenarios/src/com/android/wm/shell/scenarios/StartAppMediaProjectionInSplitScreen.kt
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.scenarios
+
+import android.app.Instrumentation
+import android.platform.test.annotations.Postsubmit
+import android.tools.NavBar
+import android.tools.Rotation
+import android.tools.device.apphelpers.CalculatorAppHelper
+import android.tools.traces.parsers.WindowManagerStateHelper
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
+import com.android.launcher3.tapl.LauncherInstrumentation
+import com.android.server.wm.flicker.helpers.StartMediaProjectionAppHelper
+import com.android.wm.shell.Utils
+import com.android.wm.shell.flicker.utils.SplitScreenUtils
+import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
+
+/**
+ * Test scenario which requests an a single-app MediaProjection session, while the TARGET app is in
+ * split screen (next to the host app)
+ *
+ * This is for testing that the split pair isn't broken, and capture still proceeds as expected
+ */
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+open class StartAppMediaProjectionInSplitScreen {
+
+ val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ val tapl = LauncherInstrumentation()
+ val wmHelper = WindowManagerStateHelper(instrumentation)
+ val device = UiDevice.getInstance(instrumentation)
+
+ private val initialRotation = Rotation.ROTATION_0
+ private val targetApp = CalculatorAppHelper(instrumentation)
+ private val testApp = StartMediaProjectionAppHelper(instrumentation)
+
+ @Rule
+ @JvmField
+ val testSetupRule = Utils.testSetupRule(NavBar.MODE_GESTURAL, initialRotation)
+
+ @Before
+ fun setup() {
+ tapl.workspace.switchToOverview().dismissAllTasks()
+ tapl.setEnableRotation(true)
+ tapl.setExpectedRotation(initialRotation.value)
+ SplitScreenUtils.enterSplit(wmHelper, tapl, device, targetApp, testApp, initialRotation)
+ }
+
+ @Test
+ open fun startMediaProjection() {
+ testApp.startSingleAppMediaProjection(wmHelper, targetApp)
+
+ wmHelper
+ .StateSyncBuilder()
+ .withAppTransitionIdle()
+ .withWindowSurfaceAppeared(targetApp)
+ .withWindowSurfaceAppeared(testApp)
+ .waitForAndVerify()
+ }
+
+ @After
+ fun teardown() {
+ testApp.exit(wmHelper)
+ targetApp.exit(wmHelper)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/mediaprojection/scenarios/src/com/android/wm/shell/scenarios/StartAppMediaProjectionWithExtraIntent.kt b/libs/WindowManager/Shell/tests/e2e/mediaprojection/scenarios/src/com/android/wm/shell/scenarios/StartAppMediaProjectionWithExtraIntent.kt
new file mode 100644
index 0000000..0b2a1ca
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/mediaprojection/scenarios/src/com/android/wm/shell/scenarios/StartAppMediaProjectionWithExtraIntent.kt
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.scenarios
+
+import android.app.Instrumentation
+import android.platform.test.annotations.Postsubmit
+import android.tools.NavBar
+import android.tools.Rotation
+import android.tools.device.apphelpers.CalculatorAppHelper
+import android.tools.traces.parsers.WindowManagerStateHelper
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
+import com.android.launcher3.tapl.LauncherInstrumentation
+import com.android.server.wm.flicker.helpers.StartMediaProjectionAppHelper
+import com.android.wm.shell.Utils
+import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
+
+/**
+ * Test scenario which requests an a single-app MediaProjection session but launches an intent to
+ * return to the home screen, before the intent for opening the requested app to capture.
+ *
+ * This is for testing that even if a different intent interrupts the process the launching the
+ * requested capture target, the MediaProjection process isn't interrupted and the device is still
+ * interactive.
+ */
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+open class StartAppMediaProjectionWithExtraIntent {
+
+ val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ val tapl = LauncherInstrumentation()
+ val wmHelper = WindowManagerStateHelper(instrumentation)
+ val device = UiDevice.getInstance(instrumentation)
+
+ private val initialRotation = Rotation.ROTATION_0
+ private val targetApp = CalculatorAppHelper(instrumentation)
+ private val testApp = StartMediaProjectionAppHelper(instrumentation)
+
+ @Rule
+ @JvmField
+ val testSetupRule = Utils.testSetupRule(NavBar.MODE_GESTURAL, initialRotation)
+
+ @Before
+ fun setup() {
+ tapl.setEnableRotation(true)
+ testApp.launchViaIntent(wmHelper)
+ }
+
+ @Test
+ open fun startMediaProjection() {
+ testApp.startSingleAppMediaProjectionWithExtraIntent(wmHelper, targetApp)
+
+ // Check we can still interact with device after
+ tapl.workspace.switchToAllApps().getAppIcon(targetApp.appName).launch(targetApp.packageName)
+
+ wmHelper
+ .StateSyncBuilder()
+ .withAppTransitionIdle()
+ .withWindowSurfaceAppeared(targetApp)
+ .waitForAndVerify()
+ }
+
+ @After
+ fun teardown() {
+ testApp.exit(wmHelper)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/mediaprojection/scenarios/src/com/android/wm/shell/scenarios/StartRecentAppMediaProjection.kt b/libs/WindowManager/Shell/tests/e2e/mediaprojection/scenarios/src/com/android/wm/shell/scenarios/StartRecentAppMediaProjection.kt
new file mode 100644
index 0000000..30e0e4a
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/mediaprojection/scenarios/src/com/android/wm/shell/scenarios/StartRecentAppMediaProjection.kt
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.scenarios
+
+import android.app.Instrumentation
+import android.platform.test.annotations.Postsubmit
+import android.tools.NavBar
+import android.tools.Rotation
+import android.tools.device.apphelpers.CalculatorAppHelper
+import android.tools.traces.parsers.WindowManagerStateHelper
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
+import com.android.launcher3.tapl.LauncherInstrumentation
+import com.android.server.wm.flicker.helpers.StartMediaProjectionAppHelper
+import com.android.wm.shell.Utils
+import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
+
+/**
+ * Test scenario which requests an a single-app MediaProjection session from recents.
+ *
+ * This is for testing that the app is started from recents and capture proceeds as expected.
+ */
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+open class StartRecentAppMediaProjection {
+
+ val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ val tapl = LauncherInstrumentation()
+ val wmHelper = WindowManagerStateHelper(instrumentation)
+ val device = UiDevice.getInstance(instrumentation)
+
+ private val initialRotation = Rotation.ROTATION_0
+ private val targetApp = CalculatorAppHelper(instrumentation)
+ private val testApp = StartMediaProjectionAppHelper(instrumentation)
+
+ @Rule
+ @JvmField
+ val testSetupRule = Utils.testSetupRule(NavBar.MODE_GESTURAL, initialRotation)
+
+ @Before
+ fun setup() {
+ tapl.setEnableRotation(true)
+ tapl.setExpectedRotation(initialRotation.value)
+ targetApp.open()
+ testApp.launchViaIntent(wmHelper)
+ }
+
+ @Test
+ open fun startMediaProjection() {
+ testApp.startSingleAppMediaProjectionFromRecents(wmHelper, targetApp)
+ }
+
+ @After
+ fun teardown() {
+ testApp.exit(wmHelper)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/mediaprojection/scenarios/src/com/android/wm/shell/scenarios/StartRecentAppMediaProjectionFromSplitScreen.kt b/libs/WindowManager/Shell/tests/e2e/mediaprojection/scenarios/src/com/android/wm/shell/scenarios/StartRecentAppMediaProjectionFromSplitScreen.kt
new file mode 100644
index 0000000..f1dcf1f
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/mediaprojection/scenarios/src/com/android/wm/shell/scenarios/StartRecentAppMediaProjectionFromSplitScreen.kt
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.scenarios
+
+import android.app.Instrumentation
+import android.platform.test.annotations.Postsubmit
+import android.tools.NavBar
+import android.tools.Rotation
+import android.tools.device.apphelpers.CalculatorAppHelper
+import android.tools.traces.parsers.WindowManagerStateHelper
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
+import com.android.launcher3.tapl.LauncherInstrumentation
+import com.android.server.wm.flicker.helpers.SimpleAppHelper
+import com.android.server.wm.flicker.helpers.StartMediaProjectionAppHelper
+import com.android.wm.shell.Utils
+import com.android.wm.shell.flicker.utils.SplitScreenUtils
+import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
+
+/**
+ * Test scenario which requests an a single-app MediaProjection session from recents while the HOST
+ * app is in split screen.
+ *
+ * This is for testing that the split pair isn't broken, and capture still proceeds as expected
+ */
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+open class StartRecentAppMediaProjectionFromSplitScreen {
+
+ val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ val tapl = LauncherInstrumentation()
+ val wmHelper = WindowManagerStateHelper(instrumentation)
+ val device = UiDevice.getInstance(instrumentation)
+
+ private val initialRotation = Rotation.ROTATION_0
+ private val simpleApp = SimpleAppHelper(instrumentation)
+ private val targetApp = CalculatorAppHelper(instrumentation)
+ private val testApp = StartMediaProjectionAppHelper(instrumentation)
+
+ @Rule
+ @JvmField
+ val testSetupRule = Utils.testSetupRule(NavBar.MODE_GESTURAL, initialRotation)
+
+ @Before
+ fun setup() {
+ tapl.workspace.switchToOverview().dismissAllTasks()
+ tapl.setEnableRotation(true)
+ tapl.setExpectedRotation(initialRotation.value)
+ targetApp.open()
+ SplitScreenUtils.enterSplit(wmHelper, tapl, device, simpleApp, testApp, initialRotation)
+ }
+
+ @Test
+ open fun startMediaProjection() {
+ // The app we want to open for PSS will be the second item in the carousel,
+ // because the first will be the app open in split screen alongside the MediaProjection app
+ testApp.startSingleAppMediaProjectionFromRecents(wmHelper, targetApp, recentTasksIndex = 1)
+ }
+
+ @After
+ fun teardown() {
+ testApp.exit(wmHelper)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/mediaprojection/scenarios/src/com/android/wm/shell/scenarios/StartRecentAppMediaProjectionInSplitScreen.kt b/libs/WindowManager/Shell/tests/e2e/mediaprojection/scenarios/src/com/android/wm/shell/scenarios/StartRecentAppMediaProjectionInSplitScreen.kt
new file mode 100644
index 0000000..0a6992f
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/mediaprojection/scenarios/src/com/android/wm/shell/scenarios/StartRecentAppMediaProjectionInSplitScreen.kt
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.scenarios
+
+import android.app.Instrumentation
+import android.platform.test.annotations.Postsubmit
+import android.tools.NavBar
+import android.tools.Rotation
+import android.tools.device.apphelpers.CalculatorAppHelper
+import android.tools.traces.parsers.WindowManagerStateHelper
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
+import com.android.launcher3.tapl.LauncherInstrumentation
+import com.android.server.wm.flicker.helpers.StartMediaProjectionAppHelper
+import com.android.wm.shell.Utils
+import com.android.wm.shell.flicker.utils.SplitScreenUtils
+import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
+
+/**
+ * Test scenario which requests an a single-app MediaProjection session from recents while the
+ * TARGET app is in split screen (with host app).
+ *
+ * This is for testing that the split pair isn't broken, and capture still proceeds as expected
+ */
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+open class StartRecentAppMediaProjectionInSplitScreen {
+
+ val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ val tapl = LauncherInstrumentation()
+ val wmHelper = WindowManagerStateHelper(instrumentation)
+ val device = UiDevice.getInstance(instrumentation)
+
+ private val initialRotation = Rotation.ROTATION_0
+ private val targetApp = CalculatorAppHelper(instrumentation)
+ private val testApp = StartMediaProjectionAppHelper(instrumentation)
+
+ @Rule
+ @JvmField
+ val testSetupRule = Utils.testSetupRule(NavBar.MODE_GESTURAL, initialRotation)
+
+ @Before
+ fun setup() {
+ tapl.workspace.switchToOverview().dismissAllTasks()
+ tapl.setEnableRotation(true)
+ tapl.setExpectedRotation(initialRotation.value)
+ SplitScreenUtils.enterSplit(wmHelper, tapl, device, targetApp, testApp, initialRotation)
+ }
+
+ @Test
+ open fun startMediaProjection() {
+ testApp.startSingleAppMediaProjectionFromRecents(wmHelper, targetApp)
+
+ wmHelper
+ .StateSyncBuilder()
+ .withAppTransitionIdle()
+ .withWindowSurfaceAppeared(targetApp)
+ .withWindowSurfaceAppeared(testApp)
+ .waitForAndVerify()
+ }
+
+ @After
+ fun teardown() {
+ testApp.exit(wmHelper)
+ targetApp.exit(wmHelper)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt
index a248303..fd4328d 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt
@@ -16,16 +16,18 @@
package com.android.wm.shell.flicker.pip
+import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
+import android.platform.test.annotations.RequiresDevice
+import android.platform.test.annotations.RequiresFlagsDisabled
import android.tools.flicker.junit.FlickerParametersRunnerFactory
import android.tools.flicker.legacy.FlickerBuilder
import android.tools.flicker.legacy.LegacyFlickerTest
import android.tools.flicker.subject.exceptions.ExceptionMessageBuilder
import android.tools.flicker.subject.exceptions.IncorrectRegionException
import android.tools.flicker.subject.layers.LayerSubject
-import androidx.test.filters.FlakyTest
-import androidx.test.filters.RequiresDevice
+import com.android.wm.shell.Flags
import com.android.wm.shell.flicker.pip.common.EnterPipTransition
import org.junit.Assume
import org.junit.FixMethodOrder
@@ -35,6 +37,7 @@
import org.junit.runners.Parameterized
import kotlin.math.abs
+
/**
* Test entering pip from an app via auto-enter property when navigating to home.
*
@@ -60,6 +63,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@RequiresFlagsDisabled(Flags.FLAG_ENABLE_PIP2)
open class AutoEnterPipOnGoToHomeTest(flicker: LegacyFlickerTest) : EnterPipTransition(flicker) {
override val thisTransition: FlickerBuilder.() -> Unit = { transitions { tapl.goHome() } }
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/AutoEnterPipWithSourceRectHintTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/AutoEnterPipWithSourceRectHintTest.kt
index df952c9..d4ad4ef8 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/AutoEnterPipWithSourceRectHintTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/AutoEnterPipWithSourceRectHintTest.kt
@@ -17,10 +17,12 @@
package com.android.wm.shell.flicker.pip
import android.platform.test.annotations.Presubmit
+import android.platform.test.annotations.RequiresFlagsDisabled
import android.tools.flicker.junit.FlickerParametersRunnerFactory
import android.tools.flicker.legacy.FlickerBuilder
import android.tools.flicker.legacy.LegacyFlickerTest
import android.tools.traces.component.ComponentNameMatcher
+import com.android.wm.shell.Flags
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -52,6 +54,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@RequiresFlagsDisabled(Flags.FLAG_ENABLE_PIP2)
class AutoEnterPipWithSourceRectHintTest(flicker: LegacyFlickerTest) :
AutoEnterPipOnGoToHomeTest(flicker) {
override val defaultEnterPip: FlickerBuilder.() -> Unit = {
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTest.kt
index 302b8c4..cc6e4b5 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTest.kt
@@ -17,6 +17,7 @@
package com.android.wm.shell.flicker.pip
import android.platform.test.annotations.Presubmit
+import android.platform.test.annotations.RequiresFlagsDisabled
import android.tools.flicker.junit.FlickerParametersRunnerFactory
import android.tools.flicker.legacy.FlickerBuilder
import android.tools.flicker.legacy.LegacyFlickerTest
@@ -53,6 +54,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@RequiresFlagsDisabled(Flags.FLAG_ENABLE_PIP2)
class ClosePipBySwipingDownTest(flicker: LegacyFlickerTest) : ClosePipTransition(flicker) {
override val thisTransition: FlickerBuilder.() -> Unit = {
transitions {
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ClosePipWithDismissButtonTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ClosePipWithDismissButtonTest.kt
index 77a1edb..53725fa 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ClosePipWithDismissButtonTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ClosePipWithDismissButtonTest.kt
@@ -17,9 +17,11 @@
package com.android.wm.shell.flicker.pip
import android.platform.test.annotations.Presubmit
+import android.platform.test.annotations.RequiresFlagsDisabled
import android.tools.flicker.junit.FlickerParametersRunnerFactory
import android.tools.flicker.legacy.FlickerBuilder
import android.tools.flicker.legacy.LegacyFlickerTest
+import com.android.wm.shell.Flags
import com.android.wm.shell.flicker.pip.common.ClosePipTransition
import org.junit.FixMethodOrder
import org.junit.Test
@@ -52,6 +54,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@RequiresFlagsDisabled(Flags.FLAG_ENABLE_PIP2)
class ClosePipWithDismissButtonTest(flicker: LegacyFlickerTest) : ClosePipTransition(flicker) {
override val thisTransition: FlickerBuilder.() -> Unit = {
transitions { pipApp.closePipWindow(wmHelper) }
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTest.kt
index 6e32d64..a1551b7 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTest.kt
@@ -17,9 +17,11 @@
package com.android.wm.shell.flicker.pip
import android.platform.test.annotations.Presubmit
+import android.platform.test.annotations.RequiresFlagsDisabled
import android.tools.flicker.junit.FlickerParametersRunnerFactory
import android.tools.flicker.legacy.FlickerBuilder
import android.tools.flicker.legacy.LegacyFlickerTest
+import com.android.wm.shell.Flags
import com.android.wm.shell.flicker.pip.common.EnterPipTransition
import org.junit.Assume
import org.junit.FixMethodOrder
@@ -43,6 +45,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@RequiresFlagsDisabled(Flags.FLAG_ENABLE_PIP2)
class EnterPipOnUserLeaveHintTest(flicker: LegacyFlickerTest) : EnterPipTransition(flicker) {
override val thisTransition: FlickerBuilder.() -> Unit = { transitions { tapl.goHome() } }
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientation.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientation.kt
index 9a6cb61..ea5b3e5 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientation.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientation.kt
@@ -19,6 +19,7 @@
import android.app.Activity
import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
+import android.platform.test.annotations.RequiresFlagsDisabled
import android.tools.Rotation
import android.tools.flicker.assertions.FlickerTest
import android.tools.flicker.junit.FlickerParametersRunnerFactory
@@ -32,6 +33,7 @@
import com.android.server.wm.flicker.helpers.FixedOrientationAppHelper
import com.android.server.wm.flicker.testapp.ActivityOptions.Pip.ACTION_ENTER_PIP
import com.android.server.wm.flicker.testapp.ActivityOptions.PortraitOnlyActivity.EXTRA_FIXED_ORIENTATION
+import com.android.wm.shell.Flags
import com.android.wm.shell.flicker.pip.common.PipTransition
import com.android.wm.shell.flicker.pip.common.PipTransition.BroadcastActionTrigger.Companion.ORIENTATION_LANDSCAPE
import com.android.wm.shell.flicker.pip.common.PipTransition.BroadcastActionTrigger.Companion.ORIENTATION_PORTRAIT
@@ -68,6 +70,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@RequiresFlagsDisabled(Flags.FLAG_ENABLE_PIP2)
class EnterPipToOtherOrientation(flicker: LegacyFlickerTest) : PipTransition(flicker) {
private val testApp = FixedOrientationAppHelper(instrumentation)
private val startingBounds = WindowUtils.getDisplayBounds(Rotation.ROTATION_90)
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/EnterPipViaAppUiButtonTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/EnterPipViaAppUiButtonTest.kt
index 6b4751c..a109c4b 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/EnterPipViaAppUiButtonTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/EnterPipViaAppUiButtonTest.kt
@@ -16,9 +16,11 @@
package com.android.wm.shell.flicker.pip
+import android.platform.test.annotations.RequiresFlagsDisabled
import android.tools.flicker.junit.FlickerParametersRunnerFactory
import android.tools.flicker.legacy.FlickerBuilder
import android.tools.flicker.legacy.LegacyFlickerTest
+import com.android.wm.shell.Flags
import com.android.wm.shell.flicker.pip.common.EnterPipTransition
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
@@ -49,6 +51,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@RequiresFlagsDisabled(Flags.FLAG_ENABLE_PIP2)
open class EnterPipViaAppUiButtonTest(flicker: LegacyFlickerTest) : EnterPipTransition(flicker) {
override val thisTransition: FlickerBuilder.() -> Unit = {
transitions { pipApp.clickEnterPipButton(wmHelper) }
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTest.kt
index 8d0bc0f..14ec303 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTest.kt
@@ -16,9 +16,11 @@
package com.android.wm.shell.flicker.pip
+import android.platform.test.annotations.RequiresFlagsDisabled
import android.tools.flicker.junit.FlickerParametersRunnerFactory
import android.tools.flicker.legacy.FlickerBuilder
import android.tools.flicker.legacy.LegacyFlickerTest
+import com.android.wm.shell.Flags
import com.android.wm.shell.flicker.pip.common.ExitPipToAppTransition
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
@@ -51,6 +53,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@RequiresFlagsDisabled(Flags.FLAG_ENABLE_PIP2)
class ExitPipToAppViaExpandButtonTest(flicker: LegacyFlickerTest) :
ExitPipToAppTransition(flicker) {
override val thisTransition: FlickerBuilder.() -> Unit = {
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaIntentTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaIntentTest.kt
index 939f328..8a34b5e 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaIntentTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaIntentTest.kt
@@ -16,9 +16,11 @@
package com.android.wm.shell.flicker.pip
+import android.platform.test.annotations.RequiresFlagsDisabled
import android.tools.flicker.junit.FlickerParametersRunnerFactory
import android.tools.flicker.legacy.FlickerBuilder
import android.tools.flicker.legacy.LegacyFlickerTest
+import com.android.wm.shell.Flags
import com.android.wm.shell.flicker.pip.common.ExitPipToAppTransition
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
@@ -50,6 +52,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@RequiresFlagsDisabled(Flags.FLAG_ENABLE_PIP2)
class ExitPipToAppViaIntentTest(flicker: LegacyFlickerTest) : ExitPipToAppTransition(flicker) {
override val thisTransition: FlickerBuilder.() -> Unit = {
setup {
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt
index 258663b..4f189fc 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt
@@ -17,12 +17,14 @@
package com.android.wm.shell.flicker.pip
import android.platform.test.annotations.Presubmit
+import android.platform.test.annotations.RequiresFlagsDisabled
import android.tools.Rotation
import android.tools.flicker.junit.FlickerParametersRunnerFactory
import android.tools.flicker.legacy.FlickerBuilder
import android.tools.flicker.legacy.LegacyFlickerTest
import android.tools.flicker.legacy.LegacyFlickerTestFactory
import android.tools.traces.component.ComponentNameMatcher
+import com.android.wm.shell.Flags
import com.android.wm.shell.flicker.pip.common.PipTransition
import org.junit.FixMethodOrder
import org.junit.Test
@@ -54,6 +56,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@RequiresFlagsDisabled(Flags.FLAG_ENABLE_PIP2)
class ExpandPipOnDoubleClickTest(flicker: LegacyFlickerTest) : PipTransition(flicker) {
override val thisTransition: FlickerBuilder.() -> Unit = {
transitions { pipApp.doubleClickPipWindow(wmHelper) }
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ExpandPipOnPinchOpenTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ExpandPipOnPinchOpenTest.kt
index 1964e3c..4d72b03d 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ExpandPipOnPinchOpenTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ExpandPipOnPinchOpenTest.kt
@@ -17,11 +17,13 @@
package com.android.wm.shell.flicker.pip
import android.platform.test.annotations.Presubmit
+import android.platform.test.annotations.RequiresFlagsDisabled
import android.tools.Rotation
import android.tools.flicker.junit.FlickerParametersRunnerFactory
import android.tools.flicker.legacy.FlickerBuilder
import android.tools.flicker.legacy.LegacyFlickerTest
import android.tools.flicker.legacy.LegacyFlickerTestFactory
+import com.android.wm.shell.Flags
import com.android.wm.shell.flicker.pip.common.PipTransition
import org.junit.FixMethodOrder
import org.junit.Test
@@ -33,6 +35,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@RequiresFlagsDisabled(Flags.FLAG_ENABLE_PIP2)
class ExpandPipOnPinchOpenTest(flicker: LegacyFlickerTest) : PipTransition(flicker) {
override val thisTransition: FlickerBuilder.() -> Unit = {
transitions { pipApp.pinchOpenPipWindow(wmHelper, 0.25f, 30) }
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/FromSplitScreenAutoEnterPipOnGoToHomeTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/FromSplitScreenAutoEnterPipOnGoToHomeTest.kt
index 5f8ac2a..1c40d89 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/FromSplitScreenAutoEnterPipOnGoToHomeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/FromSplitScreenAutoEnterPipOnGoToHomeTest.kt
@@ -16,7 +16,10 @@
package com.android.wm.shell.flicker.pip
+import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Presubmit
+import android.platform.test.annotations.RequiresDevice
+import android.platform.test.annotations.RequiresFlagsDisabled
import android.tools.Rotation
import android.tools.flicker.junit.FlickerParametersRunnerFactory
import android.tools.flicker.legacy.FlickerBuilder
@@ -24,10 +27,9 @@
import android.tools.flicker.legacy.LegacyFlickerTestFactory
import android.tools.helpers.WindowUtils
import android.tools.traces.parsers.toFlickerComponent
-import androidx.test.filters.FlakyTest
-import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.helpers.SimpleAppHelper
import com.android.server.wm.flicker.testapp.ActivityOptions
+import com.android.wm.shell.Flags
import com.android.wm.shell.flicker.utils.SplitScreenUtils
import org.junit.Assume
import org.junit.FixMethodOrder
@@ -62,6 +64,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@RequiresFlagsDisabled(Flags.FLAG_ENABLE_PIP2)
class FromSplitScreenAutoEnterPipOnGoToHomeTest(flicker: LegacyFlickerTest) :
AutoEnterPipOnGoToHomeTest(flicker) {
private val portraitDisplayBounds = WindowUtils.getDisplayBounds(Rotation.ROTATION_0)
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/FromSplitScreenEnterPipOnUserLeaveHintTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/FromSplitScreenEnterPipOnUserLeaveHintTest.kt
index 48c85a8..12e2328 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/FromSplitScreenEnterPipOnUserLeaveHintTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/FromSplitScreenEnterPipOnUserLeaveHintTest.kt
@@ -16,7 +16,10 @@
package com.android.wm.shell.flicker.pip
+import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Presubmit
+import android.platform.test.annotations.RequiresDevice
+import android.platform.test.annotations.RequiresFlagsDisabled
import android.tools.Rotation
import android.tools.flicker.junit.FlickerParametersRunnerFactory
import android.tools.flicker.legacy.FlickerBuilder
@@ -24,10 +27,9 @@
import android.tools.flicker.legacy.LegacyFlickerTestFactory
import android.tools.helpers.WindowUtils
import android.tools.traces.parsers.toFlickerComponent
-import androidx.test.filters.FlakyTest
-import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.helpers.SimpleAppHelper
import com.android.server.wm.flicker.testapp.ActivityOptions
+import com.android.wm.shell.Flags
import com.android.wm.shell.flicker.pip.common.EnterPipTransition
import com.android.wm.shell.flicker.utils.SplitScreenUtils
import org.junit.Assume
@@ -63,6 +65,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@RequiresFlagsDisabled(Flags.FLAG_ENABLE_PIP2)
class FromSplitScreenEnterPipOnUserLeaveHintTest(flicker: LegacyFlickerTest) :
EnterPipTransition(flicker) {
private val portraitDisplayBounds = WindowUtils.getDisplayBounds(Rotation.ROTATION_0)
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/MovePipDownOnShelfHeightChange.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/MovePipDownOnShelfHeightChange.kt
index ee62cf5..d979b42 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/MovePipDownOnShelfHeightChange.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/MovePipDownOnShelfHeightChange.kt
@@ -17,10 +17,12 @@
package com.android.wm.shell.flicker.pip
import android.platform.test.annotations.Presubmit
+import android.platform.test.annotations.RequiresDevice
+import android.platform.test.annotations.RequiresFlagsDisabled
import android.tools.flicker.junit.FlickerParametersRunnerFactory
import android.tools.flicker.legacy.FlickerBuilder
import android.tools.flicker.legacy.LegacyFlickerTest
-import androidx.test.filters.RequiresDevice
+import com.android.wm.shell.Flags
import com.android.wm.shell.flicker.pip.common.MovePipShelfHeightTransition
import com.android.wm.shell.flicker.utils.Direction
import org.junit.FixMethodOrder
@@ -56,6 +58,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@RequiresFlagsDisabled(Flags.FLAG_ENABLE_PIP2)
class MovePipDownOnShelfHeightChange(flicker: LegacyFlickerTest) :
MovePipShelfHeightTransition(flicker) {
override val thisTransition: FlickerBuilder.() -> Unit = {
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/MovePipOnImeVisibilityChangeTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/MovePipOnImeVisibilityChangeTest.kt
index 04fedf4..88d78ed 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/MovePipOnImeVisibilityChangeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/MovePipOnImeVisibilityChangeTest.kt
@@ -17,6 +17,7 @@
package com.android.wm.shell.flicker.pip
import android.platform.test.annotations.Presubmit
+import android.platform.test.annotations.RequiresFlagsDisabled
import android.tools.Rotation
import android.tools.flicker.assertions.FlickerTest
import android.tools.flicker.junit.FlickerParametersRunnerFactory
@@ -27,6 +28,7 @@
import android.tools.traces.component.ComponentNameMatcher
import com.android.server.wm.flicker.helpers.ImeAppHelper
import com.android.server.wm.flicker.helpers.setRotation
+import com.android.wm.shell.Flags
import com.android.wm.shell.flicker.pip.common.PipTransition
import org.junit.FixMethodOrder
import org.junit.Test
@@ -41,6 +43,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@RequiresFlagsDisabled(Flags.FLAG_ENABLE_PIP2)
class MovePipOnImeVisibilityChangeTest(flicker: LegacyFlickerTest) : PipTransition(flicker) {
private val imeApp = ImeAppHelper(instrumentation)
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/MovePipUpOnShelfHeightChangeTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/MovePipUpOnShelfHeightChangeTest.kt
index 4d643f7..c533800 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/MovePipUpOnShelfHeightChangeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/MovePipUpOnShelfHeightChangeTest.kt
@@ -17,10 +17,12 @@
package com.android.wm.shell.flicker.pip
import android.platform.test.annotations.Presubmit
+import android.platform.test.annotations.RequiresDevice
+import android.platform.test.annotations.RequiresFlagsDisabled
import android.tools.flicker.junit.FlickerParametersRunnerFactory
import android.tools.flicker.legacy.FlickerBuilder
import android.tools.flicker.legacy.LegacyFlickerTest
-import androidx.test.filters.RequiresDevice
+import com.android.wm.shell.Flags
import com.android.wm.shell.flicker.pip.common.MovePipShelfHeightTransition
import com.android.wm.shell.flicker.utils.Direction
import org.junit.FixMethodOrder
@@ -56,6 +58,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@RequiresFlagsDisabled(Flags.FLAG_ENABLE_PIP2)
open class MovePipUpOnShelfHeightChangeTest(flicker: LegacyFlickerTest) :
MovePipShelfHeightTransition(flicker) {
override val thisTransition: FlickerBuilder.() -> Unit = {
@@ -65,7 +68,8 @@
}
/** Checks that the visible region of [pipApp] window always moves up during the animation. */
- @Presubmit @Test fun pipWindowMovesUp() = pipWindowMoves(Direction.UP)
+ @Presubmit
+ @Test fun pipWindowMovesUp() = pipWindowMoves(Direction.UP)
/** Checks that the visible region of [pipApp] layer always moves up during the animation. */
@Presubmit @Test fun pipLayerMovesUp() = pipLayerMoves(Direction.UP)
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/PipAspectRatioChangeTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/PipAspectRatioChangeTest.kt
index 429774f..04016a9 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/PipAspectRatioChangeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/PipAspectRatioChangeTest.kt
@@ -17,11 +17,13 @@
package com.android.wm.shell.flicker.pip
import android.platform.test.annotations.Presubmit
+import android.platform.test.annotations.RequiresFlagsDisabled
import android.tools.Rotation
import android.tools.flicker.junit.FlickerParametersRunnerFactory
import android.tools.flicker.legacy.FlickerBuilder
import android.tools.flicker.legacy.LegacyFlickerTest
import android.tools.flicker.legacy.LegacyFlickerTestFactory
+import com.android.wm.shell.Flags
import com.android.wm.shell.flicker.pip.common.PipTransition
import org.junit.FixMethodOrder
import org.junit.Test
@@ -33,6 +35,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@RequiresFlagsDisabled(Flags.FLAG_ENABLE_PIP2)
class PipAspectRatioChangeTest(flicker: LegacyFlickerTest) : PipTransition(flicker) {
override val thisTransition: FlickerBuilder.() -> Unit = {
transitions { pipApp.changeAspectRatio(wmHelper) }
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/PipDragTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/PipDragTest.kt
index a4df69f..6bcaabc 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/PipDragTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/PipDragTest.kt
@@ -18,11 +18,13 @@
import android.platform.test.annotations.Presubmit
import android.platform.test.annotations.RequiresDevice
+import android.platform.test.annotations.RequiresFlagsDisabled
import android.tools.flicker.junit.FlickerParametersRunnerFactory
import android.tools.flicker.legacy.FlickerBuilder
import android.tools.flicker.legacy.LegacyFlickerTest
import android.tools.flicker.legacy.LegacyFlickerTestFactory
import com.android.server.wm.flicker.testapp.ActivityOptions
+import com.android.wm.shell.Flags
import com.android.wm.shell.flicker.pip.common.PipTransition
import org.junit.FixMethodOrder
import org.junit.Test
@@ -35,6 +37,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@RequiresFlagsDisabled(Flags.FLAG_ENABLE_PIP2)
class PipDragTest(flicker: LegacyFlickerTest) : PipTransition(flicker) {
private var isDraggedLeft: Boolean = true
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/PipDragThenSnapTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/PipDragThenSnapTest.kt
index cbd4a52..d82bfdd 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/PipDragThenSnapTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/PipDragThenSnapTest.kt
@@ -17,16 +17,18 @@
package com.android.wm.shell.flicker.pip
import android.graphics.Rect
+import android.platform.test.annotations.FlakyTest
+import android.platform.test.annotations.RequiresDevice
+import android.platform.test.annotations.RequiresFlagsDisabled
import android.tools.Rotation
import android.tools.flicker.junit.FlickerParametersRunnerFactory
import android.tools.flicker.legacy.FlickerBuilder
import android.tools.flicker.legacy.LegacyFlickerTest
import android.tools.flicker.legacy.LegacyFlickerTestFactory
import android.tools.flicker.rules.RemoveAllTasksButHomeRule
-import androidx.test.filters.FlakyTest
-import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.testapp.ActivityOptions
+import com.android.wm.shell.Flags
import com.android.wm.shell.flicker.pip.common.PipTransition
import org.junit.FixMethodOrder
import org.junit.Test
@@ -40,6 +42,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@RequiresFlagsDisabled(Flags.FLAG_ENABLE_PIP2)
class PipDragThenSnapTest(flicker: LegacyFlickerTest) : PipTransition(flicker) {
// represents the direction in which the pip window should be snapping
private var willSnapRight: Boolean = true
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/PipPinchInTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/PipPinchInTest.kt
index 16d08e5..dbc97d0 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/PipPinchInTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/PipPinchInTest.kt
@@ -17,13 +17,15 @@
package com.android.wm.shell.flicker.pip
import android.platform.test.annotations.Presubmit
+import android.platform.test.annotations.RequiresDevice
+import android.platform.test.annotations.RequiresFlagsDisabled
import android.tools.Rotation
import android.tools.flicker.junit.FlickerParametersRunnerFactory
import android.tools.flicker.legacy.FlickerBuilder
import android.tools.flicker.legacy.LegacyFlickerTest
import android.tools.flicker.legacy.LegacyFlickerTestFactory
import android.tools.flicker.subject.exceptions.IncorrectRegionException
-import androidx.test.filters.RequiresDevice
+import com.android.wm.shell.Flags
import com.android.wm.shell.flicker.pip.common.PipTransition
import org.junit.FixMethodOrder
import org.junit.Test
@@ -36,6 +38,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@RequiresFlagsDisabled(Flags.FLAG_ENABLE_PIP2)
class PipPinchInTest(flicker: LegacyFlickerTest) : PipTransition(flicker) {
override val thisTransition: FlickerBuilder.() -> Unit = {
transitions { pipApp.pinchInPipWindow(wmHelper, 0.4f, 30) }
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinned.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinned.kt
index 578a9b5..9d46ac1 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinned.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinned.kt
@@ -17,8 +17,11 @@
package com.android.wm.shell.flicker.pip
import android.app.Activity
+import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
+import android.platform.test.annotations.RequiresDevice
+import android.platform.test.annotations.RequiresFlagsDisabled
import android.tools.Rotation
import android.tools.flicker.assertions.FlickerTest
import android.tools.flicker.junit.FlickerParametersRunnerFactory
@@ -26,10 +29,9 @@
import android.tools.flicker.legacy.LegacyFlickerTest
import android.tools.flicker.legacy.LegacyFlickerTestFactory
import android.tools.helpers.WindowUtils
-import androidx.test.filters.FlakyTest
-import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.testapp.ActivityOptions
import com.android.server.wm.flicker.testapp.ActivityOptions.PortraitOnlyActivity.EXTRA_FIXED_ORIENTATION
+import com.android.wm.shell.Flags
import com.android.wm.shell.flicker.pip.common.PipTransition
import com.android.wm.shell.flicker.pip.common.PipTransition.BroadcastActionTrigger.Companion.ORIENTATION_LANDSCAPE
import org.junit.Assume
@@ -48,6 +50,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@RequiresFlagsDisabled(Flags.FLAG_ENABLE_PIP2)
open class SetRequestedOrientationWhilePinned(flicker: LegacyFlickerTest) : PipTransition(flicker) {
private val startingBounds = WindowUtils.getDisplayBounds(Rotation.ROTATION_0)
private val endingBounds = WindowUtils.getDisplayBounds(Rotation.ROTATION_90)
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ShowPipAndRotateDisplay.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ShowPipAndRotateDisplay.kt
index c6cf341..e72251f 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ShowPipAndRotateDisplay.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ShowPipAndRotateDisplay.kt
@@ -17,6 +17,7 @@
package com.android.wm.shell.flicker.pip
import android.platform.test.annotations.Presubmit
+import android.platform.test.annotations.RequiresFlagsDisabled
import android.tools.flicker.assertions.FlickerTest
import android.tools.flicker.junit.FlickerParametersRunnerFactory
import android.tools.flicker.legacy.FlickerBuilder
@@ -25,6 +26,7 @@
import android.tools.helpers.WindowUtils
import com.android.server.wm.flicker.helpers.SimpleAppHelper
import com.android.server.wm.flicker.helpers.setRotation
+import com.android.wm.shell.Flags
import com.android.wm.shell.flicker.pip.common.PipTransition
import org.junit.FixMethodOrder
import org.junit.Test
@@ -58,6 +60,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@RequiresFlagsDisabled(Flags.FLAG_ENABLE_PIP2)
class ShowPipAndRotateDisplay(flicker: LegacyFlickerTest) : PipTransition(flicker) {
private val testApp = SimpleAppHelper(instrumentation)
private val screenBoundsStart = WindowUtils.getDisplayBounds(flicker.scenario.startRotation)
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/PipTransition.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/PipTransition.kt
index bc2bfdb..c37bf35 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/PipTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/PipTransition.kt
@@ -20,6 +20,7 @@
import android.content.Intent
import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
+import android.platform.test.flag.junit.DeviceFlagsValueProvider
import android.tools.Rotation
import android.tools.flicker.legacy.FlickerBuilder
import android.tools.flicker.legacy.LegacyFlickerTest
@@ -31,9 +32,14 @@
import com.android.server.wm.flicker.testapp.ActivityOptions
import com.android.wm.shell.flicker.BaseTest
import com.google.common.truth.Truth
+import org.junit.Rule
import org.junit.Test
abstract class PipTransition(flicker: LegacyFlickerTest) : BaseTest(flicker) {
+ @JvmField
+ @Rule
+ val checkFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule()
+
protected val pipApp = PipAppHelper(instrumentation)
protected val displayBounds = WindowUtils.getDisplayBounds(flicker.scenario.startRotation)
protected val broadcastActionTrigger = BroadcastActionTrigger(instrumentation)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/MediaProjectionUtils.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/MediaProjectionUtils.kt
index f9706969..8c2bdad 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/MediaProjectionUtils.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/MediaProjectionUtils.kt
@@ -17,7 +17,11 @@
package com.android.wm.shell.flicker.utils
object MediaProjectionUtils {
- const val REQUEST_CODE: Int = 99
+ // Request code for the normal media projection request
+ const val REQUEST_CODE_NORMAL: Int = 11
+ // Request code for the media projection request which will include an extra intent to open
+ // home screen before starting requested app
+ const val REQUEST_CODE_EXTRA_INTENT: Int = 12
const val MSG_START_FOREGROUND_DONE: Int = 1
const val MSG_SERVICE_DESTROYED: Int = 2
const val EXTRA_MESSENGER: String = "messenger"
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestSyncExecutor.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestSyncExecutor.kt
new file mode 100644
index 0000000..528ca7e
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestSyncExecutor.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.wm.shell
+
+import com.android.wm.shell.common.ShellExecutor
+
+/**
+ * Test ShellExecutor that runs everything synchronously.
+ */
+class TestSyncExecutor : ShellExecutor {
+ override fun execute(runnable: Runnable) {
+ runnable.run()
+ }
+
+ override fun executeDelayed(runnable: Runnable, delayMillis: Long) {
+ runnable.run()
+ }
+
+ override fun removeCallbacks(runnable: Runnable) {
+ }
+
+ override fun hasCallback(runnable: Runnable): Boolean {
+ return false
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
index 6fa3788..ce640b5 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
@@ -47,6 +47,7 @@
import androidx.test.filters.SmallTest;
+import com.android.internal.logging.testing.UiEventLoggerFake;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.bubbles.BubbleData.TimeSource;
import com.android.wm.shell.common.ShellExecutor;
@@ -102,6 +103,7 @@
private BubbleData mBubbleData;
private TestableBubblePositioner mPositioner;
+ private UiEventLoggerFake mUiEventLogger;
@Mock
private TimeSource mTimeSource;
@@ -112,8 +114,6 @@
@Mock
private PendingIntent mDeleteIntent;
@Mock
- private BubbleLogger mBubbleLogger;
- @Mock
private BubbleEducationController mEducationController;
@Mock
private ShellExecutor mMainExecutor;
@@ -196,10 +196,12 @@
mock(Icon.class),
mMainExecutor, mBgExecutor);
+ mUiEventLogger = new UiEventLoggerFake();
+
mPositioner = new TestableBubblePositioner(mContext,
mContext.getSystemService(WindowManager.class));
- mBubbleData = new BubbleData(getContext(), mBubbleLogger, mPositioner, mEducationController,
- mMainExecutor, mBgExecutor);
+ mBubbleData = new BubbleData(getContext(), new BubbleLogger(mUiEventLogger), mPositioner,
+ mEducationController, mMainExecutor, mBgExecutor);
// Used by BubbleData to set lastAccessedTime
when(mTimeSource.currentTimeMillis()).thenReturn(1000L);
@@ -297,6 +299,82 @@
}
@Test
+ public void testRemoveBubbleFromBubbleBar_notifCancelled_logEvent() {
+ mPositioner.setShowingInBubbleBar(true);
+
+ sendUpdatedEntryAtTime(mEntryA1, 1000);
+ mBubbleData.setListener(mListener);
+
+ mBubbleData.dismissBubbleWithKey(mEntryA1.getKey(), Bubbles.DISMISS_NOTIF_CANCEL);
+ assertThat(mUiEventLogger.numLogs()).isEqualTo(1);
+ assertThat(mUiEventLogger.eventId(0)).isEqualTo(
+ BubbleLogger.Event.BUBBLE_BAR_BUBBLE_REMOVED_CANCELED.getId());
+ }
+
+ @Test
+ public void testRemoveBubbleFromBubbleBar_taskFinished_logEvent() {
+ mPositioner.setShowingInBubbleBar(true);
+
+ sendUpdatedEntryAtTime(mEntryA1, 1000);
+ mBubbleData.setListener(mListener);
+
+ mBubbleData.dismissBubbleWithKey(mEntryA1.getKey(), Bubbles.DISMISS_TASK_FINISHED);
+ assertThat(mUiEventLogger.numLogs()).isEqualTo(1);
+ assertThat(mUiEventLogger.eventId(0)).isEqualTo(
+ BubbleLogger.Event.BUBBLE_BAR_BUBBLE_ACTIVITY_FINISH.getId());
+ }
+
+ @Test
+ public void testRemoveBubbleFromBubbleBar_notifBlocked_logEvent() {
+ mPositioner.setShowingInBubbleBar(true);
+
+ sendUpdatedEntryAtTime(mEntryA1, 1000);
+ mBubbleData.setListener(mListener);
+
+ mBubbleData.dismissBubbleWithKey(mEntryA1.getKey(), Bubbles.DISMISS_BLOCKED);
+ assertThat(mUiEventLogger.numLogs()).isEqualTo(1);
+ assertThat(mUiEventLogger.eventId(0)).isEqualTo(
+ BubbleLogger.Event.BUBBLE_BAR_BUBBLE_REMOVED_BLOCKED.getId());
+ }
+
+ @Test
+ public void testRemoveBubbleFromBubbleBar_noLongerBubble_logEvent() {
+ mPositioner.setShowingInBubbleBar(true);
+
+ sendUpdatedEntryAtTime(mEntryA1, 1000);
+ mBubbleData.setListener(mListener);
+
+ mBubbleData.dismissBubbleWithKey(mEntryA1.getKey(), Bubbles.DISMISS_NO_LONGER_BUBBLE);
+ assertThat(mUiEventLogger.numLogs()).isEqualTo(1);
+ assertThat(mUiEventLogger.eventId(0)).isEqualTo(
+ BubbleLogger.Event.BUBBLE_BAR_BUBBLE_REMOVED_BLOCKED.getId());
+ }
+
+ @Test
+ public void testRemoveBubbleFromBubbleBar_addToOverflow_logEvent() {
+ mPositioner.setShowingInBubbleBar(true);
+
+ sendUpdatedEntryAtTime(mEntryA1, 1000);
+ mBubbleData.setListener(mListener);
+
+ mBubbleData.dismissBubbleWithKey(mEntryA1.getKey(), Bubbles.DISMISS_AGED);
+ assertThat(mUiEventLogger.numLogs()).isEqualTo(1);
+ assertThat(mUiEventLogger.eventId(0)).isEqualTo(
+ BubbleLogger.Event.BUBBLE_BAR_OVERFLOW_ADD_AGED.getId());
+ }
+
+ @Test
+ public void testRemoveBubble_notifCancelled_noLog() {
+ mPositioner.setShowingInBubbleBar(false);
+
+ sendUpdatedEntryAtTime(mEntryA1, 1000);
+ mBubbleData.setListener(mListener);
+
+ mBubbleData.dismissBubbleWithKey(mEntryA1.getKey(), Bubbles.DISMISS_BLOCKED);
+ assertThat(mUiEventLogger.numLogs()).isEqualTo(0);
+ }
+
+ @Test
public void ifSuppress_hideFlyout() {
// Setup
mBubbleData.setListener(mListener);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxConfigurationTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxConfigurationTest.kt
new file mode 100644
index 0000000..75025d90
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxConfigurationTest.kt
@@ -0,0 +1,160 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.compatui.letterbox
+
+import android.annotation.ColorRes
+import android.content.Context
+import android.content.res.Resources
+import android.graphics.Color
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn
+import com.android.internal.R
+import com.android.wm.shell.ShellTestCase
+import java.util.function.Consumer
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.doReturn
+
+/**
+ * Tests for [LetterboxConfiguration].
+ *
+ * Build/Install/Run:
+ * atest WMShellUnitTests:LetterboxConfigurationTest
+ */
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class LetterboxConfigurationTest : ShellTestCase() {
+
+ companion object {
+ @JvmStatic
+ val COLOR_WHITE = Color.valueOf(Color.WHITE)
+ @JvmStatic
+ val COLOR_RED = Color.valueOf(Color.RED)
+ @JvmStatic
+ val COLOR_BLACK = Color.valueOf(Color.BLACK)
+ @JvmStatic
+ val COLOR_WHITE_RESOURCE_ID = android.R.color.white
+ @JvmStatic
+ val COLOR_BLACK_RESOURCE_ID = android.R.color.black
+ }
+
+ @Test
+ fun `default background color is used if override is not set`() {
+ runTestScenario { r ->
+ r.setDefaultBackgroundColorId(COLOR_WHITE_RESOURCE_ID)
+ r.loadConfiguration()
+ r.checkBackgroundColor(COLOR_WHITE)
+ }
+ }
+
+ @Test
+ fun `overridden background color is used if set`() {
+ runTestScenario { r ->
+ r.setDefaultBackgroundColorId(COLOR_WHITE_RESOURCE_ID)
+ r.loadConfiguration()
+ r.overrideBackgroundColor(COLOR_RED)
+ r.checkBackgroundColor(COLOR_RED)
+ }
+ }
+
+ @Test
+ fun `overridden background color resource is used if set without override`() {
+ runTestScenario { r ->
+ r.setDefaultBackgroundColorId(COLOR_WHITE_RESOURCE_ID)
+ r.loadConfiguration()
+ r.overrideBackgroundColorId(COLOR_BLACK_RESOURCE_ID)
+ r.checkBackgroundColor(COLOR_BLACK)
+ }
+ }
+
+ @Test
+ fun `overridden background color has precedence over color id`() {
+ runTestScenario { r ->
+ r.setDefaultBackgroundColorId(COLOR_WHITE_RESOURCE_ID)
+ r.loadConfiguration()
+ r.overrideBackgroundColor(COLOR_RED)
+ r.overrideBackgroundColorId(COLOR_BLACK_RESOURCE_ID)
+ r.checkBackgroundColor(COLOR_RED)
+ }
+ }
+
+ @Test
+ fun `reset background color`() {
+ runTestScenario { r ->
+ r.setDefaultBackgroundColorId(COLOR_WHITE_RESOURCE_ID)
+ r.loadConfiguration()
+ r.overrideBackgroundColor(COLOR_RED)
+ r.checkBackgroundColor(COLOR_RED)
+
+ r.resetBackgroundColor()
+ r.checkBackgroundColor(COLOR_WHITE)
+
+ r.overrideBackgroundColorId(COLOR_BLACK_RESOURCE_ID)
+ r.checkBackgroundColor(COLOR_BLACK)
+
+ r.resetBackgroundColor()
+ r.checkBackgroundColor(COLOR_WHITE)
+ }
+ }
+
+ /**
+ * Runs a test scenario providing a Robot.
+ */
+ fun runTestScenario(consumer: Consumer<LetterboxConfigurationRobotTest>) {
+ val robot = LetterboxConfigurationRobotTest(mContext)
+ consumer.accept(robot)
+ }
+
+ class LetterboxConfigurationRobotTest(private val ctx: Context) {
+
+ private val resources: Resources
+ private lateinit var letterboxConfig: LetterboxConfiguration
+
+ init {
+ resources = ctx.resources
+ spyOn(resources)
+ }
+
+ fun setDefaultBackgroundColorId(@ColorRes colorId: Int) {
+ doReturn(colorId).`when`(resources)
+ .getColor(R.color.config_letterboxBackgroundColor, null)
+ }
+
+ fun loadConfiguration() {
+ letterboxConfig = LetterboxConfiguration(ctx)
+ }
+
+ fun overrideBackgroundColor(color: Color) {
+ letterboxConfig.setLetterboxBackgroundColor(color)
+ }
+
+ fun resetBackgroundColor() {
+ letterboxConfig.resetLetterboxBackgroundColor()
+ }
+
+ fun overrideBackgroundColorId(@ColorRes colorId: Int) {
+ letterboxConfig.setLetterboxBackgroundColorResourceId(colorId)
+ }
+
+ fun checkBackgroundColor(expected: Color) {
+ val colorComponents = letterboxConfig.getBackgroundColorRgbArray()
+ val expectedComponents = expected.components
+ assert(expectedComponents.contentEquals(colorComponents))
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxTransitionObserverTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxTransitionObserverTest.kt
new file mode 100644
index 0000000..1ae1c3f
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxTransitionObserverTest.kt
@@ -0,0 +1,294 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.compatui.letterbox
+
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.SetFlagsRule
+import android.testing.AndroidTestingRunner
+import android.view.WindowManager.TRANSIT_CLOSE
+import androidx.test.filters.SmallTest
+import com.android.window.flags.Flags
+import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.common.ShellExecutor
+import com.android.wm.shell.sysui.ShellInit
+import com.android.wm.shell.transition.Transitions
+import com.android.wm.shell.util.TransitionObserverInputBuilder
+import com.android.wm.shell.util.executeTransitionObserverTest
+import java.util.function.Consumer
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito
+import org.mockito.kotlin.any
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.never
+import org.mockito.kotlin.times
+import org.mockito.kotlin.anyOrNull
+import org.mockito.verification.VerificationMode
+
+/**
+ * Tests for [LetterboxTransitionObserver].
+ *
+ * Build/Install/Run:
+ * atest WMShellUnitTests:LetterboxTransitionObserverTest
+ */
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class LetterboxTransitionObserverTest : ShellTestCase() {
+
+ @get:Rule
+ val setFlagsRule: SetFlagsRule = SetFlagsRule()
+
+ @Test
+ @DisableFlags(Flags.FLAG_APP_COMPAT_REFACTORING)
+ fun `when initialized and flag disabled the observer is not registered`() {
+ runTestScenario { r ->
+ executeTransitionObserverTest(observerFactory = r.observerFactory) {
+ r.invokeShellInit()
+ r.checkObservableIsRegistered(expected = false)
+ }
+ }
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_APP_COMPAT_REFACTORING)
+ fun `when initialized and flag enabled the observer is registered`() {
+ runTestScenario { r ->
+ executeTransitionObserverTest(observerFactory = r.observerFactory) {
+ r.invokeShellInit()
+ r.checkObservableIsRegistered(expected = true)
+ }
+ }
+ }
+
+ @Test
+ fun `LetterboxController not used without TaskInfos in Change`() {
+ runTestScenario { r ->
+ executeTransitionObserverTest(observerFactory = r.observerFactory) {
+ r.invokeShellInit()
+
+ inputBuilder {
+ buildTransitionInfo()
+ addChange(createChange())
+ addChange(createChange())
+ addChange(createChange())
+ }
+
+ validateOutput {
+ r.creationEventDetected(expected = false)
+ r.visibilityEventDetected(expected = false)
+ r.destroyEventDetected(expected = false)
+ r.boundsEventDetected(expected = false)
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `When a topActivity is letterboxed surfaces creation is requested`() {
+ runTestScenario { r ->
+ executeTransitionObserverTest(observerFactory = r.observerFactory) {
+ r.invokeShellInit()
+
+ inputBuilder {
+ buildTransitionInfo()
+ r.createTopActivityChange(inputBuilder = this, isLetterboxed = true)
+ }
+
+ validateOutput {
+ r.creationEventDetected(expected = true)
+ r.visibilityEventDetected(expected = true, visible = true)
+ r.destroyEventDetected(expected = false)
+ r.boundsEventDetected(expected = true)
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `When a topActivity is not letterboxed visibility is updated`() {
+ runTestScenario { r ->
+ executeTransitionObserverTest(observerFactory = r.observerFactory) {
+ r.invokeShellInit()
+
+ inputBuilder {
+ buildTransitionInfo()
+ r.createTopActivityChange(inputBuilder = this, isLetterboxed = false)
+ }
+
+ validateOutput {
+ r.creationEventDetected(expected = false)
+ r.visibilityEventDetected(expected = true, visible = false)
+ r.destroyEventDetected(expected = false)
+ r.boundsEventDetected(expected = false)
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `When closing change letterbox surface destroy is triggered`() {
+ runTestScenario { r ->
+ executeTransitionObserverTest(observerFactory = r.observerFactory) {
+ r.invokeShellInit()
+
+ inputBuilder {
+ buildTransitionInfo()
+ r.createClosingChange(inputBuilder = this)
+ }
+
+ validateOutput {
+ r.destroyEventDetected(expected = true)
+ r.creationEventDetected(expected = false)
+ r.visibilityEventDetected(expected = false, visible = false)
+ r.boundsEventDetected(expected = false)
+ }
+ }
+ }
+ }
+
+ /**
+ * Runs a test scenario providing a Robot.
+ */
+ fun runTestScenario(consumer: Consumer<LetterboxTransitionObserverRobotTest>) {
+ val robot = LetterboxTransitionObserverRobotTest()
+ consumer.accept(robot)
+ }
+
+ class LetterboxTransitionObserverRobotTest {
+
+ companion object {
+ @JvmStatic
+ private val DISPLAY_ID = 1
+
+ @JvmStatic
+ private val TASK_ID = 20
+ }
+
+ private val executor: ShellExecutor
+ private val shellInit: ShellInit
+ private val transitions: Transitions
+ private val letterboxController: LetterboxController
+ private val letterboxObserver: LetterboxTransitionObserver
+
+ val observerFactory: () -> LetterboxTransitionObserver
+
+ init {
+ executor = Mockito.mock(ShellExecutor::class.java)
+ shellInit = ShellInit(executor)
+ transitions = Mockito.mock(Transitions::class.java)
+ letterboxController = Mockito.mock(LetterboxController::class.java)
+ letterboxObserver =
+ LetterboxTransitionObserver(shellInit, transitions, letterboxController)
+ observerFactory = { letterboxObserver }
+ }
+
+ fun invokeShellInit() = shellInit.init()
+
+ fun observer() = letterboxObserver
+
+ fun checkObservableIsRegistered(expected: Boolean) {
+ Mockito.verify(transitions, expected.asMode()).registerObserver(observer())
+ }
+
+ fun creationEventDetected(
+ expected: Boolean,
+ displayId: Int = DISPLAY_ID,
+ taskId: Int = TASK_ID
+ ) {
+ Mockito.verify(letterboxController, expected.asMode()).createLetterboxSurface(
+ toLetterboxKeyMatcher(displayId, taskId),
+ anyOrNull(),
+ anyOrNull()
+ )
+ }
+
+ fun visibilityEventDetected(
+ expected: Boolean,
+ displayId: Int = DISPLAY_ID,
+ taskId: Int = TASK_ID,
+ visible: Boolean? = null
+ ) {
+ Mockito.verify(letterboxController, expected.asMode()).updateLetterboxSurfaceVisibility(
+ toLetterboxKeyMatcher(displayId, taskId),
+ anyOrNull(),
+ visible.asMatcher()
+ )
+ }
+
+ fun destroyEventDetected(
+ expected: Boolean,
+ displayId: Int = DISPLAY_ID,
+ taskId: Int = TASK_ID
+ ) {
+ Mockito.verify(letterboxController, expected.asMode()).destroyLetterboxSurface(
+ toLetterboxKeyMatcher(displayId, taskId),
+ anyOrNull()
+ )
+ }
+
+ fun boundsEventDetected(
+ expected: Boolean,
+ displayId: Int = DISPLAY_ID,
+ taskId: Int = TASK_ID
+ ) {
+ Mockito.verify(letterboxController, expected.asMode()).updateLetterboxSurfaceBounds(
+ toLetterboxKeyMatcher(displayId, taskId),
+ anyOrNull(),
+ anyOrNull()
+ )
+ }
+
+ fun createTopActivityChange(
+ inputBuilder: TransitionObserverInputBuilder,
+ isLetterboxed: Boolean = true,
+ displayId: Int = DISPLAY_ID,
+ taskId: Int = TASK_ID
+ ) {
+ inputBuilder.addChange(changeTaskInfo = inputBuilder.createTaskInfo().apply {
+ appCompatTaskInfo.isTopActivityLetterboxed = isLetterboxed
+ this.taskId = taskId
+ this.displayId = displayId
+ })
+ }
+
+ fun createClosingChange(
+ inputBuilder: TransitionObserverInputBuilder,
+ displayId: Int = DISPLAY_ID,
+ taskId: Int = TASK_ID
+ ) {
+ inputBuilder.addChange(changeTaskInfo = inputBuilder.createTaskInfo().apply {
+ this.taskId = taskId
+ this.displayId = displayId
+ }, changeMode = TRANSIT_CLOSE)
+ }
+
+ private fun Boolean.asMode(): VerificationMode = if (this) times(1) else never()
+
+ private fun Boolean?.asMatcher(): Boolean =
+ if (this != null) eq(this) else any()
+
+ private fun toLetterboxKeyMatcher(displayId: Int, taskId: Int): LetterboxKey {
+ if (displayId < 0 || taskId < 0) {
+ return any()
+ } else {
+ return eq(LetterboxKey(displayId, taskId))
+ }
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandlerTest.kt
index f21f264..62717a3 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandlerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandlerTest.kt
@@ -31,6 +31,8 @@
import android.testing.TestableLooper.RunWithLooper
import android.view.SurfaceControl
import android.view.WindowManager
+import android.view.WindowManager.TRANSIT_CHANGE
+import android.view.WindowManager.TRANSIT_NONE
import android.view.WindowManager.TRANSIT_OPEN
import android.view.WindowManager.TRANSIT_TO_BACK
import android.view.WindowManager.TransitionType
@@ -47,6 +49,7 @@
import com.android.wm.shell.freeform.FreeformTaskTransitionHandler
import com.android.wm.shell.sysui.ShellInit
import com.android.wm.shell.transition.Transitions
+import com.android.wm.shell.util.StubTransaction
import com.google.common.truth.Truth.assertThat
import org.junit.Assert.assertFalse
import org.junit.Assert.assertNull
@@ -491,6 +494,72 @@
}
@Test
+ @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
+ fun startLaunchTransition_unknownLaunchingTask_animates() {
+ val wct = WindowContainerTransaction()
+ val task = createTask(WINDOWING_MODE_FREEFORM)
+ val transition = Binder()
+ whenever(transitions.startTransition(eq(TRANSIT_OPEN), eq(wct), anyOrNull()))
+ .thenReturn(transition)
+ whenever(transitions.dispatchTransition(eq(transition), any(), any(), any(), any(), any()))
+ .thenReturn(mock())
+
+ mixedHandler.startLaunchTransition(
+ transitionType = TRANSIT_OPEN,
+ wct = wct,
+ taskId = null,
+ )
+
+ val started = mixedHandler.startAnimation(
+ transition,
+ createTransitionInfo(
+ TRANSIT_OPEN,
+ listOf(createChange(task, mode = TRANSIT_OPEN))
+ ),
+ StubTransaction(),
+ StubTransaction(),
+ ) { }
+
+ assertThat(started).isEqualTo(true)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
+ fun startLaunchTransition_unknownLaunchingTaskOverImmersive_animatesImmersiveChange() {
+ val wct = WindowContainerTransaction()
+ val immersiveTask = createTask(WINDOWING_MODE_FREEFORM)
+ val openingTask = createTask(WINDOWING_MODE_FREEFORM)
+ val transition = Binder()
+ whenever(transitions.startTransition(eq(TRANSIT_OPEN), eq(wct), anyOrNull()))
+ .thenReturn(transition)
+ whenever(transitions.dispatchTransition(eq(transition), any(), any(), any(), any(), any()))
+ .thenReturn(mock())
+
+ mixedHandler.startLaunchTransition(
+ transitionType = TRANSIT_OPEN,
+ wct = wct,
+ taskId = null,
+ exitingImmersiveTask = immersiveTask.taskId,
+ )
+
+ val immersiveChange = createChange(immersiveTask, mode = TRANSIT_CHANGE)
+ val openingChange = createChange(openingTask, mode = TRANSIT_OPEN)
+ val started = mixedHandler.startAnimation(
+ transition,
+ createTransitionInfo(
+ TRANSIT_OPEN,
+ listOf(immersiveChange, openingChange)
+ ),
+ StubTransaction(),
+ StubTransaction(),
+ ) { }
+
+ assertThat(started).isEqualTo(true)
+ verify(desktopImmersiveController)
+ .animateResizeChange(eq(immersiveChange), any(), any(), any())
+ }
+
+ @Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS)
fun addPendingAndAnimateLaunchTransition_noMinimizeChange_doesNotReparentMinimizeChange() {
val wct = WindowContainerTransaction()
@@ -712,9 +781,13 @@
changes.forEach { change -> addChange(change) }
}
- private fun createChange(task: RunningTaskInfo): TransitionInfo.Change =
+ private fun createChange(
+ task: RunningTaskInfo,
+ @TransitionInfo.TransitionMode mode: Int = TRANSIT_NONE
+ ): TransitionInfo.Change =
TransitionInfo.Change(task.token, SurfaceControl()).apply {
taskInfo = task
+ setMode(mode)
}
private fun createTask(@WindowingMode windowingMode: Int): RunningTaskInfo =
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeKeyGestureHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeKeyGestureHandlerTest.kt
new file mode 100644
index 0000000..9e63a6d
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeKeyGestureHandlerTest.kt
@@ -0,0 +1,312 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.desktopmode
+
+import android.app.ActivityManager.RunningTaskInfo
+import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN
+import android.content.pm.ActivityInfo
+import android.graphics.Rect
+import android.hardware.input.InputManager
+import android.hardware.input.InputManager.KeyGestureEventHandler
+import android.hardware.input.KeyGestureEvent
+import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.SetFlagsRule
+import android.testing.AndroidTestingRunner
+import android.view.Display.DEFAULT_DISPLAY
+import android.view.KeyEvent
+import android.window.DisplayAreaInfo
+import androidx.test.filters.SmallTest
+import com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER
+import com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE
+import com.android.window.flags.Flags.FLAG_ENABLE_DISPLAY_FOCUS_IN_SHELL_TRANSITIONS
+import com.android.window.flags.Flags.FLAG_ENABLE_MOVE_TO_NEXT_DISPLAY_SHORTCUT
+import com.android.wm.shell.MockToken
+import com.android.wm.shell.RootTaskDisplayAreaOrganizer
+import com.android.wm.shell.ShellTaskOrganizer
+import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFreeformTask
+import com.android.wm.shell.transition.FocusTransitionObserver
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.whenever
+import com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer
+import com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn
+import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
+import com.android.dx.mockito.inline.extended.StaticMockitoSession
+import com.android.window.flags.Flags.FLAG_ENABLE_TASK_RESIZING_KEYBOARD_SHORTCUTS
+import com.android.wm.shell.common.DisplayController
+import com.android.wm.shell.common.DisplayLayout
+import com.android.wm.shell.common.ShellExecutor
+import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ResizeTrigger
+import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
+import com.android.wm.shell.sysui.ShellInit
+import com.android.wm.shell.windowdecor.DesktopModeWindowDecorViewModel
+import java.util.Optional
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.SupervisorJob
+import kotlinx.coroutines.cancel
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.setMain
+import org.junit.After
+import org.mockito.Mockito.anyInt
+import org.mockito.Mockito.spy
+import org.mockito.Mockito.verify
+import org.mockito.kotlin.any
+import org.mockito.kotlin.mock
+import org.mockito.quality.Strictness
+
+/**
+ * Test class for [DesktopModeKeyGestureHandler]
+ *
+ * Usage: atest WMShellUnitTests:DesktopModeKeyGestureHandlerTest
+ */
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@ExperimentalCoroutinesApi
+@EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
+class DesktopModeKeyGestureHandlerTest : ShellTestCase() {
+
+ @JvmField @Rule val setFlagsRule = SetFlagsRule()
+
+ private val rootTaskDisplayAreaOrganizer = mock<RootTaskDisplayAreaOrganizer>()
+ private val shellTaskOrganizer = mock<ShellTaskOrganizer>()
+ private val focusTransitionObserver = mock<FocusTransitionObserver>()
+ private val testExecutor = mock<ShellExecutor>()
+ private val inputManager = mock<InputManager>()
+ private val displayController = mock<DisplayController>()
+ private val displayLayout = mock<DisplayLayout>()
+ private val desktopModeWindowDecorViewModel = mock<DesktopModeWindowDecorViewModel>()
+ private val desktopTasksController = mock<DesktopTasksController>()
+
+ private lateinit var desktopModeKeyGestureHandler: DesktopModeKeyGestureHandler
+ private lateinit var keyGestureEventHandler: KeyGestureEventHandler
+ private lateinit var mockitoSession: StaticMockitoSession
+ private lateinit var testScope: CoroutineScope
+ private lateinit var shellInit: ShellInit
+
+ // Mock running tasks are registered here so we can get the list from mock shell task organizer
+ private val runningTasks = mutableListOf<RunningTaskInfo>()
+
+ @Before
+ fun setUp() {
+ Dispatchers.setMain(StandardTestDispatcher())
+ mockitoSession =
+ mockitoSession()
+ .strictness(Strictness.LENIENT)
+ .spyStatic(DesktopModeStatus::class.java)
+ .startMocking()
+ doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
+
+ testScope = CoroutineScope(Dispatchers.Unconfined + SupervisorJob())
+ shellInit = spy(ShellInit(testExecutor))
+
+ whenever(shellTaskOrganizer.getRunningTasks(anyInt())).thenAnswer { runningTasks }
+ whenever(displayController.getDisplayLayout(anyInt())).thenReturn(displayLayout)
+ whenever(displayLayout.getStableBounds(any())).thenAnswer { i ->
+ (i.arguments.first() as Rect).set(STABLE_BOUNDS)
+ }
+ val tda = DisplayAreaInfo(MockToken().token(), DEFAULT_DISPLAY, 0)
+ tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FULLSCREEN
+ whenever(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)).thenReturn(tda)
+
+ doAnswer {
+ keyGestureEventHandler = (it.arguments[0] as KeyGestureEventHandler)
+ null
+ }.whenever(inputManager).registerKeyGestureEventHandler(any())
+ shellInit.init()
+ }
+
+ @After
+ fun tearDown() {
+ mockitoSession.finishMocking()
+
+ runningTasks.clear()
+ testScope.cancel()
+ }
+
+ @Test
+ @EnableFlags(
+ FLAG_ENABLE_DISPLAY_FOCUS_IN_SHELL_TRANSITIONS,
+ FLAG_ENABLE_MOVE_TO_NEXT_DISPLAY_SHORTCUT,
+ FLAG_USE_KEY_GESTURE_EVENT_HANDLER
+ )
+ fun keyGestureMoveToNextDisplay_shouldMoveToNextDisplay() {
+ desktopModeKeyGestureHandler = DesktopModeKeyGestureHandler(
+ context,
+ Optional.of(desktopModeWindowDecorViewModel), Optional.of(desktopTasksController),
+ inputManager, shellTaskOrganizer, focusTransitionObserver
+ )
+ // Set up two display ids
+ whenever(rootTaskDisplayAreaOrganizer.displayIds)
+ .thenReturn(intArrayOf(DEFAULT_DISPLAY, SECOND_DISPLAY))
+ // Create a mock for the target display area: default display
+ val defaultDisplayArea = DisplayAreaInfo(MockToken().token(), DEFAULT_DISPLAY, 0)
+ whenever(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY))
+ .thenReturn(defaultDisplayArea)
+ // Setup a focused task on secondary display, which is expected to move to default display
+ val task = setUpFreeformTask(displayId = SECOND_DISPLAY)
+ task.isFocused = true
+ whenever(shellTaskOrganizer.getRunningTasks()).thenReturn(arrayListOf(task))
+ whenever(focusTransitionObserver.hasGlobalFocus(eq(task))).thenReturn(true)
+
+ val event = KeyGestureEvent.Builder()
+ .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_MOVE_TO_NEXT_DISPLAY)
+ .setDisplayId(SECOND_DISPLAY)
+ .setKeycodes(intArrayOf(KeyEvent.KEYCODE_D))
+ .setModifierState(KeyEvent.META_META_ON or KeyEvent.META_CTRL_ON)
+ .build()
+ val result = keyGestureEventHandler.handleKeyGestureEvent(event, null)
+
+ assertThat(result).isTrue()
+ verify(desktopTasksController).moveToNextDisplay(task.taskId)
+ }
+
+ @Test
+ @EnableFlags(
+ FLAG_USE_KEY_GESTURE_EVENT_HANDLER,
+ FLAG_ENABLE_TASK_RESIZING_KEYBOARD_SHORTCUTS
+ )
+ fun keyGestureSnapLeft_shouldSnapResizeTaskToLeft() {
+ desktopModeKeyGestureHandler = DesktopModeKeyGestureHandler(
+ context,
+ Optional.of(desktopModeWindowDecorViewModel), Optional.of(desktopTasksController),
+ inputManager, shellTaskOrganizer, focusTransitionObserver
+ )
+ val task = setUpFreeformTask()
+ task.isFocused = true
+ whenever(shellTaskOrganizer.getRunningTasks()).thenReturn(arrayListOf(task))
+ whenever(focusTransitionObserver.hasGlobalFocus(eq(task))).thenReturn(true)
+
+ val event = KeyGestureEvent.Builder()
+ .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_SNAP_LEFT_FREEFORM_WINDOW)
+ .setKeycodes(intArrayOf(KeyEvent.KEYCODE_LEFT_BRACKET))
+ .setModifierState(KeyEvent.META_META_ON)
+ .build()
+ val result = keyGestureEventHandler.handleKeyGestureEvent(event, null)
+
+ assertThat(result).isTrue()
+ verify(desktopModeWindowDecorViewModel).onSnapResize(task.taskId, true, null)
+ }
+
+ @Test
+ @EnableFlags(
+ FLAG_USE_KEY_GESTURE_EVENT_HANDLER,
+ FLAG_ENABLE_TASK_RESIZING_KEYBOARD_SHORTCUTS
+ )
+ fun keyGestureSnapRight_shouldSnapResizeTaskToRight() {
+ desktopModeKeyGestureHandler = DesktopModeKeyGestureHandler(
+ context,
+ Optional.of(desktopModeWindowDecorViewModel), Optional.of(desktopTasksController),
+ inputManager, shellTaskOrganizer, focusTransitionObserver
+ )
+ val task = setUpFreeformTask()
+ task.isFocused = true
+ whenever(shellTaskOrganizer.getRunningTasks()).thenReturn(arrayListOf(task))
+ whenever(focusTransitionObserver.hasGlobalFocus(eq(task))).thenReturn(true)
+
+ val event = KeyGestureEvent.Builder()
+ .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_SNAP_RIGHT_FREEFORM_WINDOW)
+ .setKeycodes(intArrayOf(KeyEvent.KEYCODE_RIGHT_BRACKET))
+ .setModifierState(KeyEvent.META_META_ON)
+ .build()
+ val result = keyGestureEventHandler.handleKeyGestureEvent(event, null)
+
+ assertThat(result).isTrue()
+ verify(desktopModeWindowDecorViewModel).onSnapResize(task.taskId, false, null)
+ }
+
+ @Test
+ @EnableFlags(
+ FLAG_USE_KEY_GESTURE_EVENT_HANDLER,
+ FLAG_ENABLE_TASK_RESIZING_KEYBOARD_SHORTCUTS
+ )
+ fun keyGestureToggleFreeformWindowSize_shouldToggleTaskSize() {
+ desktopModeKeyGestureHandler = DesktopModeKeyGestureHandler(
+ context,
+ Optional.of(desktopModeWindowDecorViewModel), Optional.of(desktopTasksController),
+ inputManager, shellTaskOrganizer, focusTransitionObserver
+ )
+ val task = setUpFreeformTask()
+ task.isFocused = true
+ whenever(shellTaskOrganizer.getRunningTasks()).thenReturn(arrayListOf(task))
+ whenever(focusTransitionObserver.hasGlobalFocus(eq(task))).thenReturn(true)
+
+ val event = KeyGestureEvent.Builder()
+ .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_MAXIMIZE_FREEFORM_WINDOW)
+ .setKeycodes(intArrayOf(KeyEvent.KEYCODE_EQUALS))
+ .setModifierState(KeyEvent.META_META_ON)
+ .build()
+ val result = keyGestureEventHandler.handleKeyGestureEvent(event, null)
+
+ assertThat(result).isTrue()
+ verify(desktopTasksController).toggleDesktopTaskSize(
+ task,
+ ResizeTrigger.MAXIMIZE_MENU,
+ null
+ )
+ }
+
+ @Test
+ @EnableFlags(
+ FLAG_USE_KEY_GESTURE_EVENT_HANDLER,
+ FLAG_ENABLE_TASK_RESIZING_KEYBOARD_SHORTCUTS
+ )
+ fun keyGestureMinimizeFreeformWindow_shouldMinimizeTask() {
+ desktopModeKeyGestureHandler = DesktopModeKeyGestureHandler(
+ context,
+ Optional.of(desktopModeWindowDecorViewModel), Optional.of(desktopTasksController),
+ inputManager, shellTaskOrganizer, focusTransitionObserver
+ )
+ val task = setUpFreeformTask()
+ task.isFocused = true
+ whenever(shellTaskOrganizer.getRunningTasks()).thenReturn(arrayListOf(task))
+ whenever(focusTransitionObserver.hasGlobalFocus(eq(task))).thenReturn(true)
+
+ val event = KeyGestureEvent.Builder()
+ .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_MINIMIZE_FREEFORM_WINDOW)
+ .setKeycodes(intArrayOf(KeyEvent.KEYCODE_MINUS))
+ .setModifierState(KeyEvent.META_META_ON)
+ .build()
+ val result = keyGestureEventHandler.handleKeyGestureEvent(event, null)
+
+ assertThat(result).isTrue()
+ verify(desktopTasksController).minimizeTask(task)
+ }
+
+ private fun setUpFreeformTask(
+ displayId: Int = DEFAULT_DISPLAY,
+ bounds: Rect? = null,
+ ): RunningTaskInfo {
+ val task = createFreeformTask(displayId, bounds)
+ val activityInfo = ActivityInfo()
+ task.topActivityInfo = activityInfo
+ whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task)
+ runningTasks.add(task)
+ return task
+ }
+
+ private companion object {
+ const val SECOND_DISPLAY = 2
+ val STABLE_BOUNDS = Rect(0, 0, 1000, 1000)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
index ad266ea..5df3957 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
@@ -36,12 +36,10 @@
import android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
import android.content.res.Configuration.ORIENTATION_LANDSCAPE
import android.content.res.Configuration.ORIENTATION_PORTRAIT
+import android.content.res.Resources
import android.graphics.Point
import android.graphics.PointF
import android.graphics.Rect
-import android.hardware.input.InputManager
-import android.hardware.input.InputManager.KeyGestureEventHandler
-import android.hardware.input.KeyGestureEvent
import android.os.Binder
import android.os.Bundle
import android.os.Handler
@@ -53,7 +51,6 @@
import android.view.Display.DEFAULT_DISPLAY
import android.view.DragEvent
import android.view.Gravity
-import android.view.KeyEvent
import android.view.MotionEvent
import android.view.SurfaceControl
import android.view.WindowInsets
@@ -63,6 +60,7 @@
import android.view.WindowManager.TRANSIT_OPEN
import android.view.WindowManager.TRANSIT_TO_BACK
import android.view.WindowManager.TRANSIT_TO_FRONT
+import android.widget.Toast
import android.window.DisplayAreaInfo
import android.window.IWindowContainerToken
import android.window.RemoteTransition
@@ -75,18 +73,14 @@
import android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER
import android.window.WindowContainerTransaction.HierarchyOp.LAUNCH_KEY_TASK_ID
import androidx.test.filters.SmallTest
-import com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer
import com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn
import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
import com.android.dx.mockito.inline.extended.ExtendedMockito.never
import com.android.dx.mockito.inline.extended.StaticMockitoSession
-import com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER
import com.android.internal.jank.InteractionJankMonitor
import com.android.window.flags.Flags
import com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE
-import com.android.window.flags.Flags.FLAG_ENABLE_DISPLAY_FOCUS_IN_SHELL_TRANSITIONS
import com.android.window.flags.Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP
-import com.android.window.flags.Flags.FLAG_ENABLE_MOVE_TO_NEXT_DISPLAY_SHORTCUT
import com.android.wm.shell.MockToken
import com.android.wm.shell.R
import com.android.wm.shell.RootTaskDisplayAreaOrganizer
@@ -96,17 +90,20 @@
import com.android.wm.shell.TestShellExecutor
import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.common.DisplayLayout
-import com.android.wm.shell.common.LaunchAdjacentController
import com.android.wm.shell.common.MultiInstanceHelper
import com.android.wm.shell.common.ShellExecutor
import com.android.wm.shell.common.SyncTransactionQueue
+import com.android.wm.shell.desktopmode.DesktopImmersiveController.ExitResult
import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ResizeTrigger
+import com.android.wm.shell.desktopmode.DesktopTasksController.DesktopModeEntryExitTransitionListener
import com.android.wm.shell.desktopmode.DesktopTasksController.SnapPosition
import com.android.wm.shell.desktopmode.DesktopTasksController.TaskbarDesktopTaskListener
import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFreeformTask
import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFullscreenTask
import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createHomeTask
import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createSplitScreenTask
+import com.android.wm.shell.desktopmode.EnterDesktopTaskTransitionHandler.FREEFORM_ANIMATION_DURATION
+import com.android.wm.shell.desktopmode.ExitDesktopTaskTransitionHandler.FULLSCREEN_ANIMATION_DURATION
import com.android.wm.shell.desktopmode.minimize.DesktopWindowLimitRemoteHandler
import com.android.wm.shell.desktopmode.persistence.Desktop
import com.android.wm.shell.desktopmode.persistence.DesktopPersistentRepository
@@ -119,11 +116,11 @@
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource.UNKNOWN
import com.android.wm.shell.shared.split.SplitScreenConstants
+import com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_INDEX_UNDEFINED
import com.android.wm.shell.splitscreen.SplitScreenController
import com.android.wm.shell.sysui.ShellCommandHandler
import com.android.wm.shell.sysui.ShellController
import com.android.wm.shell.sysui.ShellInit
-import com.android.wm.shell.transition.FocusTransitionObserver
import com.android.wm.shell.transition.OneShotRemoteHandler
import com.android.wm.shell.transition.TestRemoteTransition
import com.android.wm.shell.transition.Transitions
@@ -167,6 +164,7 @@
import org.mockito.Mockito.times
import org.mockito.kotlin.any
import org.mockito.kotlin.anyOrNull
+import org.mockito.kotlin.argumentCaptor
import org.mockito.kotlin.atLeastOnce
import org.mockito.kotlin.capture
import org.mockito.kotlin.eq
@@ -206,12 +204,10 @@
@Mock lateinit var dragToDesktopTransitionHandler: DragToDesktopTransitionHandler
@Mock
lateinit var mMockDesktopImmersiveController: DesktopImmersiveController
- @Mock lateinit var launchAdjacentController: LaunchAdjacentController
@Mock lateinit var splitScreenController: SplitScreenController
@Mock lateinit var recentsTransitionHandler: RecentsTransitionHandler
@Mock lateinit var dragAndDropController: DragAndDropController
@Mock lateinit var multiInstanceHelper: MultiInstanceHelper
- @Mock lateinit var desktopModeLoggerTransitionObserver: DesktopModeLoggerTransitionObserver
@Mock lateinit var desktopModeVisualIndicator: DesktopModeVisualIndicator
@Mock lateinit var recentTasksController: RecentTasksController
@Mock
@@ -222,23 +218,23 @@
@Mock private lateinit var mockHandler: Handler
@Mock private lateinit var desktopModeEventLogger: DesktopModeEventLogger
@Mock lateinit var persistentRepository: DesktopPersistentRepository
- @Mock private lateinit var mockInputManager: InputManager
- @Mock private lateinit var mockFocusTransitionObserver: FocusTransitionObserver
@Mock lateinit var motionEvent: MotionEvent
@Mock lateinit var repositoryInitializer: DesktopRepositoryInitializer
-
+ @Mock private lateinit var mockToast: Toast
private lateinit var mockitoSession: StaticMockitoSession
@Mock
private lateinit var desktopTilingDecorViewModel: DesktopTilingDecorViewModel
@Mock
private lateinit var desktopWindowDecoration: DesktopModeWindowDecoration
+ @Mock private lateinit var resources: Resources
+ @Mock
+ lateinit var desktopModeEnterExitTransitionListener: DesktopModeEntryExitTransitionListener
private lateinit var controller: DesktopTasksController
private lateinit var shellInit: ShellInit
private lateinit var taskRepository: DesktopRepository
private lateinit var desktopTasksLimiter: DesktopTasksLimiter
private lateinit var recentsTransitionStateListener: RecentsTransitionStateListener
private lateinit var testScope: CoroutineScope
- private lateinit var keyGestureEventHandler: KeyGestureEventHandler
private val shellExecutor = TestShellExecutor()
@@ -261,6 +257,7 @@
mockitoSession()
.strictness(Strictness.LENIENT)
.spyStatic(DesktopModeStatus::class.java)
+ .spyStatic(Toast::class.java)
.startMocking()
doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
@@ -288,25 +285,22 @@
whenever(runBlocking { persistentRepository.readDesktop(any(), any()) }).thenReturn(
Desktop.getDefaultInstance()
)
+ doReturn(mockToast).`when` { Toast.makeText(any(), anyInt(), anyInt()) }
val tda = DisplayAreaInfo(MockToken().token(), DEFAULT_DISPLAY, 0)
tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FULLSCREEN
whenever(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)).thenReturn(tda)
whenever(mMockDesktopImmersiveController
.exitImmersiveIfApplicable(any(), any<RunningTaskInfo>()))
- .thenReturn(DesktopImmersiveController.ExitResult.NoExit)
+ .thenReturn(ExitResult.NoExit)
whenever(mMockDesktopImmersiveController
.exitImmersiveIfApplicable(any(), anyInt(), anyOrNull()))
- .thenReturn(DesktopImmersiveController.ExitResult.NoExit)
+ .thenReturn(ExitResult.NoExit)
controller = createController()
controller.setSplitScreenController(splitScreenController)
controller.freeformTaskTransitionStarter = freeformTaskTransitionStarter
-
- doAnswer {
- keyGestureEventHandler = (it.arguments[0] as KeyGestureEventHandler)
- null
- }.whenever(mockInputManager).registerKeyGestureEventHandler(any())
+ controller.desktopModeEnterExitTransitionListener = desktopModeEnterExitTransitionListener
shellInit.init()
@@ -341,8 +335,6 @@
dragToDesktopTransitionHandler,
mMockDesktopImmersiveController,
taskRepository,
- desktopModeLoggerTransitionObserver,
- launchAdjacentController,
recentsTransitionHandler,
multiInstanceHelper,
shellExecutor,
@@ -350,8 +342,6 @@
recentTasksController,
mockInteractionJankMonitor,
mockHandler,
- mockInputManager,
- mockFocusTransitionObserver,
desktopModeEventLogger,
desktopTilingDecorViewModel,
)
@@ -1071,6 +1061,7 @@
controller.moveRunningTaskToDesktop(task, transitionSource = UNKNOWN)
val wct = getLatestEnterDesktopWct()
assertThat(wct.changes[task.token.asBinder()]?.windowingMode).isEqualTo(WINDOWING_MODE_FREEFORM)
+ verify(desktopModeEnterExitTransitionListener).onEnterDesktopModeTransitionStarted(FREEFORM_ANIMATION_DURATION)
}
@Test
@@ -1082,12 +1073,14 @@
val wct = getLatestEnterDesktopWct()
assertThat(wct.changes[task.token.asBinder()]?.windowingMode)
.isEqualTo(WINDOWING_MODE_UNDEFINED)
+ verify(desktopModeEnterExitTransitionListener).onEnterDesktopModeTransitionStarted(FREEFORM_ANIMATION_DURATION)
}
@Test
fun moveTaskToDesktop_nonExistentTask_doesNothing() {
controller.moveTaskToDesktop(999, transitionSource = UNKNOWN)
verifyEnterDesktopWCTNotExecuted()
+ verify(desktopModeEnterExitTransitionListener, times(0)).onEnterDesktopModeTransitionStarted(anyInt())
}
@Test
@@ -1132,9 +1125,9 @@
}
controller.moveRunningTaskToDesktop(task, transitionSource = UNKNOWN)
-
val wct = getLatestEnterDesktopWct()
assertThat(wct.changes[task.token.asBinder()]?.windowingMode).isEqualTo(WINDOWING_MODE_FREEFORM)
+ verify(desktopModeEnterExitTransitionListener).onEnterDesktopModeTransitionStarted(FREEFORM_ANIMATION_DURATION)
}
@Test
@@ -1149,6 +1142,9 @@
controller.moveRunningTaskToDesktop(task, transitionSource = UNKNOWN)
verifyEnterDesktopWCTNotExecuted()
+ verify(desktopModeEnterExitTransitionListener, times(0)).onEnterDesktopModeTransitionStarted(
+ FREEFORM_ANIMATION_DURATION
+ )
}
@Test
@@ -1195,6 +1191,7 @@
val wct = getLatestEnterDesktopWct()
assertThat(wct.changes[task.token.asBinder()]?.windowingMode).isEqualTo(WINDOWING_MODE_FREEFORM)
+ verify(desktopModeEnterExitTransitionListener).onEnterDesktopModeTransitionStarted(FREEFORM_ANIMATION_DURATION)
}
@Test
@@ -1214,6 +1211,7 @@
assertThat(changes[fullscreenTask.token.asBinder()]?.windowingMode)
.isEqualTo(WINDOWING_MODE_FREEFORM)
}
+ verify(desktopModeEnterExitTransitionListener).onEnterDesktopModeTransitionStarted(FREEFORM_ANIMATION_DURATION)
}
@Test
@@ -1234,6 +1232,7 @@
assertThat(changes[fullscreenTask.token.asBinder()]?.windowingMode)
.isEqualTo(WINDOWING_MODE_FREEFORM)
}
+ verify(desktopModeEnterExitTransitionListener).onEnterDesktopModeTransitionStarted(FREEFORM_ANIMATION_DURATION)
}
@Test
@@ -1255,6 +1254,7 @@
assertThat(hierarchyOps.map { it.container })
.doesNotContain(freeformTaskSecond.token.asBinder())
}
+ verify(desktopModeEnterExitTransitionListener).onEnterDesktopModeTransitionStarted(FREEFORM_ANIMATION_DURATION)
}
@Test
@@ -1263,6 +1263,7 @@
controller.moveRunningTaskToDesktop(task, transitionSource = UNKNOWN)
val wct = getLatestEnterDesktopWct()
assertThat(wct.changes[task.token.asBinder()]?.windowingMode).isEqualTo(WINDOWING_MODE_FREEFORM)
+ verify(desktopModeEnterExitTransitionListener).onEnterDesktopModeTransitionStarted(FREEFORM_ANIMATION_DURATION)
verify(splitScreenController)
.prepareExitSplitScreen(any(), anyInt(), eq(SplitScreenController.EXIT_REASON_DESKTOP_MODE))
}
@@ -1273,6 +1274,7 @@
controller.moveRunningTaskToDesktop(task, transitionSource = UNKNOWN)
val wct = getLatestEnterDesktopWct()
assertThat(wct.changes[task.token.asBinder()]?.windowingMode).isEqualTo(WINDOWING_MODE_FREEFORM)
+ verify(desktopModeEnterExitTransitionListener).onEnterDesktopModeTransitionStarted(FREEFORM_ANIMATION_DURATION)
verify(splitScreenController, never())
.prepareExitSplitScreen(any(), anyInt(), eq(SplitScreenController.EXIT_REASON_DESKTOP_MODE))
}
@@ -1287,6 +1289,7 @@
controller.moveRunningTaskToDesktop(newTask, transitionSource = UNKNOWN)
val wct = getLatestEnterDesktopWct()
+ verify(desktopModeEnterExitTransitionListener).onEnterDesktopModeTransitionStarted(FREEFORM_ANIMATION_DURATION)
assertThat(wct.hierarchyOps.size).isEqualTo(MAX_TASK_LIMIT + 1) // visible tasks + home
wct.assertReorderAt(0, homeTask)
wct.assertReorderSequenceInRange(
@@ -1305,6 +1308,7 @@
controller.moveRunningTaskToDesktop(newTask, transitionSource = UNKNOWN)
val wct = getLatestEnterDesktopWct()
+ verify(desktopModeEnterExitTransitionListener).onEnterDesktopModeTransitionStarted(FREEFORM_ANIMATION_DURATION)
assertThat(wct.hierarchyOps.size).isEqualTo(MAX_TASK_LIMIT + 2) // tasks + home + wallpaper
// Move home to front
wct.assertReorderAt(0, homeTask)
@@ -1324,6 +1328,7 @@
tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FULLSCREEN
controller.moveToFullscreen(task.taskId, transitionSource = UNKNOWN)
val wct = getLatestExitDesktopWct()
+ verify(desktopModeEnterExitTransitionListener, times(1)).onExitDesktopModeTransitionStarted(FULLSCREEN_ANIMATION_DURATION)
assertThat(wct.changes[task.token.asBinder()]?.windowingMode)
.isEqualTo(WINDOWING_MODE_UNDEFINED)
}
@@ -1341,6 +1346,7 @@
val wct = getLatestExitDesktopWct()
val taskChange = assertNotNull(wct.changes[task.token.asBinder()])
+ verify(desktopModeEnterExitTransitionListener).onExitDesktopModeTransitionStarted(FULLSCREEN_ANIMATION_DURATION)
assertThat(taskChange.windowingMode).isEqualTo(WINDOWING_MODE_UNDEFINED)
// Removes wallpaper activity when leaving desktop
wct.assertRemoveAt(index = 0, wallpaperToken)
@@ -1355,6 +1361,7 @@
val wct = getLatestExitDesktopWct()
assertThat(wct.changes[task.token.asBinder()]?.windowingMode)
.isEqualTo(WINDOWING_MODE_FULLSCREEN)
+ verify(desktopModeEnterExitTransitionListener).onExitDesktopModeTransitionStarted(FULLSCREEN_ANIMATION_DURATION)
}
@Test
@@ -1371,6 +1378,7 @@
val wct = getLatestExitDesktopWct()
val taskChange = assertNotNull(wct.changes[task.token.asBinder()])
assertThat(taskChange.windowingMode).isEqualTo(WINDOWING_MODE_FULLSCREEN)
+ verify(desktopModeEnterExitTransitionListener).onExitDesktopModeTransitionStarted(FULLSCREEN_ANIMATION_DURATION)
// Removes wallpaper activity when leaving desktop
wct.assertRemoveAt(index = 0, wallpaperToken)
}
@@ -1391,6 +1399,7 @@
val wct = getLatestExitDesktopWct()
val task1Change = assertNotNull(wct.changes[task1.token.asBinder()])
assertThat(task1Change.windowingMode).isEqualTo(WINDOWING_MODE_UNDEFINED)
+ verify(desktopModeEnterExitTransitionListener).onExitDesktopModeTransitionStarted(FULLSCREEN_ANIMATION_DURATION)
// Does not remove wallpaper activity, as desktop still has a visible desktop task
assertThat(wct.hierarchyOps).isEmpty()
}
@@ -1405,13 +1414,13 @@
fun moveToFullscreen_secondDisplayTaskHasFreeform_secondDisplayNotAffected() {
val taskDefaultDisplay = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
val taskSecondDisplay = setUpFreeformTask(displayId = SECOND_DISPLAY)
-
controller.moveToFullscreen(taskDefaultDisplay.taskId, transitionSource = UNKNOWN)
with(getLatestExitDesktopWct()) {
assertThat(changes.keys).contains(taskDefaultDisplay.token.asBinder())
assertThat(changes.keys).doesNotContain(taskSecondDisplay.token.asBinder())
}
+ verify(desktopModeEnterExitTransitionListener).onExitDesktopModeTransitionStarted(FULLSCREEN_ANIMATION_DURATION)
}
@Test
@@ -1556,44 +1565,6 @@
}
@Test
- @EnableFlags(
- FLAG_ENABLE_DISPLAY_FOCUS_IN_SHELL_TRANSITIONS,
- FLAG_ENABLE_MOVE_TO_NEXT_DISPLAY_SHORTCUT,
- FLAG_USE_KEY_GESTURE_EVENT_HANDLER
- )
- fun moveToNextDisplay_withKeyGesture() {
- // Set up two display ids
- whenever(rootTaskDisplayAreaOrganizer.displayIds)
- .thenReturn(intArrayOf(DEFAULT_DISPLAY, SECOND_DISPLAY))
- // Create a mock for the target display area: default display
- val defaultDisplayArea = DisplayAreaInfo(MockToken().token(), DEFAULT_DISPLAY, 0)
- whenever(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY))
- .thenReturn(defaultDisplayArea)
- // Setup a focused task on secondary display, which is expected to move to default display
- val task = setUpFreeformTask(displayId = SECOND_DISPLAY)
- task.isFocused = true
- whenever(shellTaskOrganizer.getRunningTasks()).thenReturn(arrayListOf(task))
- whenever(mockFocusTransitionObserver.hasGlobalFocus(eq(task))).thenReturn(true)
-
- val event = KeyGestureEvent.Builder()
- .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_MOVE_TO_NEXT_DISPLAY)
- .setDisplayId(SECOND_DISPLAY)
- .setKeycodes(intArrayOf(KeyEvent.KEYCODE_D))
- .setModifierState(KeyEvent.META_META_ON or KeyEvent.META_CTRL_ON)
- .build()
- val result = keyGestureEventHandler.handleKeyGestureEvent(event, null)
-
- assertThat(result).isTrue()
- with(getLatestWct(type = TRANSIT_CHANGE)) {
- assertThat(hierarchyOps).hasSize(1)
- assertThat(hierarchyOps[0].container).isEqualTo(task.token.asBinder())
- assertThat(hierarchyOps[0].isReparent).isTrue()
- assertThat(hierarchyOps[0].newParent).isEqualTo(defaultDisplayArea.token.asBinder())
- assertThat(hierarchyOps[0].toTop).isTrue()
- }
- }
-
- @Test
fun getTaskWindowingMode() {
val fullscreenTask = setUpFullscreenTask()
val freeformTask = setUpFreeformTask()
@@ -1833,7 +1804,8 @@
whenever(freeformTaskTransitionStarter.startMinimizedModeTransition(any()))
.thenReturn(transition)
whenever(mMockDesktopImmersiveController.exitImmersiveIfApplicable(any(), eq(task)))
- .thenReturn(DesktopImmersiveController.ExitResult.Exit(
+ .thenReturn(
+ ExitResult.Exit(
exitingTask = task.taskId,
runOnTransitionStart = runOnTransit,
))
@@ -3187,7 +3159,7 @@
runOpenNewWindow(task)
verify(splitScreenController)
.startIntent(any(), anyInt(), any(), any(),
- optionsCaptor.capture(), anyOrNull(), eq(true)
+ optionsCaptor.capture(), anyOrNull(), eq(true), eq(SPLIT_INDEX_UNDEFINED)
)
assertThat(ActivityOptions.fromBundle(optionsCaptor.value).launchWindowingMode)
.isEqualTo(WINDOWING_MODE_MULTI_WINDOW)
@@ -3203,7 +3175,7 @@
verify(splitScreenController)
.startIntent(
any(), anyInt(), any(), any(),
- optionsCaptor.capture(), anyOrNull(), eq(true)
+ optionsCaptor.capture(), anyOrNull(), eq(true), eq(SPLIT_INDEX_UNDEFINED)
)
assertThat(ActivityOptions.fromBundle(optionsCaptor.value).launchWindowingMode)
.isEqualTo(WINDOWING_MODE_MULTI_WINDOW)
@@ -3214,13 +3186,43 @@
fun newWindow_fromFreeformAddsNewWindow() {
setUpLandscapeDisplay()
val task = setUpFreeformTask()
- val wctCaptor = ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
+ val wctCaptor = argumentCaptor<WindowContainerTransaction>()
+ val transition = Binder()
+ whenever(mMockDesktopImmersiveController
+ .exitImmersiveIfApplicable(any(), anyInt(), anyOrNull()))
+ .thenReturn(ExitResult.NoExit)
+ whenever(desktopMixedTransitionHandler
+ .startLaunchTransition(anyInt(), any(), anyOrNull(), anyOrNull(), anyOrNull()))
+ .thenReturn(transition)
+
runOpenNewWindow(task)
- verify(transitions).startTransition(anyInt(), wctCaptor.capture(), anyOrNull())
- assertThat(ActivityOptions.fromBundle(wctCaptor.value.hierarchyOps[0].launchOptions)
+
+ verify(desktopMixedTransitionHandler)
+ .startLaunchTransition(anyInt(), wctCaptor.capture(), anyOrNull(), anyOrNull(), anyOrNull())
+ assertThat(ActivityOptions.fromBundle(wctCaptor.firstValue.hierarchyOps[0].launchOptions)
.launchWindowingMode).isEqualTo(WINDOWING_MODE_FREEFORM)
}
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MULTI_INSTANCE_FEATURES)
+ fun newWindow_fromFreeform_exitsImmersiveIfNeeded() {
+ setUpLandscapeDisplay()
+ val immersiveTask = setUpFreeformTask()
+ val task = setUpFreeformTask()
+ val runOnStart = RunOnStartTransitionCallback()
+ val transition = Binder()
+ whenever(mMockDesktopImmersiveController
+ .exitImmersiveIfApplicable(any(), anyInt(), anyOrNull()))
+ .thenReturn(ExitResult.Exit(immersiveTask.taskId, runOnStart))
+ whenever(desktopMixedTransitionHandler
+ .startLaunchTransition(anyInt(), any(), anyOrNull(), anyOrNull(), anyOrNull()))
+ .thenReturn(transition)
+
+ runOpenNewWindow(task)
+
+ runOnStart.assertOnlyInvocation(transition)
+ }
+
private fun runOpenNewWindow(task: RunningTaskInfo) {
markTaskVisible(task)
task.baseActivity = mock(ComponentName::class.java)
@@ -3314,7 +3316,8 @@
.thenReturn(transition)
whenever(mMockDesktopImmersiveController
.exitImmersiveIfApplicable(any(), eq(immersiveTask.displayId), eq(freeformTask.taskId)))
- .thenReturn(DesktopImmersiveController.ExitResult.Exit(
+ .thenReturn(
+ ExitResult.Exit(
exitingTask = immersiveTask.taskId,
runOnTransitionStart = runOnStartTransit,
))
@@ -3431,7 +3434,7 @@
@Test
@DisableFlags(Flags.FLAG_DISABLE_NON_RESIZABLE_APP_SNAP_RESIZING, Flags.FLAG_ENABLE_TILE_RESIZING)
- fun handleSnapResizingTask_nonResizable_snapsToHalfScreen() {
+ fun handleSnapResizingTaskOnDrag_nonResizable_snapsToHalfScreen() {
val task = setUpFreeformTask(DEFAULT_DISPLAY, Rect(0, 0, 200, 100)).apply {
isResizeable = false
}
@@ -3440,7 +3443,7 @@
val expectedBounds =
Rect(STABLE_BOUNDS.left, STABLE_BOUNDS.top, STABLE_BOUNDS.right / 2, STABLE_BOUNDS.bottom)
- controller.handleSnapResizingTask(
+ controller.handleSnapResizingTaskOnDrag(
task, SnapPosition.LEFT, mockSurface, currentDragBounds, preDragBounds, motionEvent,
desktopWindowDecoration
@@ -3459,14 +3462,14 @@
@Test
@EnableFlags(Flags.FLAG_DISABLE_NON_RESIZABLE_APP_SNAP_RESIZING)
- fun handleSnapResizingTask_nonResizable_startsRepositionAnimation() {
+ fun handleSnapResizingTaskOnDrag_nonResizable_startsRepositionAnimation() {
val task = setUpFreeformTask(DEFAULT_DISPLAY, Rect(0, 0, 200, 100)).apply {
isResizeable = false
}
val preDragBounds = Rect(100, 100, 400, 500)
val currentDragBounds = Rect(0, 100, 300, 500)
- controller.handleSnapResizingTask(
+ controller.handleSnapResizingTaskOnDrag(
task, SnapPosition.LEFT, mockSurface, currentDragBounds, preDragBounds, motionEvent,
desktopWindowDecoration)
verify(mReturnToDragStartAnimator).start(
@@ -3486,6 +3489,59 @@
}
@Test
+ @EnableFlags(
+ Flags.FLAG_DISABLE_NON_RESIZABLE_APP_SNAP_RESIZING
+ )
+ fun handleInstantSnapResizingTask_nonResizable_animatorNotStartedAndShowsToast() {
+ val taskBounds = Rect(0, 0, 200, 100)
+ val task = setUpFreeformTask(DEFAULT_DISPLAY, taskBounds).apply {
+ isResizeable = false
+ }
+
+ controller.handleInstantSnapResizingTask(
+ task, SnapPosition.LEFT, ResizeTrigger.SNAP_LEFT_MENU, motionEvent, desktopWindowDecoration)
+
+ // Assert that task is NOT updated via WCT
+ verify(toggleResizeDesktopTaskTransitionHandler, never()).startTransition(any(), any())
+ verify(mockToast).show()
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_DISABLE_NON_RESIZABLE_APP_SNAP_RESIZING)
+ @DisableFlags(Flags.FLAG_ENABLE_TILE_RESIZING)
+ fun handleInstantSnapResizingTask_resizable_snapsToHalfScreenAndNotShowToast() {
+ val taskBounds = Rect(0, 0, 200, 100)
+ val task = setUpFreeformTask(DEFAULT_DISPLAY, taskBounds).apply {
+ isResizeable = true
+ }
+ val expectedBounds = Rect(
+ STABLE_BOUNDS.left, STABLE_BOUNDS.top, STABLE_BOUNDS.right / 2, STABLE_BOUNDS.bottom
+ )
+
+ controller.handleInstantSnapResizingTask(
+ task, SnapPosition.LEFT, ResizeTrigger.SNAP_LEFT_MENU, motionEvent, desktopWindowDecoration)
+
+ // Assert bounds set to half of the stable bounds
+ val wct = getLatestToggleResizeDesktopTaskWct(taskBounds)
+ assertThat(findBoundsChange(wct, task)).isEqualTo(expectedBounds)
+ verify(mockToast, never()).show()
+ verify(desktopModeEventLogger, times(1)).logTaskResizingStarted(
+ ResizeTrigger.SNAP_LEFT_MENU,
+ motionEvent,
+ task,
+ displayController
+ )
+ verify(desktopModeEventLogger, times(1)).logTaskResizingEnded(
+ ResizeTrigger.SNAP_LEFT_MENU,
+ motionEvent,
+ task,
+ expectedBounds.height(),
+ expectedBounds.width(),
+ displayController
+ )
+ }
+
+ @Test
fun toggleBounds_togglesToCalculatedBoundsForNonResizable() {
val bounds = Rect(0, 0, 200, 100)
val task = setUpFreeformTask(DEFAULT_DISPLAY, bounds).apply {
@@ -3719,7 +3775,8 @@
val transition = Binder()
whenever(mMockDesktopImmersiveController
.exitImmersiveIfApplicable(wct, task.displayId, task.taskId))
- .thenReturn(DesktopImmersiveController.ExitResult.Exit(
+ .thenReturn(
+ ExitResult.Exit(
exitingTask = 5,
runOnTransitionStart = runOnStartTransit,
))
@@ -3740,7 +3797,8 @@
val transition = Binder()
whenever(mMockDesktopImmersiveController
.exitImmersiveIfApplicable(wct, task.displayId, task.taskId))
- .thenReturn(DesktopImmersiveController.ExitResult.Exit(
+ .thenReturn(
+ ExitResult.Exit(
exitingTask = 5,
runOnTransitionStart = runOnStartTransit,
))
@@ -3760,7 +3818,8 @@
val transition = Binder()
whenever(mMockDesktopImmersiveController
.exitImmersiveIfApplicable(any(), eq(task.displayId), eq(task.taskId)))
- .thenReturn(DesktopImmersiveController.ExitResult.Exit(
+ .thenReturn(
+ ExitResult.Exit(
exitingTask = 5,
runOnTransitionStart = runOnStartTransit,
))
@@ -3782,7 +3841,8 @@
val transition = Binder()
whenever(mMockDesktopImmersiveController
.exitImmersiveIfApplicable(any(), eq(task.displayId), eq(task.taskId)))
- .thenReturn(DesktopImmersiveController.ExitResult.Exit(
+ .thenReturn(
+ ExitResult.Exit(
exitingTask = 5,
runOnTransitionStart = runOnStartTransit,
))
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/SplitDragPolicyTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/SplitDragPolicyTest.java
index 2cfce69..0cf15ba 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/SplitDragPolicyTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/SplitDragPolicyTest.java
@@ -24,6 +24,7 @@
import static android.content.ClipDescription.MIMETYPE_APPLICATION_TASK;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_INDEX_UNDEFINED;
import static com.android.wm.shell.draganddrop.DragTestUtils.createAppClipData;
import static com.android.wm.shell.draganddrop.DragTestUtils.createIntentClipData;
import static com.android.wm.shell.draganddrop.DragTestUtils.createTaskInfo;
@@ -226,7 +227,7 @@
mPolicy.onDropped(filterTargetByType(targets, TYPE_FULLSCREEN), null /* hideTaskToken */);
verify(mFullscreenStarter).startIntent(any(), anyInt(), any(),
- eq(SPLIT_POSITION_UNDEFINED), any(), any());
+ eq(SPLIT_POSITION_UNDEFINED), any(), any(), eq(SPLIT_INDEX_UNDEFINED));
}
private void dragOverFullscreenApp_expectSplitScreenTargets(ClipData data) {
@@ -241,12 +242,12 @@
mPolicy.onDropped(filterTargetByType(targets, TYPE_SPLIT_LEFT), null /* hideTaskToken */);
verify(mSplitScreenStarter).startIntent(any(), anyInt(), any(),
- eq(SPLIT_POSITION_TOP_OR_LEFT), any(), any());
+ eq(SPLIT_POSITION_TOP_OR_LEFT), any(), any(), eq(SPLIT_INDEX_UNDEFINED));
reset(mSplitScreenStarter);
mPolicy.onDropped(filterTargetByType(targets, TYPE_SPLIT_RIGHT), null /* hideTaskToken */);
verify(mSplitScreenStarter).startIntent(any(), anyInt(), any(),
- eq(SPLIT_POSITION_BOTTOM_OR_RIGHT), any(), any());
+ eq(SPLIT_POSITION_BOTTOM_OR_RIGHT), any(), any(), eq(SPLIT_INDEX_UNDEFINED));
}
private void dragOverFullscreenAppPhone_expectVerticalSplitScreenTargets(ClipData data) {
@@ -261,13 +262,13 @@
mPolicy.onDropped(filterTargetByType(targets, TYPE_SPLIT_TOP), null /* hideTaskToken */);
verify(mSplitScreenStarter).startIntent(any(), anyInt(), any(),
- eq(SPLIT_POSITION_TOP_OR_LEFT), any(), any());
+ eq(SPLIT_POSITION_TOP_OR_LEFT), any(), any(), eq(SPLIT_INDEX_UNDEFINED));
reset(mSplitScreenStarter);
mPolicy.onDropped(filterTargetByType(targets, TYPE_SPLIT_BOTTOM),
null /* hideTaskToken */);
verify(mSplitScreenStarter).startIntent(any(), anyInt(), any(),
- eq(SPLIT_POSITION_BOTTOM_OR_RIGHT), any(), any());
+ eq(SPLIT_POSITION_BOTTOM_OR_RIGHT), any(), any(), eq(SPLIT_INDEX_UNDEFINED));
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
index 5f58265..289fd2d 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
@@ -36,6 +36,8 @@
import android.content.pm.ActivityInfo;
import android.graphics.Rect;
import android.os.RemoteException;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.util.Rational;
@@ -46,6 +48,7 @@
import androidx.test.filters.SmallTest;
+import com.android.wm.shell.Flags;
import com.android.wm.shell.MockSurfaceControlHelper;
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.ShellTaskOrganizer;
@@ -67,6 +70,8 @@
import com.android.wm.shell.splitscreen.SplitScreenController;
import org.junit.Before;
+import org.junit.ClassRule;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentMatchers;
@@ -81,7 +86,13 @@
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
+@DisableFlags(Flags.FLAG_ENABLE_PIP2)
public class PipTaskOrganizerTest extends ShellTestCase {
+ @ClassRule
+ public static final SetFlagsRule.ClassRule mClassRule = new SetFlagsRule.ClassRule();
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = mClassRule.createSetFlagsRule();
+
private PipTaskOrganizer mPipTaskOrganizer;
@Mock private DisplayController mMockDisplayController;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
index 9600351..b123f4d 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
@@ -42,11 +42,14 @@
import android.os.Bundle;
import android.os.Handler;
import android.os.RemoteException;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import androidx.test.filters.SmallTest;
+import com.android.wm.shell.Flags;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.WindowManagerShellWrapper;
import com.android.wm.shell.common.DisplayController;
@@ -74,6 +77,8 @@
import com.android.wm.shell.sysui.ShellInit;
import org.junit.Before;
+import org.junit.ClassRule;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -88,7 +93,11 @@
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
+@DisableFlags(Flags.FLAG_ENABLE_PIP2)
public class PipControllerTest extends ShellTestCase {
+ @ClassRule public static final SetFlagsRule.ClassRule mClassRule = new SetFlagsRule.ClassRule();
+ @Rule public final SetFlagsRule mSetFlagsRule = mClassRule.createSetFlagsRule();
+
private PipController mPipController;
private ShellInit mShellInit;
private ShellController mShellController;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipEnterAnimatorTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipEnterAnimatorTest.java
index a4008c1..72c4666 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipEnterAnimatorTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipEnterAnimatorTest.java
@@ -22,6 +22,7 @@
import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
@@ -39,6 +40,7 @@
import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
+import com.android.wm.shell.R;
import com.android.wm.shell.pip2.PipSurfaceTransactionHelper;
import com.android.wm.shell.pip2.phone.PipAppIconOverlay;
@@ -49,33 +51,25 @@
import org.mockito.MockitoAnnotations;
/**
- * Unit test again {@link PipEnterAnimator}.
+ * Unit test against {@link PipEnterAnimator}.
*/
@SmallTest
@TestableLooper.RunWithLooper
@RunWith(AndroidTestingRunner.class)
public class PipEnterAnimatorTest {
+ private static final float TEST_CORNER_RADIUS = 1f;
+ private static final float TEST_SHADOW_RADIUS = 2f;
@Mock private Context mMockContext;
-
@Mock private Resources mMockResources;
-
@Mock private PipSurfaceTransactionHelper.SurfaceControlTransactionFactory mMockFactory;
-
@Mock private SurfaceControl.Transaction mMockAnimateTransaction;
-
@Mock private SurfaceControl.Transaction mMockStartTransaction;
-
@Mock private SurfaceControl.Transaction mMockFinishTransaction;
-
@Mock private Runnable mMockStartCallback;
-
@Mock private Runnable mMockEndCallback;
-
@Mock private PipAppIconOverlay mMockPipAppIconOverlay;
-
@Mock private SurfaceControl mMockAppIconOverlayLeash;
-
@Mock private ActivityInfo mMockActivityInfo;
@Surface.Rotation private int mRotation;
@@ -89,13 +83,15 @@
when(mMockContext.getResources()).thenReturn(mMockResources);
when(mMockResources.getInteger(anyInt())).thenReturn(0);
when(mMockFactory.getTransaction()).thenReturn(mMockAnimateTransaction);
- when(mMockAnimateTransaction.setMatrix(any(SurfaceControl.class), any(Matrix.class), any()))
- .thenReturn(mMockAnimateTransaction);
- when(mMockStartTransaction.setMatrix(any(SurfaceControl.class), any(Matrix.class), any()))
- .thenReturn(mMockStartTransaction);
- when(mMockFinishTransaction.setMatrix(any(SurfaceControl.class), any(Matrix.class), any()))
- .thenReturn(mMockFinishTransaction);
when(mMockPipAppIconOverlay.getLeash()).thenReturn(mMockAppIconOverlayLeash);
+ when(mMockResources.getDimensionPixelSize(R.dimen.pip_corner_radius))
+ .thenReturn((int) TEST_CORNER_RADIUS);
+ when(mMockResources.getDimensionPixelSize(R.dimen.pip_shadow_radius))
+ .thenReturn((int) TEST_SHADOW_RADIUS);
+
+ prepareTransaction(mMockAnimateTransaction);
+ prepareTransaction(mMockStartTransaction);
+ prepareTransaction(mMockFinishTransaction);
mTestLeash = new SurfaceControl.Builder()
.setContainerLayer()
@@ -122,6 +118,12 @@
verify(mMockStartCallback).run();
verifyZeroInteractions(mMockEndCallback);
+
+ // Check corner and shadow radii were set
+ verify(mMockAnimateTransaction, atLeastOnce())
+ .setCornerRadius(eq(mTestLeash), eq(TEST_CORNER_RADIUS));
+ verify(mMockAnimateTransaction, atLeastOnce())
+ .setShadowRadius(eq(mTestLeash), eq(TEST_SHADOW_RADIUS));
}
@Test
@@ -142,6 +144,12 @@
verify(mMockStartCallback).run();
verify(mMockEndCallback).run();
+
+ // Check corner and shadow radii were set
+ verify(mMockAnimateTransaction, atLeastOnce())
+ .setCornerRadius(eq(mTestLeash), eq(TEST_CORNER_RADIUS));
+ verify(mMockAnimateTransaction, atLeastOnce())
+ .setShadowRadius(eq(mTestLeash), eq(TEST_SHADOW_RADIUS));
}
@Test
@@ -197,5 +205,21 @@
verify(mMockPipAppIconOverlay).onAnimationUpdate(
eq(mMockAnimateTransaction), anyFloat(), eq(fraction), eq(mEndBounds));
+
+ // Check corner and shadow radii were set
+ verify(mMockAnimateTransaction, atLeastOnce())
+ .setCornerRadius(eq(mTestLeash), eq(TEST_CORNER_RADIUS));
+ verify(mMockAnimateTransaction, atLeastOnce())
+ .setShadowRadius(eq(mTestLeash), eq(TEST_SHADOW_RADIUS));
+ }
+
+ // set up transaction chaining
+ private void prepareTransaction(SurfaceControl.Transaction tx) {
+ when(tx.setMatrix(any(SurfaceControl.class), any(Matrix.class), any()))
+ .thenReturn(tx);
+ when(tx.setCornerRadius(any(SurfaceControl.class), anyFloat()))
+ .thenReturn(tx);
+ when(tx.setShadowRadius(any(SurfaceControl.class), anyFloat()))
+ .thenReturn(tx);
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipResizeAnimatorTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipResizeAnimatorTest.java
index 0adb50b..23fbad0 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipResizeAnimatorTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipResizeAnimatorTest.java
@@ -16,15 +16,18 @@
package com.android.wm.shell.pip2.animation;
+import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyFloat;
+import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import static org.mockito.kotlin.MatchersKt.eq;
-import static org.junit.Assert.assertEquals;
import android.content.Context;
+import android.content.res.Resources;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.testing.AndroidTestingRunner;
@@ -34,12 +37,14 @@
import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
+import com.android.wm.shell.R;
import com.android.wm.shell.pip2.PipSurfaceTransactionHelper;
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;
@@ -52,40 +57,40 @@
public class PipResizeAnimatorTest {
private static final float FLOAT_COMPARISON_DELTA = 0.001f;
+ private static final float TEST_CORNER_RADIUS = 1f;
+ private static final float TEST_SHADOW_RADIUS = 2f;
@Mock private Context mMockContext;
-
+ @Mock private Resources mMockResources;
@Mock private PipSurfaceTransactionHelper.SurfaceControlTransactionFactory mMockFactory;
-
@Mock private SurfaceControl.Transaction mMockTransaction;
-
@Mock private SurfaceControl.Transaction mMockStartTransaction;
-
@Mock private SurfaceControl.Transaction mMockFinishTransaction;
-
@Mock private Runnable mMockStartCallback;
-
@Mock private Runnable mMockEndCallback;
+ @Captor private ArgumentCaptor<Matrix> mArgumentCaptor;
+
private PipResizeAnimator mPipResizeAnimator;
private Rect mBaseBounds;
private Rect mStartBounds;
private Rect mEndBounds;
private SurfaceControl mTestLeash;
- private ArgumentCaptor<Matrix> mArgumentCaptor;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
when(mMockFactory.getTransaction()).thenReturn(mMockTransaction);
- when(mMockTransaction.setMatrix(any(SurfaceControl.class), any(Matrix.class), any()))
- .thenReturn(mMockTransaction);
- when(mMockStartTransaction.setMatrix(any(SurfaceControl.class), any(Matrix.class), any()))
- .thenReturn(mMockStartTransaction);
- when(mMockFinishTransaction.setMatrix(any(SurfaceControl.class), any(Matrix.class), any()))
- .thenReturn(mMockFinishTransaction);
+ when(mMockContext.getResources()).thenReturn(mMockResources);
+ when(mMockResources.getDimensionPixelSize(R.dimen.pip_corner_radius))
+ .thenReturn((int) TEST_CORNER_RADIUS);
+ when(mMockResources.getDimensionPixelSize(R.dimen.pip_shadow_radius))
+ .thenReturn((int) TEST_SHADOW_RADIUS);
- mArgumentCaptor = ArgumentCaptor.forClass(Matrix.class);
+ prepareTransaction(mMockTransaction);
+ prepareTransaction(mMockStartTransaction);
+ prepareTransaction(mMockFinishTransaction);
+
mTestLeash = new SurfaceControl.Builder()
.setContainerLayer()
.setName("PipResizeAnimatorTest")
@@ -187,6 +192,12 @@
assertEquals(matrix[Matrix.MSCALE_Y], 1f, FLOAT_COMPARISON_DELTA);
assertEquals(matrix[Matrix.MTRANS_X], mEndBounds.left, FLOAT_COMPARISON_DELTA);
assertEquals(matrix[Matrix.MTRANS_Y], mEndBounds.top, FLOAT_COMPARISON_DELTA);
+
+ // Check corner and shadow radii were set
+ verify(mMockTransaction, atLeastOnce())
+ .setCornerRadius(eq(mTestLeash), eq(TEST_CORNER_RADIUS));
+ verify(mMockTransaction, atLeastOnce())
+ .setShadowRadius(eq(mTestLeash), eq(TEST_SHADOW_RADIUS));
}
@Test
@@ -237,6 +248,12 @@
assertEquals(matrix[Matrix.MSCALE_Y], 1f, FLOAT_COMPARISON_DELTA);
assertEquals(matrix[Matrix.MTRANS_X], mEndBounds.left, FLOAT_COMPARISON_DELTA);
assertEquals(matrix[Matrix.MTRANS_Y], mEndBounds.top, FLOAT_COMPARISON_DELTA);
+
+ // Check corner and shadow radii were set
+ verify(mMockTransaction, atLeastOnce())
+ .setCornerRadius(eq(mTestLeash), eq(TEST_CORNER_RADIUS));
+ verify(mMockTransaction, atLeastOnce())
+ .setShadowRadius(eq(mTestLeash), eq(TEST_SHADOW_RADIUS));
}
@Test
@@ -272,5 +289,21 @@
mArgumentCaptor.getValue().getValues(matrix);
assertEquals(matrix[Matrix.MSKEW_X], 0f, FLOAT_COMPARISON_DELTA);
assertEquals(matrix[Matrix.MSKEW_Y], 0f, FLOAT_COMPARISON_DELTA);
+
+ // Check corner and shadow radii were set
+ verify(mMockTransaction, atLeastOnce())
+ .setCornerRadius(eq(mTestLeash), eq(TEST_CORNER_RADIUS));
+ verify(mMockTransaction, atLeastOnce())
+ .setShadowRadius(eq(mTestLeash), eq(TEST_SHADOW_RADIUS));
+ }
+
+ // set up transaction chaining
+ private void prepareTransaction(SurfaceControl.Transaction tx) {
+ when(tx.setMatrix(any(SurfaceControl.class), any(Matrix.class), any()))
+ .thenReturn(tx);
+ when(tx.setCornerRadius(any(SurfaceControl.class), anyFloat()))
+ .thenReturn(tx);
+ when(tx.setShadowRadius(any(SurfaceControl.class), anyFloat()))
+ .thenReturn(tx);
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/GroupedTaskInfoTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/GroupedTaskInfoTest.kt
index 2b30bc3..fd3adab 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/GroupedTaskInfoTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/GroupedTaskInfoTest.kt
@@ -176,6 +176,30 @@
assertThat(recentTaskInfoParcel.minimizedTaskIds).isEqualTo(arrayOf(2).toIntArray())
}
+ @Test
+ fun testGetTaskById_singleTasks() {
+ val task1 = createTaskInfo(id = 1234)
+
+ val taskInfo = GroupedTaskInfo.forFullscreenTasks(task1)
+
+ assertThat(taskInfo.getTaskById(1234)).isEqualTo(task1)
+ assertThat(taskInfo.containsTask(1234)).isTrue()
+ }
+
+ @Test
+ fun testGetTaskById_multipleTasks() {
+ val task1 = createTaskInfo(id = 1)
+ val task2 = createTaskInfo(id = 2)
+ val splitBounds = SplitBounds(Rect(), Rect(), 1, 2, SNAP_TO_2_50_50)
+
+ val taskInfo = GroupedTaskInfo.forSplitTasks(task1, task2, splitBounds)
+
+ assertThat(taskInfo.getTaskById(1)).isEqualTo(task1)
+ assertThat(taskInfo.getTaskById(2)).isEqualTo(task2)
+ assertThat(taskInfo.containsTask(1)).isTrue()
+ assertThat(taskInfo.containsTask(2)).isTrue()
+ }
+
private fun createTaskInfo(id: Int) = ActivityManager.RecentTaskInfo().apply {
taskId = id
token = WindowContainerToken(mock(IWindowContainerToken::class.java))
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
index dede583..12c3978 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
@@ -610,7 +610,7 @@
mRecentTasksControllerReal.onTaskMovedToFrontThroughTransition(taskInfo);
GroupedTaskInfo runningTask = GroupedTaskInfo.forFullscreenTasks(taskInfo);
- verify(mRecentTasksListener).onTaskMovedToFront(eq(new GroupedTaskInfo[] { runningTask }));
+ verify(mRecentTasksListener).onTaskMovedToFront(eq(runningTask));
}
@Test
@@ -656,6 +656,35 @@
assertEquals(splitBounds4, pair2Bounds);
}
+ @Test
+ @DisableFlags(Flags.FLAG_ENABLE_TASK_STACK_OBSERVER_IN_SHELL)
+ @EnableFlags(com.android.wm.shell.Flags.FLAG_ENABLE_SHELL_TOP_TASK_TRACKING)
+ public void shellTopTaskTracker_onTaskStackChanged_expectNoRecentsChanged() throws Exception {
+ mRecentTasksControllerReal.registerRecentTasksListener(mRecentTasksListener);
+ mRecentTasksControllerReal.onTaskStackChanged();
+ verify(mRecentTasksListener, never()).onRecentTasksChanged();
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_ENABLE_TASK_STACK_OBSERVER_IN_SHELL)
+ @EnableFlags(com.android.wm.shell.Flags.FLAG_ENABLE_SHELL_TOP_TASK_TRACKING)
+ public void shellTopTaskTracker_onTaskRemoved_expectNoRecentsChanged() throws Exception {
+ mRecentTasksControllerReal.registerRecentTasksListener(mRecentTasksListener);
+ ActivityManager.RunningTaskInfo taskInfo = makeRunningTaskInfo(/* taskId= */10);
+ mRecentTasksControllerReal.onTaskRemoved(taskInfo);
+ verify(mRecentTasksListener, never()).onRecentTasksChanged();
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_ENABLE_TASK_STACK_OBSERVER_IN_SHELL)
+ @EnableFlags(com.android.wm.shell.Flags.FLAG_ENABLE_SHELL_TOP_TASK_TRACKING)
+ public void shellTopTaskTracker_onVisibleTasksChanged() throws Exception {
+ mRecentTasksControllerReal.registerRecentTasksListener(mRecentTasksListener);
+ ActivityManager.RunningTaskInfo taskInfo = makeRunningTaskInfo(/* taskId= */10);
+ mRecentTasksControllerReal.onVisibleTasksChanged(List.of(taskInfo));
+ verify(mRecentTasksListener, never()).onVisibleTasksChanged(any());
+ }
+
/**
* Helper to create a task with a given task id.
*/
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/TaskStackTransitionObserverTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/TaskStackTransitionObserverTest.kt
index efe4fb1..9919462 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/TaskStackTransitionObserverTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/TaskStackTransitionObserverTest.kt
@@ -16,21 +16,36 @@
package com.android.wm.shell.recents
-import android.app.ActivityManager
+import android.app.ActivityManager.RunningTaskInfo
+import android.app.TaskInfo
import android.app.WindowConfiguration
+import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN
+import android.app.WindowConfiguration.WINDOWING_MODE_PINNED
+import android.content.ComponentName
+import android.content.Intent
import android.os.IBinder
+import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.platform.test.flag.junit.SetFlagsRule
import android.testing.AndroidTestingRunner
import android.view.SurfaceControl
import android.view.WindowManager
+import android.view.WindowManager.TRANSIT_CHANGE
+import android.view.WindowManager.TRANSIT_CLOSE
+import android.view.WindowManager.TRANSIT_FIRST_CUSTOM
+import android.view.WindowManager.TRANSIT_OPEN
+import android.view.WindowManager.TRANSIT_TO_FRONT
import android.window.IWindowContainerToken
import android.window.TransitionInfo
+import android.window.TransitionInfo.FLAG_MOVED_TO_TOP
import android.window.WindowContainerToken
import androidx.test.filters.SmallTest
import com.android.window.flags.Flags
+import com.android.wm.shell.ShellTaskOrganizer
import com.android.wm.shell.TestShellExecutor
+import com.android.wm.shell.TestSyncExecutor
import com.android.wm.shell.common.ShellExecutor
+import com.android.wm.shell.sysui.ShellCommandHandler
import com.android.wm.shell.sysui.ShellInit
import com.android.wm.shell.transition.TransitionInfoBuilder
import com.android.wm.shell.transition.Transitions
@@ -61,7 +76,10 @@
@JvmField @Rule val setFlagsRule = SetFlagsRule()
@Mock private lateinit var shellInit: ShellInit
- @Mock lateinit var testExecutor: ShellExecutor
+ @Mock private lateinit var shellTaskOrganizerLazy: Lazy<ShellTaskOrganizer>
+ @Mock private lateinit var shellTaskOrganizer: ShellTaskOrganizer
+ @Mock private lateinit var shellCommandHandler: ShellCommandHandler
+ @Mock private lateinit var testExecutor: ShellExecutor
@Mock private lateinit var transitionsLazy: Lazy<Transitions>
@Mock private lateinit var transitions: Transitions
@Mock private lateinit var mockTransitionBinder: IBinder
@@ -73,24 +91,23 @@
MockitoAnnotations.initMocks(this)
shellInit = Mockito.spy(ShellInit(testExecutor))
whenever(transitionsLazy.get()).thenReturn(transitions)
- transitionObserver = TaskStackTransitionObserver(transitionsLazy, shellInit)
- if (Transitions.ENABLE_SHELL_TRANSITIONS) {
- val initRunnableCaptor = ArgumentCaptor.forClass(Runnable::class.java)
- verify(shellInit)
- .addInitCallback(initRunnableCaptor.capture(), same(transitionObserver))
- initRunnableCaptor.value.run()
- } else {
- transitionObserver.onInit()
- }
+ whenever(shellTaskOrganizerLazy.get()).thenReturn(shellTaskOrganizer)
+ transitionObserver = TaskStackTransitionObserver(shellInit, shellTaskOrganizerLazy,
+ shellCommandHandler, transitionsLazy)
+
+ val initRunnableCaptor = ArgumentCaptor.forClass(Runnable::class.java)
+ verify(shellInit)
+ .addInitCallback(initRunnableCaptor.capture(), same(transitionObserver))
+ initRunnableCaptor.value.run()
}
@Test
- @EnableFlags(Flags.FLAG_ENABLE_TASK_STACK_OBSERVER_IN_SHELL)
fun testRegistersObserverAtInit() {
verify(transitions).registerObserver(same(transitionObserver))
}
@Test
+ @DisableFlags(com.android.wm.shell.Flags.FLAG_ENABLE_SHELL_TOP_TASK_TRACKING)
@EnableFlags(Flags.FLAG_ENABLE_TASK_STACK_OBSERVER_IN_SHELL)
fun taskCreated_freeformWindow_listenerNotified() {
val listener = TestListener()
@@ -98,11 +115,11 @@
transitionObserver.addTaskStackTransitionObserverListener(listener, executor)
val change =
createChange(
- WindowManager.TRANSIT_OPEN,
+ TRANSIT_OPEN,
createTaskInfo(1, WindowConfiguration.WINDOWING_MODE_FREEFORM)
)
val transitionInfo =
- TransitionInfoBuilder(WindowManager.TRANSIT_OPEN, 0).addChange(change).build()
+ TransitionInfoBuilder(TRANSIT_OPEN, 0).addChange(change).build()
callOnTransitionReady(transitionInfo)
callOnTransitionFinished()
@@ -114,6 +131,7 @@
}
@Test
+ @DisableFlags(com.android.wm.shell.Flags.FLAG_ENABLE_SHELL_TOP_TASK_TRACKING)
@EnableFlags(Flags.FLAG_ENABLE_TASK_STACK_OBSERVER_IN_SHELL)
fun taskCreated_fullscreenWindow_listenerNotified() {
val listener = TestListener()
@@ -121,11 +139,11 @@
transitionObserver.addTaskStackTransitionObserverListener(listener, executor)
val change =
createChange(
- WindowManager.TRANSIT_OPEN,
- createTaskInfo(1, WindowConfiguration.WINDOWING_MODE_FULLSCREEN)
+ TRANSIT_OPEN,
+ createTaskInfo(1, WINDOWING_MODE_FULLSCREEN)
)
val transitionInfo =
- TransitionInfoBuilder(WindowManager.TRANSIT_OPEN, 0).addChange(change).build()
+ TransitionInfoBuilder(TRANSIT_OPEN, 0).addChange(change).build()
callOnTransitionReady(transitionInfo)
callOnTransitionFinished()
@@ -133,10 +151,11 @@
assertThat(listener.taskInfoOnTaskMovedToFront.taskId).isEqualTo(1)
assertThat(listener.taskInfoOnTaskMovedToFront.windowingMode)
- .isEqualTo(WindowConfiguration.WINDOWING_MODE_FULLSCREEN)
+ .isEqualTo(WINDOWING_MODE_FULLSCREEN)
}
@Test
+ @DisableFlags(com.android.wm.shell.Flags.FLAG_ENABLE_SHELL_TOP_TASK_TRACKING)
@EnableFlags(Flags.FLAG_ENABLE_TASK_STACK_OBSERVER_IN_SHELL)
fun taskCreated_freeformWindowOnTopOfFreeform_listenerNotified() {
val listener = TestListener()
@@ -144,7 +163,7 @@
transitionObserver.addTaskStackTransitionObserverListener(listener, executor)
val freeformOpenChange =
createChange(
- WindowManager.TRANSIT_OPEN,
+ TRANSIT_OPEN,
createTaskInfo(1, WindowConfiguration.WINDOWING_MODE_FREEFORM)
)
val freeformReorderChange =
@@ -153,7 +172,7 @@
createTaskInfo(2, WindowConfiguration.WINDOWING_MODE_FREEFORM)
)
val transitionInfo =
- TransitionInfoBuilder(WindowManager.TRANSIT_OPEN, 0)
+ TransitionInfoBuilder(TRANSIT_OPEN, 0)
.addChange(freeformOpenChange)
.addChange(freeformReorderChange)
.build()
@@ -169,6 +188,7 @@
}
@Test
+ @DisableFlags(com.android.wm.shell.Flags.FLAG_ENABLE_SHELL_TOP_TASK_TRACKING)
@EnableFlags(Flags.FLAG_ENABLE_TASK_STACK_OBSERVER_IN_SHELL)
fun transitionMerged_withChange_onlyOpenChangeIsNotified() {
val listener = TestListener()
@@ -178,11 +198,11 @@
// Create open transition
val change =
createChange(
- WindowManager.TRANSIT_OPEN,
+ TRANSIT_OPEN,
createTaskInfo(1, WindowConfiguration.WINDOWING_MODE_FREEFORM)
)
val transitionInfo =
- TransitionInfoBuilder(WindowManager.TRANSIT_OPEN, 0).addChange(change).build()
+ TransitionInfoBuilder(TRANSIT_OPEN, 0).addChange(change).build()
// create change transition to be merged to above transition
val mergedChange =
@@ -212,6 +232,7 @@
}
@Test
+ @DisableFlags(com.android.wm.shell.Flags.FLAG_ENABLE_SHELL_TOP_TASK_TRACKING)
@EnableFlags(Flags.FLAG_ENABLE_TASK_STACK_OBSERVER_IN_SHELL)
fun transitionMerged_withOpen_lastOpenChangeIsNotified() {
val listener = TestListener()
@@ -221,20 +242,20 @@
// Create open transition
val change =
createChange(
- WindowManager.TRANSIT_OPEN,
+ TRANSIT_OPEN,
createTaskInfo(1, WindowConfiguration.WINDOWING_MODE_FREEFORM)
)
val transitionInfo =
- TransitionInfoBuilder(WindowManager.TRANSIT_OPEN, 0).addChange(change).build()
+ TransitionInfoBuilder(TRANSIT_OPEN, 0).addChange(change).build()
// create change transition to be merged to above transition
val mergedChange =
createChange(
- WindowManager.TRANSIT_OPEN,
+ TRANSIT_OPEN,
createTaskInfo(2, WindowConfiguration.WINDOWING_MODE_FREEFORM)
)
val mergedTransitionInfo =
- TransitionInfoBuilder(WindowManager.TRANSIT_OPEN, 0).addChange(mergedChange).build()
+ TransitionInfoBuilder(TRANSIT_OPEN, 0).addChange(mergedChange).build()
val mergedTransition = Mockito.mock(IBinder::class.java)
callOnTransitionReady(transitionInfo)
@@ -250,6 +271,7 @@
}
@Test
+ @DisableFlags(com.android.wm.shell.Flags.FLAG_ENABLE_SHELL_TOP_TASK_TRACKING)
@EnableFlags(Flags.FLAG_ENABLE_TASK_STACK_OBSERVER_IN_SHELL)
fun taskChange_freeformWindowToFullscreenWindow_listenerNotified() {
val listener = TestListener()
@@ -257,11 +279,11 @@
transitionObserver.addTaskStackTransitionObserverListener(listener, executor)
val freeformState =
createChange(
- WindowManager.TRANSIT_OPEN,
+ TRANSIT_OPEN,
createTaskInfo(1, WindowConfiguration.WINDOWING_MODE_FREEFORM)
)
val transitionInfoOpen =
- TransitionInfoBuilder(WindowManager.TRANSIT_OPEN, 0).addChange(freeformState).build()
+ TransitionInfoBuilder(TRANSIT_OPEN, 0).addChange(freeformState).build()
callOnTransitionReady(transitionInfoOpen)
callOnTransitionFinished()
executor.flushAll()
@@ -276,7 +298,7 @@
val fullscreenState =
createChange(
WindowManager.TRANSIT_CHANGE,
- createTaskInfo(1, WindowConfiguration.WINDOWING_MODE_FULLSCREEN)
+ createTaskInfo(1, WINDOWING_MODE_FULLSCREEN)
)
val transitionInfoChange =
TransitionInfoBuilder(WindowManager.TRANSIT_CHANGE, 0)
@@ -301,6 +323,7 @@
}
@Test
+ @DisableFlags(com.android.wm.shell.Flags.FLAG_ENABLE_SHELL_TOP_TASK_TRACKING)
@EnableFlags(Flags.FLAG_ENABLE_TASK_STACK_OBSERVER_IN_SHELL)
fun singleTransition_withOpenAndChange_onlyOpenIsNotified() {
val listener = TestListener()
@@ -310,13 +333,13 @@
// Creating multiple changes to be fired in a single transition
val freeformState =
createChange(
- mode = WindowManager.TRANSIT_OPEN,
+ mode = TRANSIT_OPEN,
taskInfo = createTaskInfo(1, WindowConfiguration.WINDOWING_MODE_FREEFORM)
)
val fullscreenState =
createChange(
mode = WindowManager.TRANSIT_CHANGE,
- taskInfo = createTaskInfo(1, WindowConfiguration.WINDOWING_MODE_FULLSCREEN)
+ taskInfo = createTaskInfo(1, WINDOWING_MODE_FULLSCREEN)
)
val transitionInfoWithChanges =
@@ -336,6 +359,7 @@
}
@Test
+ @DisableFlags(com.android.wm.shell.Flags.FLAG_ENABLE_SHELL_TOP_TASK_TRACKING)
@EnableFlags(Flags.FLAG_ENABLE_TASK_STACK_OBSERVER_IN_SHELL)
fun singleTransition_withMultipleChanges_listenerNotified_forEachChange() {
val listener = TestListener()
@@ -349,7 +373,7 @@
listOf(
WindowConfiguration.WINDOWING_MODE_FREEFORM,
WindowConfiguration.WINDOW_CONFIG_DISPLAY_ROTATION,
- WindowConfiguration.WINDOWING_MODE_FULLSCREEN
+ WINDOWING_MODE_FULLSCREEN
)
.map { change ->
createChange(
@@ -376,19 +400,259 @@
}
}
- class TestListener : TaskStackTransitionObserver.TaskStackTransitionObserverListener {
- var taskInfoOnTaskMovedToFront = ActivityManager.RunningTaskInfo()
- var taskInfoOnTaskChanged = mutableListOf<ActivityManager.RunningTaskInfo>()
+ @Test
+ @DisableFlags(Flags.FLAG_ENABLE_TASK_STACK_OBSERVER_IN_SHELL)
+ @EnableFlags(com.android.wm.shell.Flags.FLAG_ENABLE_SHELL_TOP_TASK_TRACKING)
+ fun openTransition_visibleTasksChanged() {
+ val listener = TestListener()
+ val executor = TestSyncExecutor()
+ transitionObserver.addTaskStackTransitionObserverListener(listener, executor)
- override fun onTaskMovedToFrontThroughTransition(
- taskInfo: ActivityManager.RunningTaskInfo
- ) {
+ // Model an opening task
+ val firstOpeningTransition =
+ createTransitionInfo(TRANSIT_OPEN,
+ listOf(
+ createChange(TRANSIT_OPEN, 1, WINDOWING_MODE_FULLSCREEN),
+ )
+ )
+
+ callOnTransitionReady(firstOpeningTransition)
+ callOnTransitionFinished()
+ // Assert that the task is reported visible
+ assertThat(listener.visibleTasksUpdatedCount).isEqualTo(1)
+ assertVisibleTasks(listener, listOf(1))
+
+ // Model opening another task
+ val nextOpeningTransition =
+ createTransitionInfo(TRANSIT_OPEN,
+ listOf(
+ createChange(TRANSIT_OPEN, 2, WINDOWING_MODE_FULLSCREEN),
+ createChange(TRANSIT_CLOSE, 1, WINDOWING_MODE_FULLSCREEN),
+ )
+ )
+
+ callOnTransitionReady(nextOpeningTransition)
+ // Assert that the visible list from top to bottom is valid (opening, closing)
+ assertThat(listener.visibleTasksUpdatedCount).isEqualTo(2)
+ assertVisibleTasks(listener, listOf(2, 1))
+
+ callOnTransitionFinished()
+ // Assert that after the transition finishes, there is only the opening task remaining
+ assertThat(listener.visibleTasksUpdatedCount).isEqualTo(3)
+ assertVisibleTasks(listener, listOf(2))
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_ENABLE_TASK_STACK_OBSERVER_IN_SHELL)
+ @EnableFlags(com.android.wm.shell.Flags.FLAG_ENABLE_SHELL_TOP_TASK_TRACKING)
+ fun toFrontTransition_visibleTasksChanged() {
+ val listener = TestListener()
+ val executor = TestSyncExecutor()
+ transitionObserver.addTaskStackTransitionObserverListener(listener, executor)
+
+ // Model an opening task
+ val firstOpeningTransition =
+ createTransitionInfo(TRANSIT_OPEN,
+ listOf(
+ createChange(TRANSIT_OPEN, 1, WINDOWING_MODE_FULLSCREEN),
+ )
+ )
+
+ callOnTransitionReady(firstOpeningTransition)
+ callOnTransitionFinished()
+ // Assert that the task is reported visible
+ assertThat(listener.visibleTasksUpdatedCount).isEqualTo(1)
+ assertVisibleTasks(listener, listOf(1))
+
+ // Model opening another task
+ val nextOpeningTransition =
+ createTransitionInfo(TRANSIT_OPEN,
+ listOf(
+ createChange(TRANSIT_OPEN, 2, WINDOWING_MODE_FULLSCREEN),
+ )
+ )
+
+ callOnTransitionReady(nextOpeningTransition)
+ callOnTransitionFinished()
+ // Assert that the visible list from top to bottom is valid
+ assertThat(listener.visibleTasksUpdatedCount).isEqualTo(2)
+ assertVisibleTasks(listener, listOf(2, 1))
+
+ // Model the first task moving to front
+ val toFrontTransition =
+ createTransitionInfo(TRANSIT_TO_FRONT,
+ listOf(
+ createChange(TRANSIT_CHANGE, 1, WINDOWING_MODE_FULLSCREEN,
+ FLAG_MOVED_TO_TOP),
+ )
+ )
+
+ callOnTransitionReady(toFrontTransition)
+ callOnTransitionFinished()
+ // Assert that the visible list from top to bottom is valid
+ assertThat(listener.visibleTasksUpdatedCount).isEqualTo(3)
+ assertVisibleTasks(listener, listOf(1, 2))
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_ENABLE_TASK_STACK_OBSERVER_IN_SHELL)
+ @EnableFlags(com.android.wm.shell.Flags.FLAG_ENABLE_SHELL_TOP_TASK_TRACKING)
+ fun closeTransition_visibleTasksChanged() {
+ val listener = TestListener()
+ val executor = TestSyncExecutor()
+ transitionObserver.addTaskStackTransitionObserverListener(listener, executor)
+
+ // Model an opening task
+ val firstOpeningTransition =
+ createTransitionInfo(TRANSIT_OPEN,
+ listOf(
+ createChange(TRANSIT_OPEN, 1, WINDOWING_MODE_FULLSCREEN),
+ )
+ )
+
+ callOnTransitionReady(firstOpeningTransition)
+ callOnTransitionFinished()
+ assertThat(listener.visibleTasksUpdatedCount).isEqualTo(1)
+
+ // Model a closing task
+ val nextOpeningTransition =
+ createTransitionInfo(TRANSIT_OPEN,
+ listOf(
+ createChange(TRANSIT_CLOSE, 1, WINDOWING_MODE_FULLSCREEN),
+ )
+ )
+
+ callOnTransitionReady(nextOpeningTransition)
+ // Assert that the visible list hasn't changed (the close is pending)
+ assertThat(listener.visibleTasksUpdatedCount).isEqualTo(1)
+
+ callOnTransitionFinished()
+ // Assert that after the transition finishes, there is only the opening task remaining
+ assertThat(listener.visibleTasksUpdatedCount).isEqualTo(2)
+ assertVisibleTasks(listener, listOf())
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_ENABLE_TASK_STACK_OBSERVER_IN_SHELL)
+ @EnableFlags(com.android.wm.shell.Flags.FLAG_ENABLE_SHELL_TOP_TASK_TRACKING)
+ fun changeTransition_visibleTasksUnchanged() {
+ val listener = TestListener()
+ val executor = TestSyncExecutor()
+ transitionObserver.addTaskStackTransitionObserverListener(listener, executor)
+
+ // Model an opening task
+ val firstOpeningTransition =
+ createTransitionInfo(TRANSIT_OPEN,
+ listOf(
+ createChange(TRANSIT_OPEN, 1, WINDOWING_MODE_FULLSCREEN),
+ )
+ )
+
+ callOnTransitionReady(firstOpeningTransition)
+ callOnTransitionFinished()
+ assertThat(listener.visibleTasksUpdatedCount).isEqualTo(1)
+
+ // Model a closing task
+ val nextOpeningTransition =
+ createTransitionInfo(
+ TRANSIT_FIRST_CUSTOM,
+ listOf(
+ createChange(TRANSIT_CHANGE, 1, WINDOWING_MODE_FULLSCREEN),
+ )
+ )
+
+ callOnTransitionReady(nextOpeningTransition)
+ // Assert that the visible list hasn't changed
+ assertThat(listener.visibleTasksUpdatedCount).isEqualTo(1)
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_ENABLE_TASK_STACK_OBSERVER_IN_SHELL)
+ @EnableFlags(com.android.wm.shell.Flags.FLAG_ENABLE_SHELL_TOP_TASK_TRACKING)
+ fun taskVanished_visibleTasksChanged() {
+ val listener = TestListener()
+ val executor = TestSyncExecutor()
+ transitionObserver.addTaskStackTransitionObserverListener(listener, executor)
+
+ // Model an opening task
+ val firstOpeningTransition =
+ createTransitionInfo(TRANSIT_OPEN,
+ listOf(
+ createChange(TRANSIT_OPEN, 1, WINDOWING_MODE_FULLSCREEN),
+ )
+ )
+
+ callOnTransitionReady(firstOpeningTransition)
+ callOnTransitionFinished()
+ // Assert that the task is reported visible
+ assertThat(listener.visibleTasksUpdatedCount).isEqualTo(1)
+ assertVisibleTasks(listener, listOf(1))
+
+ // Trigger task vanished
+ val removedTaskInfo = createTaskInfo(1, WINDOWING_MODE_FULLSCREEN)
+ transitionObserver.onTaskVanished(removedTaskInfo)
+
+ // Assert that the visible list is now empty
+ assertThat(listener.visibleTasksUpdatedCount).isEqualTo(2)
+ assertVisibleTasks(listener, listOf())
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_ENABLE_TASK_STACK_OBSERVER_IN_SHELL)
+ @EnableFlags(com.android.wm.shell.Flags.FLAG_ENABLE_SHELL_TOP_TASK_TRACKING)
+ fun alwaysOnTop_taskIsTopMostVisible() {
+ val listener = TestListener()
+ val executor = TestSyncExecutor()
+ transitionObserver.addTaskStackTransitionObserverListener(listener, executor)
+
+ // Model an opening PIP task
+ val pipOpeningTransition =
+ createTransitionInfo(TRANSIT_OPEN,
+ listOf(
+ createChange(TRANSIT_OPEN, 1, WINDOWING_MODE_PINNED),
+ )
+ )
+
+ callOnTransitionReady(pipOpeningTransition)
+ callOnTransitionFinished()
+ assertThat(listener.visibleTasksUpdatedCount).isEqualTo(1)
+ assertVisibleTasks(listener, listOf(1))
+
+ // Model an opening fullscreen task
+ val firstOpeningTransition =
+ createTransitionInfo(TRANSIT_OPEN,
+ listOf(
+ createChange(TRANSIT_OPEN, 2, WINDOWING_MODE_FULLSCREEN),
+ )
+ )
+
+ callOnTransitionReady(firstOpeningTransition)
+ callOnTransitionFinished()
+ assertThat(listener.visibleTasksUpdatedCount).isEqualTo(2)
+ assertVisibleTasks(listener, listOf(1, 2))
+ }
+
+ class TestListener : TaskStackTransitionObserver.TaskStackTransitionObserverListener {
+ // Only used if FLAG_ENABLE_SHELL_TOP_TASK_TRACKING is disabled
+ var taskInfoOnTaskMovedToFront = RunningTaskInfo()
+ var taskInfoOnTaskChanged = mutableListOf<RunningTaskInfo>()
+ // Only used if FLAG_ENABLE_SHELL_TOP_TASK_TRACKING is enabled
+ var visibleTasks = mutableListOf<TaskInfo>()
+ var visibleTasksUpdatedCount = 0
+
+ override fun onTaskMovedToFrontThroughTransition(taskInfo: RunningTaskInfo) {
taskInfoOnTaskMovedToFront = taskInfo
}
- override fun onTaskChangedThroughTransition(taskInfo: ActivityManager.RunningTaskInfo) {
+ override fun onTaskChangedThroughTransition(taskInfo: RunningTaskInfo) {
taskInfoOnTaskChanged += taskInfo
}
+
+ override fun onVisibleTasksChanged(visibleTasks: List<RunningTaskInfo>) {
+ this.visibleTasks.clear()
+ this.visibleTasks.addAll(visibleTasks)
+ visibleTasksUpdatedCount++
+ }
}
/** Simulate calling the onTransitionReady() method */
@@ -412,27 +676,64 @@
transitionObserver.onTransitionMerged(merged, playing)
}
+ /**
+ * Asserts that the listener has the given expected task ids (in order).
+ */
+ private fun assertVisibleTasks(
+ listener: TestListener,
+ expectedVisibleTaskIds: List<Int>
+ ) {
+ assertThat(listener.visibleTasks.size).isEqualTo(expectedVisibleTaskIds.size)
+ expectedVisibleTaskIds.forEachIndexed { index, taskId ->
+ assertThat(listener.visibleTasks[index].taskId).isEqualTo(taskId)
+ }
+ }
+
companion object {
- fun createTaskInfo(taskId: Int, windowingMode: Int): ActivityManager.RunningTaskInfo {
- val taskInfo = ActivityManager.RunningTaskInfo()
+ fun createTaskInfo(taskId: Int, windowingMode: Int): RunningTaskInfo {
+ val taskInfo = RunningTaskInfo()
+ taskInfo.baseIntent = Intent().setComponent(
+ ComponentName(javaClass.packageName, "Test"))
taskInfo.taskId = taskId
taskInfo.configuration.windowConfiguration.windowingMode = windowingMode
-
+ if (windowingMode == WINDOWING_MODE_PINNED) {
+ taskInfo.configuration.windowConfiguration.isAlwaysOnTop = true
+ }
return taskInfo
}
fun createChange(
mode: Int,
- taskInfo: ActivityManager.RunningTaskInfo
+ taskInfo: RunningTaskInfo,
+ flags: Int = 0,
): TransitionInfo.Change {
val change =
TransitionInfo.Change(
WindowContainerToken(Mockito.mock(IWindowContainerToken::class.java)),
Mockito.mock(SurfaceControl::class.java)
)
+ change.flags = flags
change.mode = mode
change.taskInfo = taskInfo
return change
}
+
+ fun createChange(
+ mode: Int,
+ taskId: Int,
+ windowingMode: Int,
+ flags: Int = 0,
+ ): TransitionInfo.Change {
+ return createChange(mode, createTaskInfo(taskId, windowingMode), flags)
+ }
+
+ fun createTransitionInfo(
+ transitionType: Int,
+ changes: List<TransitionInfo.Change>
+ ): TransitionInfo {
+ return TransitionInfoBuilder(transitionType, 0)
+ .apply { changes.forEach { c -> this@apply.addChange(c) } }
+ .build()
+ }
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java
index 966651f..72a7a3f 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java
@@ -23,6 +23,7 @@
import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
import static android.content.Intent.FLAG_ACTIVITY_NO_USER_ACTION;
+import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_INDEX_0;
import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
@@ -200,10 +201,11 @@
PendingIntent.getActivity(mContext, 0, startIntent, FLAG_IMMUTABLE);
mSplitScreenController.startIntent(pendingIntent, mContext.getUserId(), null,
- SPLIT_POSITION_TOP_OR_LEFT, null /* options */, null /* hideTaskToken */);
+ SPLIT_POSITION_TOP_OR_LEFT, null /* options */, null /* hideTaskToken */,
+ SPLIT_INDEX_0);
verify(mStageCoordinator).startIntent(eq(pendingIntent), mIntentCaptor.capture(),
- eq(SPLIT_POSITION_TOP_OR_LEFT), isNull(), isNull());
+ eq(SPLIT_POSITION_TOP_OR_LEFT), isNull(), isNull(), eq(SPLIT_INDEX_0));
assertEquals(FLAG_ACTIVITY_NO_USER_ACTION,
mIntentCaptor.getValue().getFlags() & FLAG_ACTIVITY_NO_USER_ACTION);
}
@@ -220,10 +222,11 @@
doReturn(topRunningTask).when(mRecentTasks).getTopRunningTask(any());
mSplitScreenController.startIntent(pendingIntent, mContext.getUserId(), null,
- SPLIT_POSITION_TOP_OR_LEFT, null /* options */, null /* hideTaskToken */);
+ SPLIT_POSITION_TOP_OR_LEFT, null /* options */, null /* hideTaskToken */,
+ SPLIT_INDEX_0);
verify(mStageCoordinator).startIntent(eq(pendingIntent), mIntentCaptor.capture(),
- eq(SPLIT_POSITION_TOP_OR_LEFT), isNull(), isNull());
+ eq(SPLIT_POSITION_TOP_OR_LEFT), isNull(), isNull(), eq(SPLIT_INDEX_0));
assertEquals(FLAG_ACTIVITY_MULTIPLE_TASK,
mIntentCaptor.getValue().getFlags() & FLAG_ACTIVITY_MULTIPLE_TASK);
}
@@ -243,10 +246,11 @@
doReturn(sameTaskInfo).when(mRecentTasks).findTaskInBackground(any(), anyInt(), any());
mSplitScreenController.startIntent(pendingIntent, mContext.getUserId(), null,
- SPLIT_POSITION_TOP_OR_LEFT, null /* options */, null /* hideTaskToken */);
+ SPLIT_POSITION_TOP_OR_LEFT, null /* options */, null /* hideTaskToken */,
+ SPLIT_INDEX_0);
verify(mStageCoordinator).startTask(anyInt(), eq(SPLIT_POSITION_TOP_OR_LEFT),
- isNull(), isNull());
+ isNull(), isNull(), eq(SPLIT_INDEX_0));
verify(mMultiInstanceHelper, never()).supportsMultiInstanceSplit(any());
verify(mStageCoordinator, never()).switchSplitPosition(any());
}
@@ -269,10 +273,11 @@
.findTaskInBackground(any(), anyInt(), any());
mSplitScreenController.startIntent(pendingIntent, mContext.getUserId(), null,
- SPLIT_POSITION_TOP_OR_LEFT, null /* options */, null /* hideTaskToken */);
+ SPLIT_POSITION_TOP_OR_LEFT, null /* options */, null /* hideTaskToken */,
+ SPLIT_INDEX_0);
verify(mMultiInstanceHelper, never()).supportsMultiInstanceSplit(any());
verify(mStageCoordinator).startTask(anyInt(), eq(SPLIT_POSITION_TOP_OR_LEFT),
- isNull(), isNull());
+ isNull(), isNull(), eq(SPLIT_INDEX_0));
}
@Test
@@ -289,7 +294,8 @@
SPLIT_POSITION_BOTTOM_OR_RIGHT);
mSplitScreenController.startIntent(pendingIntent, mContext.getUserId(), null,
- SPLIT_POSITION_TOP_OR_LEFT, null /* options */, null /* hideTaskToken */);
+ SPLIT_POSITION_TOP_OR_LEFT, null /* options */, null /* hideTaskToken */,
+ SPLIT_INDEX_0);
verify(mStageCoordinator).switchSplitPosition(anyString());
}
@@ -301,7 +307,7 @@
PendingIntent.getActivity(mContext, 0, startIntent, FLAG_IMMUTABLE);
mSplitScreenController.startIntent(pendingIntent, mContext.getUserId(), null,
SPLIT_POSITION_TOP_OR_LEFT, null /* options */, null /* hideTaskToken */,
- true /* forceLaunchNewTask */);
+ true /* forceLaunchNewTask */, SPLIT_INDEX_0);
verify(mRecentTasks, never()).findTaskInBackground(any(), anyInt(), any());
}
@@ -312,7 +318,7 @@
PendingIntent.getActivity(mContext, 0, startIntent, FLAG_IMMUTABLE);
mSplitScreenController.startIntent(pendingIntent, mContext.getUserId(), null,
SPLIT_POSITION_TOP_OR_LEFT, null /* options */, null /* hideTaskToken */,
- false /* forceLaunchNewTask */);
+ false /* forceLaunchNewTask */, SPLIT_INDEX_0);
verify(mRecentTasks).findTaskInBackground(any(), anyInt(), any());
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java
index 66dcef6..d13a888 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java
@@ -53,8 +53,8 @@
doReturn(dividerBounds).when(out).getDividerBounds();
doReturn(dividerBounds).when(out).getRefDividerBounds();
doReturn(leash).when(out).getDividerLeash();
- doReturn(bounds1).when(out).getBounds1();
- doReturn(bounds2).when(out).getBounds2();
+ doReturn(bounds1).when(out).getTopLeftBounds();
+ doReturn(bounds2).when(out).getBottomRightBounds();
return out;
}
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 ce3944a..e32cf38 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
@@ -27,6 +27,7 @@
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_MAIN;
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_SIDE;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_DRAG_DIVIDER;
@@ -133,11 +134,11 @@
mSplitLayout = SplitTestUtils.createMockSplitLayout();
mMainStage = spy(new StageTaskListener(mContext, mTaskOrganizer, DEFAULT_DISPLAY, mock(
StageTaskListener.StageListenerCallbacks.class), mSyncQueue,
- mIconProvider, Optional.of(mWindowDecorViewModel)));
+ mIconProvider, Optional.of(mWindowDecorViewModel), STAGE_TYPE_MAIN));
mMainStage.onTaskAppeared(new TestRunningTaskInfoBuilder().build(), createMockSurface());
mSideStage = spy(new StageTaskListener(mContext, mTaskOrganizer, DEFAULT_DISPLAY, mock(
StageTaskListener.StageListenerCallbacks.class), mSyncQueue,
- mIconProvider, Optional.of(mWindowDecorViewModel)));
+ mIconProvider, Optional.of(mWindowDecorViewModel), STAGE_TYPE_SIDE));
mSideStage.onTaskAppeared(new TestRunningTaskInfoBuilder().build(), createMockSurface());
mStageCoordinator = new SplitTestUtils.TestStageCoordinator(mContext, DEFAULT_DISPLAY,
mSyncQueue, mTaskOrganizer, mMainStage, mSideStage, mDisplayController,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
index a252a9d..4f6f3c6 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
@@ -19,6 +19,7 @@
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.view.Display.DEFAULT_DISPLAY;
+import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_INDEX_UNDEFINED;
import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED;
@@ -140,8 +141,8 @@
Optional.empty()));
mDividerLeash = new SurfaceControl.Builder().setName("fakeDivider").build();
- when(mSplitLayout.getBounds1()).thenReturn(mBounds1);
- when(mSplitLayout.getBounds2()).thenReturn(mBounds2);
+ when(mSplitLayout.getTopLeftBounds()).thenReturn(mBounds1);
+ when(mSplitLayout.getBottomRightBounds()).thenReturn(mBounds2);
when(mSplitLayout.getRootBounds()).thenReturn(mRootBounds);
when(mSplitLayout.isLeftRightSplit()).thenReturn(false);
when(mSplitLayout.applyTaskChanges(any(), any(), any())).thenReturn(true);
@@ -165,8 +166,9 @@
final WindowContainerTransaction wct = spy(new WindowContainerTransaction());
mStageCoordinator.moveToStage(task, SPLIT_POSITION_BOTTOM_OR_RIGHT, wct);
+ // TODO(b/349828130) Address this once we remove index_undefined called
verify(mStageCoordinator).prepareEnterSplitScreen(eq(wct), eq(task),
- eq(SPLIT_POSITION_BOTTOM_OR_RIGHT), eq(false));
+ eq(SPLIT_POSITION_BOTTOM_OR_RIGHT), eq(false), eq(SPLIT_INDEX_UNDEFINED));
verify(mMainStage).reparentTopTask(eq(wct));
assertEquals(SPLIT_POSITION_BOTTOM_OR_RIGHT, mStageCoordinator.getSideStagePosition());
assertEquals(SPLIT_POSITION_TOP_OR_LEFT, mStageCoordinator.getMainStagePosition());
@@ -183,8 +185,9 @@
final WindowContainerTransaction wct = new WindowContainerTransaction();
mStageCoordinator.moveToStage(task, SPLIT_POSITION_BOTTOM_OR_RIGHT, wct);
+ // TODO(b/349828130) Address this once we remove index_undefined called
verify(mStageCoordinator).prepareEnterSplitScreen(eq(wct), eq(task),
- eq(SPLIT_POSITION_BOTTOM_OR_RIGHT), eq(false));
+ eq(SPLIT_POSITION_BOTTOM_OR_RIGHT), eq(false), eq(SPLIT_INDEX_UNDEFINED));
assertEquals(SPLIT_POSITION_BOTTOM_OR_RIGHT, mStageCoordinator.getMainStagePosition());
assertEquals(SPLIT_POSITION_TOP_OR_LEFT, mStageCoordinator.getSideStagePosition());
}
@@ -195,8 +198,9 @@
final WindowContainerTransaction wct = new WindowContainerTransaction();
mStageCoordinator.moveToStage(task, SPLIT_POSITION_BOTTOM_OR_RIGHT, wct);
+ // TODO(b/349828130) Address this once we remove index_undefined called
verify(mStageCoordinator).prepareEnterSplitScreen(eq(wct), eq(task),
- eq(SPLIT_POSITION_BOTTOM_OR_RIGHT), eq(false));
+ eq(SPLIT_POSITION_BOTTOM_OR_RIGHT), eq(false), eq(SPLIT_INDEX_UNDEFINED));
assertEquals(SPLIT_POSITION_BOTTOM_OR_RIGHT, mStageCoordinator.getSideStagePosition());
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java
index 7144a1e..fe91440 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java
@@ -19,6 +19,8 @@
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.view.Display.DEFAULT_DISPLAY;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
+
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertFalse;
@@ -93,7 +95,8 @@
mCallbacks,
mSyncQueue,
mIconProvider,
- Optional.of(mWindowDecorViewModel));
+ Optional.of(mWindowDecorViewModel),
+ STAGE_TYPE_UNDEFINED);
mRootTask = new TestRunningTaskInfoBuilder().build();
mRootTask.parentTaskId = INVALID_TASK_ID;
mSurfaceControl = new SurfaceControl.Builder().setName("test").build();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/unfold/UnfoldTransitionHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/unfold/UnfoldTransitionHandlerTest.java
index c36b88e..71af97e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/unfold/UnfoldTransitionHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/unfold/UnfoldTransitionHandlerTest.java
@@ -43,6 +43,7 @@
import android.window.TransitionRequestInfo;
import android.window.WindowContainerTransaction;
+import com.android.wm.shell.TestSyncExecutor;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.shared.TransactionPool;
import com.android.wm.shell.sysui.ShellInit;
@@ -475,27 +476,6 @@
}
}
- private static class TestSyncExecutor implements ShellExecutor {
- @Override
- public void execute(Runnable runnable) {
- runnable.run();
- }
-
- @Override
- public void executeDelayed(Runnable runnable, long delayMillis) {
- runnable.run();
- }
-
- @Override
- public void removeCallbacks(Runnable runnable) {
- }
-
- @Override
- public boolean hasCallback(Runnable runnable) {
- return false;
- }
- }
-
private TransitionInfo createUnfoldTransitionInfo() {
TransitionInfo transitionInfo = new TransitionInfo(TRANSIT_CHANGE, /* flags= */ 0);
TransitionInfo.Change change = new TransitionInfo.Change(null, mock(SurfaceControl.class));
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/util/TransitionObserverTestUtils.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/util/TransitionObserverTestUtils.kt
new file mode 100644
index 0000000..3e26ee0
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/util/TransitionObserverTestUtils.kt
@@ -0,0 +1,182 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.util
+
+import android.app.ActivityManager.RunningTaskInfo
+import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
+import android.content.ComponentName
+import android.content.Intent
+import android.os.IBinder
+import android.view.Display.DEFAULT_DISPLAY
+import android.view.SurfaceControl
+import android.view.SurfaceControl.Transaction
+import android.view.WindowManager.TRANSIT_NONE
+import android.view.WindowManager.TransitionFlags
+import android.view.WindowManager.TransitionType
+import android.window.IWindowContainerToken
+import android.window.TransitionInfo
+import android.window.TransitionInfo.Change
+import android.window.TransitionInfo.ChangeFlags
+import android.window.TransitionInfo.FLAG_NONE
+import android.window.TransitionInfo.TransitionMode
+import android.window.WindowContainerToken
+import com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn
+import com.android.wm.shell.transition.Transitions.TransitionObserver
+import org.mockito.Mockito
+import org.mockito.kotlin.mock
+
+@DslMarker
+annotation class TransitionObserverTagMarker
+
+/**
+ * Abstraction for all the phases of the [TransitionObserver] test.
+ */
+interface TransitionObserverTestStep
+
+/**
+ * Encapsulates the values for the [TransitionObserver#onTransitionReady] input parameters.
+ */
+class TransitionObserverTransitionReadyInput(
+ val transition: IBinder,
+ val info: TransitionInfo,
+ val startTransaction: Transaction,
+ val finishTransaction: Transaction
+)
+
+@TransitionObserverTagMarker
+class TransitionObserverTestContext : TransitionObserverTestStep {
+
+ lateinit var transitionObserver: TransitionObserver
+ lateinit var transitionReadyInput: TransitionObserverTransitionReadyInput
+
+ fun inputBuilder(builderInput: TransitionObserverInputBuilder.() -> Unit) {
+ val inputFactoryObj = TransitionObserverInputBuilder()
+ inputFactoryObj.builderInput()
+ transitionReadyInput = inputFactoryObj.build()
+ }
+
+ fun validateOutput(
+ validate:
+ TransitionObserverResultValidation.() -> Unit
+ ) {
+ val validateObj = TransitionObserverResultValidation()
+ invokeObservable()
+ validateObj.validate()
+ }
+
+ fun invokeObservable() {
+ transitionObserver.onTransitionReady(
+ transitionReadyInput.transition,
+ transitionReadyInput.info,
+ transitionReadyInput.startTransaction,
+ transitionReadyInput.finishTransaction
+ )
+ }
+}
+
+/**
+ * Phase responsible for the input parameters for [TransitionObserver].
+ */
+class TransitionObserverInputBuilder : TransitionObserverTestStep {
+
+ private val transition = Mockito.mock(IBinder::class.java)
+ private var transitionInfo: TransitionInfo? = null
+ private val startTransaction = Mockito.mock(Transaction::class.java)
+ private val finishTransaction = Mockito.mock(Transaction::class.java)
+
+ fun buildTransitionInfo(
+ @TransitionType type: Int = TRANSIT_NONE,
+ @TransitionFlags flags: Int = 0
+ ) {
+ transitionInfo = TransitionInfo(type, flags)
+ spyOn(transitionInfo)
+ }
+
+ fun addChange(
+ token: WindowContainerToken? = mock(),
+ leash: SurfaceControl = mock(),
+ @TransitionMode changeMode: Int = TRANSIT_NONE,
+ parentToken: WindowContainerToken? = null,
+ changeTaskInfo: RunningTaskInfo? = null,
+ @ChangeFlags changeFlags: Int = FLAG_NONE
+ ) = addChange(Change(token, leash).apply {
+ mode = changeMode
+ parent = parentToken
+ taskInfo = changeTaskInfo
+ flags = changeFlags
+ })
+
+ fun createChange(
+ token: WindowContainerToken? = mock(),
+ leash: SurfaceControl = mock(),
+ @TransitionMode changeMode: Int = TRANSIT_NONE,
+ parentToken: WindowContainerToken? = null,
+ changeTaskInfo: RunningTaskInfo? = null,
+ @ChangeFlags changeFlags: Int = FLAG_NONE
+ ) = Change(token, leash).apply {
+ mode = changeMode
+ parent = parentToken
+ taskInfo = changeTaskInfo
+ flags = changeFlags
+ }
+
+ fun addChange(change: Change) {
+ transitionInfo!!.addChange(change)
+ }
+
+ fun createTaskInfo(id: Int = 0, windowingMode: Int = WINDOWING_MODE_FREEFORM) =
+ RunningTaskInfo().apply {
+ taskId = id
+ displayId = DEFAULT_DISPLAY
+ configuration.windowConfiguration.windowingMode = windowingMode
+ token = WindowContainerToken(Mockito.mock(IWindowContainerToken::class.java))
+ baseIntent = Intent().apply {
+ component = ComponentName("package", "component.name")
+ }
+ }
+
+ fun build(): TransitionObserverTransitionReadyInput = TransitionObserverTransitionReadyInput(
+ transition = transition,
+ info = transitionInfo!!,
+ startTransaction = startTransaction,
+ finishTransaction = finishTransaction
+ )
+}
+
+/**
+ * Phase responsible for the execution of validation methods.
+ */
+class TransitionObserverResultValidation : TransitionObserverTestStep
+
+/**
+ * Allows to run a test about a specific [TransitionObserver] passing the specific
+ * implementation and input value as parameters for the [TransitionObserver#onTransitionReady]
+ * method.
+ * @param observerFactory The Factory for the TransitionObserver
+ * @param inputFactory The Builder for the onTransitionReady input parameters
+ * @param init The test code itself.
+ */
+fun executeTransitionObserverTest(
+ observerFactory: () -> TransitionObserver,
+ init: TransitionObserverTestContext.() -> Unit
+): TransitionObserverTestContext {
+ val testContext = TransitionObserverTestContext().apply {
+ transitionObserver = observerFactory()
+ }
+ testContext.init()
+ return testContext
+}
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 be664f8..ef9b30c 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
@@ -63,7 +63,6 @@
import android.view.View
import android.view.ViewRootImpl
import android.view.WindowInsets.Type.statusBars
-import android.widget.Toast
import android.window.WindowContainerTransaction
import android.window.WindowContainerTransaction.HierarchyOp
import androidx.test.filters.SmallTest
@@ -186,7 +185,6 @@
@Mock private lateinit var mockGenericLinksParser: AppToWebGenericLinksParser
@Mock private lateinit var mockUserHandle: UserHandle
@Mock private lateinit var mockAssistContentRequester: AssistContentRequester
- @Mock private lateinit var mockToast: Toast
private val bgExecutor = TestShellExecutor()
@Mock private lateinit var mockMultiInstanceHelper: MultiInstanceHelper
@Mock private lateinit var mockTasksLimiter: DesktopTasksLimiter
@@ -226,7 +224,6 @@
.strictness(Strictness.LENIENT)
.spyStatic(DesktopModeStatus::class.java)
.spyStatic(DragPositioningCallbackUtility::class.java)
- .spyStatic(Toast::class.java)
.startMocking()
doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(Mockito.any()) }
@@ -290,8 +287,6 @@
)
.thenReturn(mockTaskPositioner)
- doReturn(mockToast).`when` { Toast.makeText(any(), anyInt(), anyInt()) }
-
// InputChannel cannot be mocked because it passes to InputEventReceiver.
val inputChannels = InputChannel.openInputChannelPair(TAG)
inputChannels.first().dispose()
@@ -640,7 +635,6 @@
@Test
fun testOnDecorSnappedLeft_snapResizes() {
- val taskSurfaceCaptor = argumentCaptor<SurfaceControl>()
val onLeftSnapClickListenerCaptor = forClass(Function0::class.java)
as ArgumentCaptor<Function0<Unit>>
val decor = createOpenTaskDecoration(
@@ -648,19 +642,15 @@
onLeftSnapClickListenerCaptor = onLeftSnapClickListenerCaptor
)
- val currentBounds = decor.mTaskInfo.configuration.windowConfiguration.bounds
onLeftSnapClickListenerCaptor.value.invoke()
- verify(mockDesktopTasksController).snapToHalfScreen(
+ verify(mockDesktopTasksController).handleInstantSnapResizingTask(
eq(decor.mTaskInfo),
- taskSurfaceCaptor.capture(),
- eq(currentBounds),
eq(SnapPosition.LEFT),
eq(ResizeTrigger.SNAP_LEFT_MENU),
eq(null),
eq(decor)
)
- assertEquals(taskSurfaceCaptor.firstValue, decor.mTaskSurface)
}
@Test
@@ -681,7 +671,6 @@
@Test
@DisableFlags(Flags.FLAG_DISABLE_NON_RESIZABLE_APP_SNAP_RESIZING)
fun testOnSnapResizeLeft_nonResizable_decorSnappedLeft() {
- val taskSurfaceCaptor = argumentCaptor<SurfaceControl>()
val onLeftSnapClickListenerCaptor = forClass(Function0::class.java)
as ArgumentCaptor<Function0<Unit>>
val decor = createOpenTaskDecoration(
@@ -689,19 +678,15 @@
onLeftSnapClickListenerCaptor = onLeftSnapClickListenerCaptor
).also { it.mTaskInfo.isResizeable = false }
- val currentBounds = decor.mTaskInfo.configuration.windowConfiguration.bounds
onLeftSnapClickListenerCaptor.value.invoke()
- verify(mockDesktopTasksController).snapToHalfScreen(
+ verify(mockDesktopTasksController).handleInstantSnapResizingTask(
eq(decor.mTaskInfo),
- taskSurfaceCaptor.capture(),
- eq(currentBounds),
eq(SnapPosition.LEFT),
eq(ResizeTrigger.SNAP_LEFT_MENU),
eq(null),
eq(decor),
)
- assertEquals(decor.mTaskSurface, taskSurfaceCaptor.firstValue)
}
@Test
@@ -723,12 +708,10 @@
eq(null),
eq(decor),
)
- verify(mockToast).show()
}
@Test
fun testOnDecorSnappedRight_snapResizes() {
- val taskSurfaceCaptor = argumentCaptor<SurfaceControl>()
val onRightSnapClickListenerCaptor = forClass(Function0::class.java)
as ArgumentCaptor<Function0<Unit>>
val decor = createOpenTaskDecoration(
@@ -736,19 +719,15 @@
onRightSnapClickListenerCaptor = onRightSnapClickListenerCaptor
)
- val currentBounds = decor.mTaskInfo.configuration.windowConfiguration.bounds
onRightSnapClickListenerCaptor.value.invoke()
- verify(mockDesktopTasksController).snapToHalfScreen(
+ verify(mockDesktopTasksController).handleInstantSnapResizingTask(
eq(decor.mTaskInfo),
- taskSurfaceCaptor.capture(),
- eq(currentBounds),
eq(SnapPosition.RIGHT),
eq(ResizeTrigger.SNAP_RIGHT_MENU),
eq(null),
eq(decor),
)
- assertEquals(decor.mTaskSurface, taskSurfaceCaptor.firstValue)
}
@Test
@@ -769,7 +748,6 @@
@Test
@DisableFlags(Flags.FLAG_DISABLE_NON_RESIZABLE_APP_SNAP_RESIZING)
fun testOnSnapResizeRight_nonResizable_decorSnappedRight() {
- val taskSurfaceCaptor = argumentCaptor<SurfaceControl>()
val onRightSnapClickListenerCaptor = forClass(Function0::class.java)
as ArgumentCaptor<Function0<Unit>>
val decor = createOpenTaskDecoration(
@@ -777,19 +755,15 @@
onRightSnapClickListenerCaptor = onRightSnapClickListenerCaptor
).also { it.mTaskInfo.isResizeable = false }
- val currentBounds = decor.mTaskInfo.configuration.windowConfiguration.bounds
onRightSnapClickListenerCaptor.value.invoke()
- verify(mockDesktopTasksController).snapToHalfScreen(
+ verify(mockDesktopTasksController).handleInstantSnapResizingTask(
eq(decor.mTaskInfo),
- taskSurfaceCaptor.capture(),
- eq(currentBounds),
eq(SnapPosition.RIGHT),
eq(ResizeTrigger.SNAP_RIGHT_MENU),
eq(null),
eq(decor),
)
- assertEquals(decor.mTaskSurface, taskSurfaceCaptor.firstValue)
}
@Test
@@ -811,7 +785,6 @@
eq(null),
eq(decor),
)
- verify(mockToast).show()
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
index 1d2d0f0..f653622 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
@@ -30,6 +30,7 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
import static com.android.wm.shell.MockSurfaceControlHelper.createMockSurfaceControlTransaction;
import static com.android.wm.shell.windowdecor.DesktopModeWindowDecoration.CLOSE_MAXIMIZE_MENU_DELAY_MS;
+import static com.android.wm.shell.windowdecor.WindowDecoration.INVALID_CORNER_RADIUS;
import static com.google.common.truth.Truth.assertThat;
@@ -219,7 +220,7 @@
@Captor
private ArgumentCaptor<Runnable> mCloseMaxMenuRunnable;
- private final InsetsState mInsetsState = new InsetsState();
+ private final InsetsState mInsetsState = createInsetsState(statusBars(), /* visible= */true);
private SurfaceControl.Transaction mMockTransaction;
private StaticMockitoSession mMockitoSession;
private TestableContext mTestableContext;
@@ -312,8 +313,9 @@
}
@Test
- public void updateRelayoutParams_noSysPropFlagsSet_roundedCornersAreEnabled() {
+ public void updateRelayoutParams_noSysPropFlagsSet_roundedCornersSetForFreeform() {
final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
+ taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
fillRoundedCornersResources(/* fillValue= */ 30);
RelayoutParams relayoutParams = new RelayoutParams();
@@ -334,6 +336,29 @@
}
@Test
+ public void updateRelayoutParams_noSysPropFlagsSet_roundedCornersNotSetForFullscreen() {
+ final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
+ taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+ fillRoundedCornersResources(/* fillValue= */ 30);
+ RelayoutParams relayoutParams = new RelayoutParams();
+
+ DesktopModeWindowDecoration.updateRelayoutParams(
+ relayoutParams,
+ mTestableContext,
+ taskInfo,
+ /* applyStartTransactionOnDraw= */ true,
+ /* shouldSetTaskPositionAndCrop */ false,
+ /* isStatusBarVisible */ true,
+ /* isKeyguardVisibleAndOccluded */ false,
+ /* inFullImmersiveMode */ false,
+ new InsetsState(),
+ /* hasGlobalFocus= */ true,
+ mExclusionRegion);
+
+ assertThat(relayoutParams.mCornerRadius).isEqualTo(INVALID_CORNER_RADIUS);
+ }
+
+ @Test
@EnableFlags(Flags.FLAG_ENABLE_APP_HEADER_WITH_TASK_DENSITY)
public void updateRelayoutParams_appHeader_usesTaskDensity() {
final int systemDensity = mTestableContext.getOrCreateTestableResources().getResources()
@@ -1408,8 +1433,6 @@
public void notifyCaptionStateChanged_flagDisabled_doNoNotify() {
when(DesktopModeStatus.canEnterDesktopMode(mContext)).thenReturn(true);
final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
- when(mMockDisplayController.getInsetsState(taskInfo.displayId))
- .thenReturn(createInsetsState(statusBars(), /* visible= */true));
final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo));
taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
@@ -1423,8 +1446,6 @@
public void notifyCaptionStateChanged_inFullscreenMode_notifiesAppHandleVisible() {
when(DesktopModeStatus.canEnterDesktopMode(mContext)).thenReturn(true);
final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
- when(mMockDisplayController.getInsetsState(taskInfo.displayId))
- .thenReturn(createInsetsState(statusBars(), /* visible= */true));
final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo));
taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
ArgumentCaptor<CaptionState> captionStateArgumentCaptor = ArgumentCaptor.forClass(
@@ -1444,8 +1465,6 @@
public void notifyCaptionStateChanged_inWindowingMode_notifiesAppHeaderVisible() {
when(DesktopModeStatus.canEnterDesktopMode(mContext)).thenReturn(true);
final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
- when(mMockDisplayController.getInsetsState(taskInfo.displayId))
- .thenReturn(createInsetsState(statusBars(), /* visible= */true));
when(mMockAppHeaderViewHolder.getAppChipLocationInWindow()).thenReturn(
new Rect(/* left= */ 0, /* top= */ 1, /* right= */ 2, /* bottom= */ 3));
final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo));
@@ -1473,8 +1492,6 @@
public void notifyCaptionStateChanged_taskNotVisible_notifiesNoCaptionVisible() {
when(DesktopModeStatus.canEnterDesktopMode(mContext)).thenReturn(true);
final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ false);
- when(mMockDisplayController.getInsetsState(taskInfo.displayId))
- .thenReturn(createInsetsState(statusBars(), /* visible= */true));
final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo));
taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_UNDEFINED);
ArgumentCaptor<CaptionState> captionStateArgumentCaptor = ArgumentCaptor.forClass(
@@ -1493,8 +1510,6 @@
public void notifyCaptionStateChanged_captionHandleExpanded_notifiesHandleMenuExpanded() {
when(DesktopModeStatus.canEnterDesktopMode(mContext)).thenReturn(true);
final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
- when(mMockDisplayController.getInsetsState(taskInfo.displayId))
- .thenReturn(createInsetsState(statusBars(), /* visible= */true));
final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo));
taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
ArgumentCaptor<CaptionState> captionStateArgumentCaptor = ArgumentCaptor.forClass(
@@ -1518,8 +1533,6 @@
public void notifyCaptionStateChanged_captionHandleClosed_notifiesHandleMenuClosed() {
when(DesktopModeStatus.canEnterDesktopMode(mContext)).thenReturn(true);
final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
- when(mMockDisplayController.getInsetsState(taskInfo.displayId))
- .thenReturn(createInsetsState(statusBars(), /* visible= */true));
final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo));
taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
ArgumentCaptor<CaptionState> captionStateArgumentCaptor = ArgumentCaptor.forClass(
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index fcb7efc..e2db2c9 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -355,6 +355,7 @@
"jni/AnimatedImageDrawable.cpp",
"jni/Bitmap.cpp",
"jni/BitmapRegionDecoder.cpp",
+ "jni/RuntimeXfermode.cpp",
"jni/BufferUtils.cpp",
"jni/HardwareBufferHelpers.cpp",
"jni/BitmapFactory.cpp",
diff --git a/libs/hwui/DeviceInfo.h b/libs/hwui/DeviceInfo.h
index fb58a69..b72e066 100644
--- a/libs/hwui/DeviceInfo.h
+++ b/libs/hwui/DeviceInfo.h
@@ -84,6 +84,7 @@
// this value is only valid after the GPU has been initialized and there is a valid graphics
// context or if you are using the HWUI_NULL_GPU
int maxTextureSize() const;
+ bool hasMaxTextureSize() const { return mMaxTextureSize > 0; }
sk_sp<SkColorSpace> getWideColorSpace() const { return mWideColorSpace; }
SkColorType getWideColorType() {
static std::once_flag kFlag;
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index 2c23864..4801bd1 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -186,7 +186,7 @@
// If we are not a layer OR we cannot be rendered (eg, view was detached)
// we need to destroy any Layers we may have had previously
if (CC_LIKELY(layerType != LayerType::RenderLayer) || CC_UNLIKELY(!isRenderable()) ||
- CC_UNLIKELY(properties().getWidth() == 0) || CC_UNLIKELY(properties().getHeight() == 0) ||
+ CC_UNLIKELY(properties().getWidth() <= 0) || CC_UNLIKELY(properties().getHeight() <= 0) ||
CC_UNLIKELY(!properties().fitsOnLayer())) {
if (CC_UNLIKELY(hasLayer())) {
this->setLayerSurface(nullptr);
diff --git a/libs/hwui/RenderProperties.h b/libs/hwui/RenderProperties.h
index b1ad8b2..4dc5700 100644
--- a/libs/hwui/RenderProperties.h
+++ b/libs/hwui/RenderProperties.h
@@ -545,7 +545,8 @@
bool fitsOnLayer() const {
const DeviceInfo* deviceInfo = DeviceInfo::get();
return mPrimitiveFields.mWidth <= deviceInfo->maxTextureSize() &&
- mPrimitiveFields.mHeight <= deviceInfo->maxTextureSize();
+ mPrimitiveFields.mHeight <= deviceInfo->maxTextureSize() &&
+ mPrimitiveFields.mWidth > 0 && mPrimitiveFields.mHeight > 0;
}
bool promotedToLayer() const {
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index 7c15086..7f5ca44 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -622,18 +622,13 @@
auto colorSpace = info.colorSpace();
// If we don't have a colorspace, we can't apply a gainmap
if (!colorSpace) return false;
- skcms_TransferFunction tfn;
- colorSpace->transferFn(&tfn);
- auto transferType = skcms_TransferFunction_getType(&tfn);
- switch (transferType) {
- case skcms_TFType_HLGish:
- case skcms_TFType_HLGinvish:
- case skcms_TFType_PQish:
- return true;
- case skcms_TFType_Invalid:
- case skcms_TFType_sRGBish:
- return false;
+ const float targetRatio = uirenderer::getTargetHdrSdrRatio(colorSpace);
+
+ if (bitmap.gainmap()->info.fBaseImageType == SkGainmapInfo::BaseImageType::kHDR) {
+ return targetRatio < bitmap.gainmap()->info.fDisplayRatioHdr;
+ } else {
+ return targetRatio > bitmap.gainmap()->info.fDisplayRatioSdr;
}
}
diff --git a/libs/hwui/apex/jni_runtime.cpp b/libs/hwui/apex/jni_runtime.cpp
index 15b2bac..56de568 100644
--- a/libs/hwui/apex/jni_runtime.cpp
+++ b/libs/hwui/apex/jni_runtime.cpp
@@ -28,6 +28,7 @@
extern int register_android_graphics_Bitmap(JNIEnv*);
extern int register_android_graphics_BitmapFactory(JNIEnv*);
extern int register_android_graphics_BitmapRegionDecoder(JNIEnv*);
+extern int register_android_graphics_RuntimeXfermode(JNIEnv*);
extern int register_android_graphics_ByteBufferStreamAdaptor(JNIEnv* env);
extern int register_android_graphics_Camera(JNIEnv* env);
extern int register_android_graphics_CreateJavaOutputStreamAdaptor(JNIEnv* env);
@@ -107,6 +108,7 @@
REG_JNI(register_android_graphics_Bitmap),
REG_JNI(register_android_graphics_BitmapFactory),
REG_JNI(register_android_graphics_BitmapRegionDecoder),
+ REG_JNI(register_android_graphics_RuntimeXfermode),
REG_JNI(register_android_graphics_ByteBufferStreamAdaptor),
REG_JNI(register_android_graphics_Camera),
REG_JNI(register_android_graphics_CreateJavaOutputStreamAdaptor),
diff --git a/libs/hwui/effects/GainmapRenderer.cpp b/libs/hwui/effects/GainmapRenderer.cpp
index eac0360..18b77bc 100644
--- a/libs/hwui/effects/GainmapRenderer.cpp
+++ b/libs/hwui/effects/GainmapRenderer.cpp
@@ -73,7 +73,9 @@
#ifdef __ANDROID__
auto destColorspace = c->imageInfo().refColorSpace();
float targetSdrHdrRatio = getTargetHdrSdrRatio(destColorspace.get());
- if (targetSdrHdrRatio > 1.f && gainmapImage) {
+ const bool baseImageHdr = gainmapInfo.fBaseImageType == SkGainmapInfo::BaseImageType::kHDR;
+ if (gainmapImage && ((baseImageHdr && targetSdrHdrRatio < gainmapInfo.fDisplayRatioHdr) ||
+ (!baseImageHdr && targetSdrHdrRatio > gainmapInfo.fDisplayRatioSdr))) {
SkPaint gainmapPaint = *paint;
float sX = gainmapImage->width() / (float)image->width();
float sY = gainmapImage->height() / (float)image->height();
@@ -183,7 +185,10 @@
baseImage->colorSpace() ? baseImage->refColorSpace() : SkColorSpace::MakeSRGB();
// Determine the color space in which the gainmap math is to be applied.
- sk_sp<SkColorSpace> gainmapMathColorSpace = baseColorSpace->makeLinearGamma();
+ sk_sp<SkColorSpace> gainmapMathColorSpace =
+ mGainmapInfo.fGainmapMathColorSpace
+ ? mGainmapInfo.fGainmapMathColorSpace->makeLinearGamma()
+ : baseColorSpace->makeLinearGamma();
// Create a color filter to transform from the base image's color space to the color space
// in which the gainmap is to be applied.
@@ -266,6 +271,10 @@
W = 1.f;
}
}
+
+ if (mGainmapInfo.fBaseImageType == SkGainmapInfo::BaseImageType::kHDR) {
+ W -= 1.f;
+ }
mBuilder.uniform("W") = W;
uniforms = mBuilder.uniforms();
}
@@ -317,4 +326,4 @@
#endif // __ANDROID__
-} // namespace android::uirenderer
\ No newline at end of file
+} // namespace android::uirenderer
diff --git a/libs/hwui/jni/Paint.cpp b/libs/hwui/jni/Paint.cpp
index da23792..a7d855d 100644
--- a/libs/hwui/jni/Paint.cpp
+++ b/libs/hwui/jni/Paint.cpp
@@ -906,6 +906,13 @@
paint->setBlendMode(mode);
}
+ static void setRuntimeXfermode(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle,
+ jlong xfermodeHandle) {
+ Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+ SkBlender* blender = reinterpret_cast<SkBlender*>(xfermodeHandle);
+ paint->setBlender(sk_ref_sp(blender));
+ }
+
static jlong setPathEffect(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jlong effectHandle) {
Paint* obj = reinterpret_cast<Paint*>(objHandle);
SkPathEffect* effect = reinterpret_cast<SkPathEffect*>(effectHandle);
@@ -1233,6 +1240,7 @@
{"nSetShader", "(JJ)J", (void*)PaintGlue::setShader},
{"nSetColorFilter", "(JJ)J", (void*)PaintGlue::setColorFilter},
{"nSetXfermode", "(JI)V", (void*)PaintGlue::setXfermode},
+ {"nSetXfermode", "(JJ)V", (void*)PaintGlue::setRuntimeXfermode},
{"nSetPathEffect", "(JJ)J", (void*)PaintGlue::setPathEffect},
{"nSetMaskFilter", "(JJ)J", (void*)PaintGlue::setMaskFilter},
{"nSetTypeface", "(JJ)V", (void*)PaintGlue::setTypeface},
diff --git a/libs/hwui/jni/RuntimeEffectUtils.cpp b/libs/hwui/jni/RuntimeEffectUtils.cpp
index 46db863..ad0e540 100644
--- a/libs/hwui/jni/RuntimeEffectUtils.cpp
+++ b/libs/hwui/jni/RuntimeEffectUtils.cpp
@@ -90,7 +90,7 @@
SkFlattenable* childEffect) {
SkRuntimeShaderBuilder::BuilderChild builderChild = builder->child(childName);
if (builderChild.fChild == nullptr) {
- ThrowIAEFmt(env, "unable to find shader named %s", childName);
+ ThrowIAEFmt(env, "unable to find child named %s", childName);
return;
}
diff --git a/libs/hwui/jni/RuntimeXfermode.cpp b/libs/hwui/jni/RuntimeXfermode.cpp
new file mode 100644
index 0000000..c1c8964
--- /dev/null
+++ b/libs/hwui/jni/RuntimeXfermode.cpp
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "GraphicsJNI.h"
+#include "RuntimeEffectUtils.h"
+#include "SkBlender.h"
+
+using namespace android::uirenderer;
+
+static void SkRuntimeEffectBuilder_delete(SkRuntimeEffectBuilder* builder) {
+ delete builder;
+}
+
+static jlong RuntimeXfermode_getNativeFinalizer(JNIEnv*, jobject) {
+ return static_cast<jlong>(reinterpret_cast<uintptr_t>(&SkRuntimeEffectBuilder_delete));
+}
+
+static jlong RuntimeXfermode_createBuilder(JNIEnv* env, jobject, jstring sksl) {
+ ScopedUtfChars strSksl(env, sksl);
+ auto result =
+ SkRuntimeEffect::MakeForBlender(SkString(strSksl.c_str()), SkRuntimeEffect::Options{});
+ if (result.effect.get() == nullptr) {
+ doThrowIAE(env, result.errorText.c_str());
+ return 0;
+ }
+ return reinterpret_cast<jlong>(new SkRuntimeEffectBuilder(std::move(result.effect)));
+}
+
+static jlong RuntimeXfermode_create(JNIEnv* env, jobject, jlong builderPtr) {
+ auto* builder = reinterpret_cast<SkRuntimeEffectBuilder*>(builderPtr);
+ sk_sp<SkBlender> blender = builder->makeBlender();
+ if (!blender) {
+ doThrowIAE(env);
+ }
+ return reinterpret_cast<jlong>(blender.release());
+}
+
+static void RuntimeXfermode_updateFloatArrayUniforms(JNIEnv* env, jobject, jlong builderPtr,
+ jstring uniformName, jfloatArray uniforms,
+ jboolean isColor) {
+ auto* builder = reinterpret_cast<SkRuntimeEffectBuilder*>(builderPtr);
+ ScopedUtfChars name(env, uniformName);
+ AutoJavaFloatArray autoValues(env, uniforms, 0, kRO_JNIAccess);
+ UpdateFloatUniforms(env, builder, name.c_str(), autoValues.ptr(), autoValues.length(), isColor);
+}
+
+static void RuntimeXfermode_updateFloatUniforms(JNIEnv* env, jobject, jlong builderPtr,
+ jstring uniformName, jfloat value1, jfloat value2,
+ jfloat value3, jfloat value4, jint count) {
+ auto* builder = reinterpret_cast<SkRuntimeEffectBuilder*>(builderPtr);
+ ScopedUtfChars name(env, uniformName);
+ const float values[4] = {value1, value2, value3, value4};
+ UpdateFloatUniforms(env, builder, name.c_str(), values, count, false);
+}
+
+static void RuntimeXfermode_updateIntArrayUniforms(JNIEnv* env, jobject, jlong builderPtr,
+ jstring uniformName, jintArray uniforms) {
+ auto* builder = reinterpret_cast<SkRuntimeEffectBuilder*>(builderPtr);
+ ScopedUtfChars name(env, uniformName);
+ AutoJavaIntArray autoValues(env, uniforms, 0);
+ UpdateIntUniforms(env, builder, name.c_str(), autoValues.ptr(), autoValues.length());
+}
+
+static void RuntimeXfermode_updateIntUniforms(JNIEnv* env, jobject, jlong builderPtr,
+ jstring uniformName, jint value1, jint value2,
+ jint value3, jint value4, jint count) {
+ auto* builder = reinterpret_cast<SkRuntimeEffectBuilder*>(builderPtr);
+ ScopedUtfChars name(env, uniformName);
+ const int values[4] = {value1, value2, value3, value4};
+ UpdateIntUniforms(env, builder, name.c_str(), values, count);
+}
+
+static void RuntimeXfermode_updateChild(JNIEnv* env, jobject, jlong builderPtr, jstring childName,
+ jlong childPtr) {
+ auto* builder = reinterpret_cast<SkRuntimeEffectBuilder*>(builderPtr);
+ ScopedUtfChars name(env, childName);
+ auto* child = reinterpret_cast<SkFlattenable*>(childPtr);
+ if (child) {
+ UpdateChild(env, builder, name.c_str(), child);
+ }
+}
+
+static const JNINativeMethod gRuntimeXfermodeMethods[] = {
+ {"nativeGetFinalizer", "()J", (void*)RuntimeXfermode_getNativeFinalizer},
+ {"nativeCreateBlenderBuilder", "(Ljava/lang/String;)J",
+ (void*)RuntimeXfermode_createBuilder},
+ {"nativeCreateNativeInstance", "(J)J", (void*)RuntimeXfermode_create},
+ {"nativeUpdateUniforms", "(JLjava/lang/String;[FZ)V",
+ (void*)RuntimeXfermode_updateFloatArrayUniforms},
+ {"nativeUpdateUniforms", "(JLjava/lang/String;FFFFI)V",
+ (void*)RuntimeXfermode_updateFloatUniforms},
+ {"nativeUpdateUniforms", "(JLjava/lang/String;[I)V",
+ (void*)RuntimeXfermode_updateIntArrayUniforms},
+ {"nativeUpdateUniforms", "(JLjava/lang/String;IIIII)V",
+ (void*)RuntimeXfermode_updateIntUniforms},
+ {"nativeUpdateChild", "(JLjava/lang/String;J)V", (void*)RuntimeXfermode_updateChild},
+};
+
+int register_android_graphics_RuntimeXfermode(JNIEnv* env) {
+ android::RegisterMethodsOrDie(env, "android/graphics/RuntimeXfermode", gRuntimeXfermodeMethods,
+ NELEM(gRuntimeXfermodeMethods));
+
+ return 0;
+}
diff --git a/libs/hwui/jni/Shader.cpp b/libs/hwui/jni/Shader.cpp
index 2a057e7..018c2b13 100644
--- a/libs/hwui/jni/Shader.cpp
+++ b/libs/hwui/jni/Shader.cpp
@@ -2,6 +2,7 @@
#include "Gainmap.h"
#include "GraphicsJNI.h"
+#include "RuntimeEffectUtils.h"
#include "SkBitmap.h"
#include "SkBlendMode.h"
#include "SkColor.h"
@@ -280,50 +281,6 @@
return ret;
}
-static bool isIntUniformType(const SkRuntimeEffect::Uniform::Type& type) {
- switch (type) {
- case SkRuntimeEffect::Uniform::Type::kFloat:
- case SkRuntimeEffect::Uniform::Type::kFloat2:
- case SkRuntimeEffect::Uniform::Type::kFloat3:
- case SkRuntimeEffect::Uniform::Type::kFloat4:
- case SkRuntimeEffect::Uniform::Type::kFloat2x2:
- case SkRuntimeEffect::Uniform::Type::kFloat3x3:
- case SkRuntimeEffect::Uniform::Type::kFloat4x4:
- return false;
- case SkRuntimeEffect::Uniform::Type::kInt:
- case SkRuntimeEffect::Uniform::Type::kInt2:
- case SkRuntimeEffect::Uniform::Type::kInt3:
- case SkRuntimeEffect::Uniform::Type::kInt4:
- return true;
- }
-}
-
-static void UpdateFloatUniforms(JNIEnv* env, SkRuntimeShaderBuilder* builder,
- const char* uniformName, const float values[], int count,
- bool isColor) {
- SkRuntimeShaderBuilder::BuilderUniform uniform = builder->uniform(uniformName);
- if (uniform.fVar == nullptr) {
- ThrowIAEFmt(env, "unable to find uniform named %s", uniformName);
- } else if (isColor != ((uniform.fVar->flags & SkRuntimeEffect::Uniform::kColor_Flag) != 0)) {
- if (isColor) {
- jniThrowExceptionFmt(
- env, "java/lang/IllegalArgumentException",
- "attempting to set a color uniform using the non-color specific APIs: %s %x",
- uniformName, uniform.fVar->flags);
- } else {
- ThrowIAEFmt(env,
- "attempting to set a non-color uniform using the setColorUniform APIs: %s",
- uniformName);
- }
- } else if (isIntUniformType(uniform.fVar->type)) {
- ThrowIAEFmt(env, "attempting to set a int uniform using the setUniform APIs: %s",
- uniformName);
- } else if (!uniform.set<float>(values, count)) {
- ThrowIAEFmt(env, "mismatch in byte size for uniform [expected: %zu actual: %zu]",
- uniform.fVar->sizeInBytes(), sizeof(float) * count);
- }
-}
-
static void RuntimeShader_updateFloatUniforms(JNIEnv* env, jobject, jlong shaderBuilder,
jstring jUniformName, jfloat value1, jfloat value2,
jfloat value3, jfloat value4, jint count) {
@@ -342,20 +299,6 @@
UpdateFloatUniforms(env, builder, name.c_str(), autoValues.ptr(), autoValues.length(), isColor);
}
-static void UpdateIntUniforms(JNIEnv* env, SkRuntimeShaderBuilder* builder, const char* uniformName,
- const int values[], int count) {
- SkRuntimeShaderBuilder::BuilderUniform uniform = builder->uniform(uniformName);
- if (uniform.fVar == nullptr) {
- ThrowIAEFmt(env, "unable to find uniform named %s", uniformName);
- } else if (!isIntUniformType(uniform.fVar->type)) {
- ThrowIAEFmt(env, "attempting to set a non-int uniform using the setIntUniform APIs: %s",
- uniformName);
- } else if (!uniform.set<int>(values, count)) {
- ThrowIAEFmt(env, "mismatch in byte size for uniform [expected: %zu actual: %zu]",
- uniform.fVar->sizeInBytes(), sizeof(float) * count);
- }
-}
-
static void RuntimeShader_updateIntUniforms(JNIEnv* env, jobject, jlong shaderBuilder,
jstring jUniformName, jint value1, jint value2,
jint value3, jint value4, jint count) {
@@ -388,6 +331,15 @@
builder->child(name.c_str()) = sk_ref_sp(shader);
}
+static void RuntimeShader_updateChild(JNIEnv* env, jobject, jlong shaderBuilder,
+ jstring jUniformName, jlong childHandle) {
+ SkRuntimeShaderBuilder* builder = reinterpret_cast<SkRuntimeShaderBuilder*>(shaderBuilder);
+ ScopedUtfChars name(env, jUniformName);
+ auto* childEffect = reinterpret_cast<SkFlattenable*>(childHandle);
+
+ UpdateChild(env, builder, name.c_str(), childEffect);
+}
+
///////////////////////////////////////////////////////////////////////////////////////////////
static const JNINativeMethod gShaderMethods[] = {
@@ -428,6 +380,7 @@
{"nativeUpdateUniforms", "(JLjava/lang/String;IIIII)V",
(void*)RuntimeShader_updateIntUniforms},
{"nativeUpdateShader", "(JLjava/lang/String;J)V", (void*)RuntimeShader_updateShader},
+ {"nativeUpdateChild", "(JLjava/lang/String;J)V", (void*)RuntimeShader_updateChild},
};
int register_android_graphics_Shader(JNIEnv* env)
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 8ec0430..b36b8be 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -418,6 +418,11 @@
RenderNode* target) {
mRenderThread.removeFrameCallback(this);
+ // Make sure we have a valid device info
+ if (!DeviceInfo::get()->hasMaxTextureSize()) {
+ (void)mRenderThread.requireGrContext();
+ }
+
// If the previous frame was dropped we don't need to hold onto it, so
// just keep using the previous frame's structure instead
const auto reason = wasSkipped(mCurrentFrameInfo);
diff --git a/libs/hwui/tests/unit/RenderPropertiesTests.cpp b/libs/hwui/tests/unit/RenderPropertiesTests.cpp
index 3e8e057..6ec042c 100644
--- a/libs/hwui/tests/unit/RenderPropertiesTests.cpp
+++ b/libs/hwui/tests/unit/RenderPropertiesTests.cpp
@@ -40,7 +40,11 @@
props.setLeftTopRightBottom(0, 0, maxTextureSize + 1, maxTextureSize + 1);
ASSERT_FALSE(props.fitsOnLayer());
- // Too small, but still 'fits'. Not fitting is an error case, so don't report empty as such.
+ // Too small, we can't create a layer for a 0 width or height
props.setLeftTopRightBottom(0, 0, 100, 0);
- ASSERT_TRUE(props.fitsOnLayer());
+ ASSERT_FALSE(props.fitsOnLayer());
+
+ // Can't create a negative-sized layer
+ props.setLeftTopRightBottom(0, 0, -100, 300);
+ ASSERT_FALSE(props.fitsOnLayer());
}
diff --git a/media/java/android/media/AudioDeviceInfo.java b/media/java/android/media/AudioDeviceInfo.java
index 2da8eec..b41f40f 100644
--- a/media/java/android/media/AudioDeviceInfo.java
+++ b/media/java/android/media/AudioDeviceInfo.java
@@ -17,6 +17,7 @@
package android.media;
import static android.media.audio.Flags.FLAG_ENABLE_MULTICHANNEL_GROUP_DEVICE;
+import static android.media.audio.Flags.FLAG_SPEAKER_LAYOUT_API;
import android.Manifest;
import android.annotation.FlaggedApi;
@@ -548,6 +549,19 @@
}
/**
+ * @return A ChannelMask representing the physical output speaker layout of the device.
+ *
+ * The layout channel mask only indicates which speaker channels are present, the
+ * physical layout of the speakers should be informed by a standard for multi-channel
+ * sound playback systems, such as ITU-R BS.2051.
+ * @see AudioFormat
+ */
+ @FlaggedApi(FLAG_SPEAKER_LAYOUT_API)
+ public int getSpeakerLayoutChannelMask() {
+ return mPort.speakerLayoutChannelMask();
+ }
+
+ /**
* @return An array of audio encodings (e.g. {@link AudioFormat#ENCODING_PCM_16BIT},
* {@link AudioFormat#ENCODING_PCM_FLOAT}) supported by the audio device.
* <code>ENCODING_PCM_FLOAT</code> indicates the device supports more
diff --git a/media/java/android/media/AudioDevicePort.java b/media/java/android/media/AudioDevicePort.java
index ab5c54b..4b3962e 100644
--- a/media/java/android/media/AudioDevicePort.java
+++ b/media/java/android/media/AudioDevicePort.java
@@ -22,6 +22,7 @@
import com.android.aconfig.annotations.VisibleForTesting;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -60,8 +61,27 @@
/* encapsulationMetadataTypes= */ null);
}
+ /** @hide */
+ // TODO: b/316864909 - Remove this method once there's a way to fake audio device ports further
+ // down the stack.
+ @VisibleForTesting
+ public static AudioDevicePort createForTesting(int speakerLayoutChannelMask) {
+ return new AudioDevicePort(
+ new AudioHandle(/* id= */ 0),
+ /* name= */ "testAudioDevicePort",
+ /* profiles= */ new ArrayList<>(),
+ /* gains= */ null,
+ /* type= */ AudioManager.DEVICE_OUT_SPEAKER,
+ /* address= */ "testAddress",
+ /* speakerLayoutChannelMask= */ speakerLayoutChannelMask,
+ /* encapsulationModes= */ null,
+ /* encapsulationMetadataTypes= */ null,
+ /* descriptors= */ new ArrayList<>());
+ }
+
private final int mType;
private final String mAddress;
+ private final int mSpeakerLayoutChannelMask;
private final int[] mEncapsulationModes;
private final int[] mEncapsulationMetadataTypes;
@@ -76,12 +96,20 @@
deviceName, samplingRates, channelMasks, channelIndexMasks, formats, gains);
mType = type;
mAddress = address;
+ mSpeakerLayoutChannelMask = AudioFormat.CHANNEL_INVALID;
mEncapsulationModes = encapsulationModes;
mEncapsulationMetadataTypes = encapsulationMetadataTypes;
}
- AudioDevicePort(AudioHandle handle, String deviceName, List<AudioProfile> profiles,
- AudioGain[] gains, int type, String address, int[] encapsulationModes,
+ AudioDevicePort(
+ AudioHandle handle,
+ String deviceName,
+ List<AudioProfile> profiles,
+ AudioGain[] gains,
+ int type,
+ String address,
+ int speakerLayoutChannelMask,
+ int[] encapsulationModes,
@AudioTrack.EncapsulationMetadataType int[] encapsulationMetadataTypes,
List<AudioDescriptor> descriptors) {
super(handle,
@@ -89,6 +117,7 @@
deviceName, profiles, gains, descriptors);
mType = type;
mAddress = address;
+ mSpeakerLayoutChannelMask = speakerLayoutChannelMask;
mEncapsulationModes = encapsulationModes;
mEncapsulationMetadataTypes = encapsulationMetadataTypes;
}
@@ -119,6 +148,16 @@
return mAddress;
}
+ /** Get the channel mask representing the physical output speaker layout of the device.
+ *
+ * The layout channel mask only indicates which speaker channels are present, the
+ * physical layout of the speakers should be informed by a standard for multi-channel
+ * sound playback systems, such as ITU-R BS.2051.
+ */
+ public int speakerLayoutChannelMask() {
+ return mSpeakerLayoutChannelMask;
+ }
+
/**
* Get supported encapsulation modes.
*/
diff --git a/media/java/android/media/AudioPlaybackConfiguration.java b/media/java/android/media/AudioPlaybackConfiguration.java
index 3cd5f52..c085b89 100644
--- a/media/java/android/media/AudioPlaybackConfiguration.java
+++ b/media/java/android/media/AudioPlaybackConfiguration.java
@@ -19,6 +19,7 @@
import static android.media.AudioAttributes.ALLOW_CAPTURE_BY_ALL;
import static android.media.AudioAttributes.ALLOW_CAPTURE_BY_NONE;
import static android.media.audio.Flags.FLAG_MUTED_BY_PORT_VOLUME_API;
+import static android.media.audio.Flags.FLAG_ROUTED_DEVICE_IDS;
import android.annotation.FlaggedApi;
import android.annotation.IntDef;
@@ -39,6 +40,9 @@
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
import java.util.Objects;
/**
@@ -56,6 +60,8 @@
public static final int PLAYER_UPID_INVALID = -1;
/** @hide */
public static final int PLAYER_DEVICEID_INVALID = 0;
+ /** @hide */
+ public static final int[] PLAYER_DEVICEIDS_INVALID = new int[0];
// information about the implementation
/**
@@ -332,7 +338,7 @@
private final Object mUpdateablePropLock = new Object();
@GuardedBy("mUpdateablePropLock")
- private int mDeviceId;
+ private @NonNull int[] mDeviceIds = AudioPlaybackConfiguration.PLAYER_DEVICEIDS_INVALID;
@GuardedBy("mUpdateablePropLock")
private int mSessionId;
@GuardedBy("mUpdateablePropLock")
@@ -361,7 +367,7 @@
mClientUid = uid;
mClientPid = pid;
mMutedState = 0;
- mDeviceId = PLAYER_DEVICEID_INVALID;
+ mDeviceIds = AudioPlaybackConfiguration.PLAYER_DEVICEIDS_INVALID;
mPlayerState = PLAYER_STATE_IDLE;
mPlayerAttr = pic.mAttributes;
if ((sPlayerDeathMonitor != null) && (pic.mIPlayer != null)) {
@@ -385,10 +391,11 @@
}
// sets the fields that are updateable and require synchronization
- private void setUpdateableFields(int deviceId, int sessionId, int mutedState, FormatInfo format)
+ private void setUpdateableFields(int[] deviceIds, int sessionId, int mutedState,
+ FormatInfo format)
{
synchronized (mUpdateablePropLock) {
- mDeviceId = deviceId;
+ mDeviceIds = deviceIds;
mSessionId = sessionId;
mMutedState = mutedState;
mFormatInfo = format;
@@ -424,7 +431,7 @@
anonymCopy.mClientPid = PLAYER_UPID_INVALID;
anonymCopy.mIPlayerShell = null;
anonymCopy.setUpdateableFields(
- /*deviceId*/ PLAYER_DEVICEID_INVALID,
+ /*deviceIds*/ new int[0],
/*sessionId*/ AudioSystem.AUDIO_SESSION_ALLOCATE,
/*mutedState*/ 0,
FormatInfo.DEFAULT);
@@ -461,17 +468,46 @@
/**
* Returns information about the {@link AudioDeviceInfo} used for this playback.
- * @return the audio playback device or null if the device is not available at the time of query
+ * @return the audio playback device or null if the device is not available at the time of
+ * query.
+ * @deprecated this information was never populated
*/
+ @Deprecated
+ @FlaggedApi(FLAG_ROUTED_DEVICE_IDS)
public @Nullable AudioDeviceInfo getAudioDeviceInfo() {
- final int deviceId;
+ final int[] deviceIds;
synchronized (mUpdateablePropLock) {
- deviceId = mDeviceId;
+ deviceIds = mDeviceIds;
}
- if (deviceId == PLAYER_DEVICEID_INVALID) {
+ if (deviceIds.length == 0) {
return null;
}
- return AudioManager.getDeviceForPortId(deviceId, AudioManager.GET_DEVICES_OUTPUTS);
+ return AudioManager.getDeviceForPortId(deviceIds[0], AudioManager.GET_DEVICES_OUTPUTS);
+ }
+
+ /**
+ * @hide
+ * Returns information about the List of {@link AudioDeviceInfo} used for this playback.
+ * @return the audio playback devices
+ */
+ @SystemApi
+ @FlaggedApi(FLAG_ROUTED_DEVICE_IDS)
+ @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ public @NonNull List<AudioDeviceInfo> getAudioDeviceInfos() {
+ List<AudioDeviceInfo> audioDeviceInfos = new ArrayList<AudioDeviceInfo>();
+ final int[] deviceIds;
+ synchronized (mUpdateablePropLock) {
+ deviceIds = mDeviceIds;
+ }
+
+ for (int i = 0; i < deviceIds.length; i++) {
+ AudioDeviceInfo audioDeviceInfo = AudioManager.getDeviceForPortId(deviceIds[i],
+ AudioManager.GET_DEVICES_OUTPUTS);
+ if (audioDeviceInfo != null) {
+ audioDeviceInfos.add(audioDeviceInfo);
+ }
+ }
+ return audioDeviceInfos;
}
/**
@@ -677,15 +713,15 @@
* @hide
* Handle a player state change
* @param event
- * @param deviceId active device id or {@Code PLAYER_DEVICEID_INVALID}
- * <br>Note device id is valid for {@code PLAYER_UPDATE_DEVICE_ID} or
- * <br>{@code PLAYER_STATE_STARTED} events, as the device id will be reset to none when
- * <br>pausing or stopping playback. It will be set to active device when playback starts or
+ * @param deviceIds an array of device ids. This can be empty.
+ * <br>Note device ids are non-empty for {@code PLAYER_UPDATE_DEVICE_ID} or
+ * <br>{@code PLAYER_STATE_STARTED} events, as the device ids will be emptied when pausing
+ * <br>or stopping playback. It will be set to active devices when playback starts or
* <br>it will be changed when PLAYER_UPDATE_DEVICE_ID is sent. The latter can happen if the
- * <br>device changes in the middle of playback.
+ * <br>devices change in the middle of playback.
* @return true if the state changed, false otherwise
*/
- public boolean handleStateEvent(int event, int deviceId) {
+ public boolean handleStateEvent(int event, int[] deviceIds) {
boolean changed = false;
synchronized (mUpdateablePropLock) {
@@ -696,8 +732,8 @@
}
if (event == PLAYER_STATE_STARTED || event == PLAYER_UPDATE_DEVICE_ID) {
- changed = changed || (mDeviceId != deviceId);
- mDeviceId = deviceId;
+ changed = changed || !Arrays.equals(mDeviceIds, deviceIds);
+ mDeviceIds = deviceIds;
}
if (changed && (event == PLAYER_STATE_RELEASED) && (mIPlayerShell != null)) {
@@ -777,8 +813,8 @@
@Override
public int hashCode() {
synchronized (mUpdateablePropLock) {
- return Objects.hash(mPlayerIId, mDeviceId, mMutedState, mPlayerType, mClientUid,
- mClientPid, mSessionId);
+ return Objects.hash(mPlayerIId, Arrays.toString(mDeviceIds), mMutedState, mPlayerType,
+ mClientUid, mClientPid, mSessionId);
}
}
@@ -791,7 +827,7 @@
public void writeToParcel(Parcel dest, int flags) {
synchronized (mUpdateablePropLock) {
dest.writeInt(mPlayerIId);
- dest.writeInt(mDeviceId);
+ dest.writeIntArray(mDeviceIds);
dest.writeInt(mMutedState);
dest.writeInt(mPlayerType);
dest.writeInt(mClientUid);
@@ -810,7 +846,10 @@
private AudioPlaybackConfiguration(Parcel in) {
mPlayerIId = in.readInt();
- mDeviceId = in.readInt();
+ mDeviceIds = new int[in.readInt()];
+ for (int i = 0; i < mDeviceIds.length; i++) {
+ mDeviceIds[i] = in.readInt();
+ }
mMutedState = in.readInt();
mPlayerType = in.readInt();
mClientUid = in.readInt();
@@ -831,7 +870,7 @@
AudioPlaybackConfiguration that = (AudioPlaybackConfiguration) o;
return ((mPlayerIId == that.mPlayerIId)
- && (mDeviceId == that.mDeviceId)
+ && Arrays.equals(mDeviceIds, that.mDeviceIds)
&& (mMutedState == that.mMutedState)
&& (mPlayerType == that.mPlayerType)
&& (mClientUid == that.mClientUid)
@@ -844,7 +883,7 @@
StringBuilder apcToString = new StringBuilder();
synchronized (mUpdateablePropLock) {
apcToString.append("AudioPlaybackConfiguration piid:").append(mPlayerIId).append(
- " deviceId:").append(mDeviceId).append(" type:").append(
+ " deviceIds:").append(Arrays.toString(mDeviceIds)).append(" type:").append(
toLogFriendlyPlayerType(mPlayerType)).append(" u/pid:").append(
mClientUid).append(
"/").append(mClientPid).append(" state:").append(
diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java
index 80e5719..cacd59f 100644
--- a/media/java/android/media/AudioRecord.java
+++ b/media/java/android/media/AudioRecord.java
@@ -20,8 +20,10 @@
import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_AUDIO;
import static android.content.Context.DEVICE_ID_DEFAULT;
import static android.media.AudioManager.AUDIO_SESSION_ID_GENERATE;
+import static android.media.audio.Flags.FLAG_ROUTED_DEVICE_IDS;
import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
import android.annotation.FloatRange;
import android.annotation.IntDef;
import android.annotation.IntRange;
@@ -1906,17 +1908,49 @@
}
/**
+ * Internal API of getRoutedDevices(). We should not call flag APIs internally.
+ */
+ private @NonNull List<AudioDeviceInfo> getRoutedDevicesInternal() {
+ List<AudioDeviceInfo> audioDeviceInfos = new ArrayList<AudioDeviceInfo>();
+ final int[] deviceIds = native_getRoutedDeviceIds();
+ if (deviceIds == null || deviceIds.length == 0) {
+ return audioDeviceInfos;
+ }
+
+ for (int i = 0; i < deviceIds.length; i++) {
+ AudioDeviceInfo audioDeviceInfo = AudioManager.getDeviceForPortId(deviceIds[i],
+ AudioManager.GET_DEVICES_INPUTS);
+ if (audioDeviceInfo != null) {
+ audioDeviceInfos.add(audioDeviceInfo);
+ }
+ }
+ return audioDeviceInfos;
+ }
+
+ /**
* Returns an {@link AudioDeviceInfo} identifying the current routing of this AudioRecord.
* Note: The query is only valid if the AudioRecord is currently recording. If it is not,
* <code>getRoutedDevice()</code> will return null.
*/
@Override
public AudioDeviceInfo getRoutedDevice() {
- int deviceId = native_getRoutedDeviceId();
- if (deviceId == 0) {
+ final List<AudioDeviceInfo> audioDeviceInfos = getRoutedDevicesInternal();
+ if (audioDeviceInfos.isEmpty()) {
return null;
}
- return AudioManager.getDeviceForPortId(deviceId, AudioManager.GET_DEVICES_INPUTS);
+ return audioDeviceInfos.get(0);
+ }
+
+ /**
+ * Returns a List of {@link AudioDeviceInfo} identifying the current routing of this
+ * AudioRecord.
+ * Note: The query is only valid if the AudioRecord is currently playing. If it is not,
+ * <code>getRoutedDevices()</code> will return an empty list.
+ */
+ @Override
+ @FlaggedApi(FLAG_ROUTED_DEVICE_IDS)
+ public @NonNull List<AudioDeviceInfo> getRoutedDevices() {
+ return getRoutedDevicesInternal();
}
/**
@@ -2494,7 +2528,7 @@
int sampleRateInHz, int channelCount, int audioFormat);
private native final boolean native_setInputDevice(int deviceId);
- private native final int native_getRoutedDeviceId();
+ private native int[] native_getRoutedDeviceIds();
private native final void native_enableDeviceCallback();
private native final void native_disableDeviceCallback();
diff --git a/media/java/android/media/AudioRouting.java b/media/java/android/media/AudioRouting.java
index 26fa631..22aa9a0 100644
--- a/media/java/android/media/AudioRouting.java
+++ b/media/java/android/media/AudioRouting.java
@@ -16,9 +16,16 @@
package android.media;
+import static android.media.audio.Flags.FLAG_ROUTED_DEVICE_IDS;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
import android.os.Handler;
import android.os.Looper;
+import java.util.ArrayList;
+import java.util.List;
+
/**
* AudioRouting defines an interface for controlling routing and routing notifications in
* AudioTrack and AudioRecord objects.
@@ -49,6 +56,22 @@
public AudioDeviceInfo getRoutedDevice();
/**
+ * Returns a List of {@link AudioDeviceInfo} identifying the current routing of this
+ * AudioTrack/AudioRecord.
+ * Note: The query is only valid if the AudioTrack/AudioRecord is currently playing.
+ * If it is not, <code>getRoutedDevices()</code> will return an empty List.
+ */
+ @FlaggedApi(FLAG_ROUTED_DEVICE_IDS)
+ default @NonNull List<AudioDeviceInfo> getRoutedDevices() {
+ List<AudioDeviceInfo> audioDeviceInfos = new ArrayList<AudioDeviceInfo>();
+ AudioDeviceInfo audioDeviceInfo = getRoutedDevice();
+ if (audioDeviceInfo != null) {
+ audioDeviceInfos.add(audioDeviceInfo);
+ }
+ return new ArrayList<AudioDeviceInfo>();
+ }
+
+ /**
* Adds an {@link AudioRouting.OnRoutingChangedListener} to receive notifications of routing
* changes on this AudioTrack/AudioRecord.
* @param listener The {@link AudioRouting.OnRoutingChangedListener} interface to receive
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index 03cd535..a5d9adb 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -17,8 +17,10 @@
package android.media;
import static android.media.AudioManager.AUDIO_SESSION_ID_GENERATE;
+import static android.media.audio.Flags.FLAG_ROUTED_DEVICE_IDS;
import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
import android.annotation.FloatRange;
import android.annotation.IntDef;
import android.annotation.IntRange;
@@ -54,7 +56,9 @@
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.NioUtils;
+import java.util.ArrayList;
import java.util.LinkedList;
+import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.Executor;
@@ -3019,7 +3023,7 @@
}
}
synchronized(mPlayStateLock) {
- baseStart(0); // unknown device at this point
+ baseStart(new int[0]); // unknown device at this point
native_start();
// FIXME see b/179218630
//baseStart(native_getRoutedDeviceId());
@@ -3780,17 +3784,51 @@
}
/**
+ * Internal API of getRoutedDevices(). We should not call flag APIs internally.
+ */
+ private @NonNull List<AudioDeviceInfo> getRoutedDevicesInternal() {
+ List<AudioDeviceInfo> audioDeviceInfos = new ArrayList<AudioDeviceInfo>();
+ final int[] deviceIds = native_getRoutedDeviceIds();
+ if (deviceIds == null || deviceIds.length == 0) {
+ return audioDeviceInfos;
+ }
+
+ for (int i = 0; i < deviceIds.length; i++) {
+ AudioDeviceInfo audioDeviceInfo = AudioManager.getDeviceForPortId(deviceIds[i],
+ AudioManager.GET_DEVICES_OUTPUTS);
+ if (audioDeviceInfo != null) {
+ audioDeviceInfos.add(audioDeviceInfo);
+ }
+ }
+ return audioDeviceInfos;
+ }
+
+ /**
* Returns an {@link AudioDeviceInfo} identifying the current routing of this AudioTrack.
* Note: The query is only valid if the AudioTrack is currently playing. If it is not,
* <code>getRoutedDevice()</code> will return null.
+ * Audio may play on multiple devices simultaneously (e.g. an alarm playing on headphones and
+ * speaker on a phone), so prefer using {@link #getRoutedDevices}.
*/
@Override
public AudioDeviceInfo getRoutedDevice() {
- int deviceId = native_getRoutedDeviceId();
- if (deviceId == 0) {
+ final List<AudioDeviceInfo> audioDeviceInfos = getRoutedDevicesInternal();
+ if (audioDeviceInfos.isEmpty()) {
return null;
}
- return AudioManager.getDeviceForPortId(deviceId, AudioManager.GET_DEVICES_OUTPUTS);
+ return audioDeviceInfos.get(0);
+ }
+
+ /**
+ * Returns a List of {@link AudioDeviceInfo} identifying the current routing of this
+ * AudioTrack.
+ * Note: The query is only valid if the AudioTrack is currently playing. If it is not,
+ * <code>getRoutedDevices()</code> will return an empty list.
+ */
+ @Override
+ @FlaggedApi(FLAG_ROUTED_DEVICE_IDS)
+ public @NonNull List<AudioDeviceInfo> getRoutedDevices() {
+ return getRoutedDevicesInternal();
}
private void tryToDisableNativeRoutingCallback() {
@@ -3950,7 +3988,7 @@
*/
private void broadcastRoutingChange() {
AudioManager.resetAudioPortGeneration();
- baseUpdateDeviceId(getRoutedDevice());
+ baseUpdateDeviceIds(getRoutedDevicesInternal());
synchronized (mRoutingChangeListeners) {
for (NativeRoutingEventHandlerDelegate delegate : mRoutingChangeListeners.values()) {
delegate.notifyClient();
@@ -4507,7 +4545,7 @@
private native final int native_setAuxEffectSendLevel(float level);
private native final boolean native_setOutputDevice(int deviceId);
- private native final int native_getRoutedDeviceId();
+ private native int[] native_getRoutedDeviceIds();
private native final void native_enableDeviceCallback();
private native final void native_disableDeviceCallback();
diff --git a/media/java/android/media/HwAudioSource.java b/media/java/android/media/HwAudioSource.java
index 167ab65..68a3aa7 100644
--- a/media/java/android/media/HwAudioSource.java
+++ b/media/java/android/media/HwAudioSource.java
@@ -145,7 +145,14 @@
mAudioAttributes);
if (isPlaying()) {
// FIXME: b/174876389 clean up device id reporting
- baseStart(getDeviceId());
+ // Set as deviceIds empty and create an array with element if device id is valid.
+ int[] deviceIds = AudioPlaybackConfiguration.PLAYER_DEVICEIDS_INVALID;
+ int deviceId = getDeviceId();
+ if (deviceId != 0) {
+ deviceIds = new int[1];
+ deviceIds[0] = deviceId;
+ }
+ baseStart(deviceIds);
}
}
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index 9fd3f5b..08b0dd3 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -87,7 +87,7 @@
oneway void playerAttributes(in int piid, in AudioAttributes attr);
- oneway void playerEvent(in int piid, in int event, in int eventId);
+ oneway void playerEvent(in int piid, in int event, in int[] eventId);
oneway void releasePlayer(in int piid);
diff --git a/media/java/android/media/ImageWriter.java b/media/java/android/media/ImageWriter.java
index 9325999..0a79f41 100644
--- a/media/java/android/media/ImageWriter.java
+++ b/media/java/android/media/ImageWriter.java
@@ -1157,7 +1157,11 @@
@Override
public void setFence(@NonNull SyncFence fence) throws IOException {
throwISEIfImageIsInvalid();
- nativeSetFenceFd(fence.getFdDup().detachFd());
+ if (fence.isValid()) {
+ nativeSetFenceFd(fence.getFdDup().detachFd());
+ } else {
+ nativeSetFenceFd(-1);
+ }
}
@Override
diff --git a/media/java/android/media/MediaCas.java b/media/java/android/media/MediaCas.java
index 3f9126a..1ecba31 100644
--- a/media/java/android/media/MediaCas.java
+++ b/media/java/android/media/MediaCas.java
@@ -16,10 +16,9 @@
package android.media;
+import static android.media.tv.flags.Flags.FLAG_MEDIACAS_UPDATE_CLIENT_PROFILE_PRIORITY;
import static android.media.tv.flags.Flags.FLAG_SET_RESOURCE_HOLDER_RETAIN;
-import static com.android.media.flags.Flags.FLAG_UPDATE_CLIENT_PROFILE_PRIORITY;
-
import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -996,7 +995,7 @@
* @param niceValue the nice value.
* @hide
*/
- @FlaggedApi(FLAG_UPDATE_CLIENT_PROFILE_PRIORITY)
+ @FlaggedApi(FLAG_MEDIACAS_UPDATE_CLIENT_PROFILE_PRIORITY)
@SystemApi
@RequiresPermission(android.Manifest.permission.TUNER_RESOURCE_ACCESS)
public boolean updateResourcePriority(int priority, int niceValue) {
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index 96edd63..782db35 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -1876,6 +1876,8 @@
* Codecs with this security model is not included in
* {@link MediaCodecList#REGULAR_CODECS}, but included in
* {@link MediaCodecList#ALL_CODECS}.
+ *
+ * @hide
*/
@FlaggedApi(FLAG_IN_PROCESS_SW_AUDIO_CODEC)
public static final int SECURITY_MODEL_TRUSTED_CONTENT_ONLY = 2;
diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java
index bd65b2e..bc09aee 100644
--- a/media/java/android/media/MediaFormat.java
+++ b/media/java/android/media/MediaFormat.java
@@ -1748,6 +1748,7 @@
(1 << MediaCodecInfo.SECURITY_MODEL_MEMORY_SAFE);
/**
* Flag for {@link MediaCodecInfo#SECURITY_MODEL_TRUSTED_CONTENT_ONLY}.
+ * @hide
*/
@FlaggedApi(FLAG_IN_PROCESS_SW_AUDIO_CODEC)
public static final int FLAG_SECURITY_MODEL_TRUSTED_CONTENT_ONLY =
@@ -1759,8 +1760,7 @@
* The associated value is a flag of the following values:
* {@link FLAG_SECURITY_MODEL_SANDBOXED},
* {@link FLAG_SECURITY_MODEL_MEMORY_SAFE},
- * {@link FLAG_SECURITY_MODEL_TRUSTED_CONTENT_ONLY}. The default value is
- * {@link FLAG_SECURITY_MODEL_SANDBOXED}.
+ * The default value is {@link FLAG_SECURITY_MODEL_SANDBOXED}.
* <p>
* When passed to {@link MediaCodecList#findDecoderForFormat} or
* {@link MediaCodecList#findEncoderForFormat}, MediaCodecList filters
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index a0f8ae5..f1c2a7a 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -18,7 +18,9 @@
import static android.Manifest.permission.BIND_IMS_SERVICE;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.media.audio.Flags.FLAG_ROUTED_DEVICE_IDS;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -84,6 +86,7 @@
import java.net.InetSocketAddress;
import java.net.URL;
import java.nio.ByteOrder;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.BitSet;
import java.util.HashMap;
@@ -1412,7 +1415,7 @@
}
private void startImpl() {
- baseStart(0); // unknown device at this point
+ baseStart(new int[0]); // unknown device at this point
stayAwake(true);
tryToEnableNativeRoutingCallback();
_start();
@@ -1538,20 +1541,54 @@
}
/**
+ * Internal API of getRoutedDevices(). We should not call flag APIs internally.
+ */
+ private @NonNull List<AudioDeviceInfo> getRoutedDevicesInternal() {
+ List<AudioDeviceInfo> audioDeviceInfos = new ArrayList<AudioDeviceInfo>();
+ final int[] deviceIds = native_getRoutedDeviceIds();
+ if (deviceIds == null || deviceIds.length == 0) {
+ return audioDeviceInfos;
+ }
+
+ for (int i = 0; i < deviceIds.length; i++) {
+ AudioDeviceInfo audioDeviceInfo = AudioManager.getDeviceForPortId(deviceIds[i],
+ AudioManager.GET_DEVICES_OUTPUTS);
+ if (audioDeviceInfo != null) {
+ audioDeviceInfos.add(audioDeviceInfo);
+ }
+ }
+ return audioDeviceInfos;
+ }
+
+ /**
* Returns an {@link AudioDeviceInfo} identifying the current routing of this MediaPlayer
* Note: The query is only valid if the MediaPlayer is currently playing.
* If the player is not playing, the returned device can be null or correspond to previously
* selected device when the player was last active.
+ * Audio may play on multiple devices simultaneously (e.g. an alarm playing on headphones and
+ * speaker on a phone), so prefer using {@link #getRoutedDevices}.
*/
@Override
public AudioDeviceInfo getRoutedDevice() {
- int deviceId = native_getRoutedDeviceId();
- if (deviceId == 0) {
+ final List<AudioDeviceInfo> audioDeviceInfos = getRoutedDevicesInternal();
+ if (audioDeviceInfos.isEmpty()) {
return null;
}
- return AudioManager.getDeviceForPortId(deviceId, AudioManager.GET_DEVICES_OUTPUTS);
+ return audioDeviceInfos.get(0);
}
+ /**
+ * Returns a List of {@link AudioDeviceInfo} identifying the current routing of this
+ * MediaPlayer.
+ * Note: The query is only valid if the MediaPlayer is currently playing.
+ * If the player is not playing, the returned devices can be empty or correspond to previously
+ * selected devices when the player was last active.
+ */
+ @Override
+ @FlaggedApi(FLAG_ROUTED_DEVICE_IDS)
+ public @NonNull List<AudioDeviceInfo> getRoutedDevices() {
+ return getRoutedDevicesInternal();
+ }
/**
* Sends device list change notification to all listeners.
@@ -1562,7 +1599,7 @@
// Prevent the case where an event is triggered by registering a routing change
// listener via the media player.
if (mEnableSelfRoutingMonitor) {
- baseUpdateDeviceId(getRoutedDevice());
+ baseUpdateDeviceIds(getRoutedDevicesInternal());
}
for (NativeRoutingEventHandlerDelegate delegate
: mRoutingChangeListeners.values()) {
@@ -1672,7 +1709,7 @@
}
private native final boolean native_setOutputDevice(int deviceId);
- private native final int native_getRoutedDeviceId();
+ private native int[] native_getRoutedDeviceIds();
private native final void native_enableDeviceCallback(boolean enabled);
/**
diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java
index 2d17bf5..7af78b8 100644
--- a/media/java/android/media/MediaRecorder.java
+++ b/media/java/android/media/MediaRecorder.java
@@ -16,7 +16,10 @@
package android.media;
+import static android.media.audio.Flags.FLAG_ROUTED_DEVICE_IDS;
+
import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
import android.annotation.FloatRange;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -1681,6 +1684,26 @@
}
/**
+ * Internal API of getRoutedDevices(). We should not call flag APIs internally.
+ */
+ private @NonNull List<AudioDeviceInfo> getRoutedDevicesInternal() {
+ List<AudioDeviceInfo> audioDeviceInfos = new ArrayList<AudioDeviceInfo>();
+ final int[] deviceIds = native_getRoutedDeviceIds();
+ if (deviceIds == null || deviceIds.length == 0) {
+ return audioDeviceInfos;
+ }
+
+ for (int i = 0; i < deviceIds.length; i++) {
+ AudioDeviceInfo audioDeviceInfo = AudioManager.getDeviceForPortId(deviceIds[i],
+ AudioManager.GET_DEVICES_INPUTS);
+ if (audioDeviceInfo != null) {
+ audioDeviceInfos.add(audioDeviceInfo);
+ }
+ }
+ return audioDeviceInfos;
+ }
+
+ /**
* Returns an {@link AudioDeviceInfo} identifying the current routing of this MediaRecorder
* Note: The query is only valid if the MediaRecorder is currently recording.
* If the recorder is not recording, the returned device can be null or correspond to previously
@@ -1688,11 +1711,24 @@
*/
@Override
public AudioDeviceInfo getRoutedDevice() {
- int deviceId = native_getRoutedDeviceId();
- if (deviceId == 0) {
+ final List<AudioDeviceInfo> audioDeviceInfos = getRoutedDevicesInternal();
+ if (audioDeviceInfos.isEmpty()) {
return null;
}
- return AudioManager.getDeviceForPortId(deviceId, AudioManager.GET_DEVICES_INPUTS);
+ return audioDeviceInfos.get(0);
+ }
+
+ /**
+ * Returns a List of {@link AudioDeviceInfo} identifying the current routing of this
+ * MediaRecorder.
+ * Note: The query is only valid if the MediaRecorder is currently recording.
+ * If the recorder is not recording, the returned devices can be empty or correspond to
+ * previously selected devices when the recorder was last active.
+ */
+ @Override
+ @FlaggedApi(FLAG_ROUTED_DEVICE_IDS)
+ public @NonNull List<AudioDeviceInfo> getRoutedDevices() {
+ return getRoutedDevicesInternal();
}
/*
@@ -1752,7 +1788,7 @@
}
private native final boolean native_setInputDevice(int deviceId);
- private native final int native_getRoutedDeviceId();
+ private native int[] native_getRoutedDeviceIds();
private native final void native_enableDeviceCallback(boolean enabled);
//--------------------------------------------------------------------------
diff --git a/media/java/android/media/PlayerBase.java b/media/java/android/media/PlayerBase.java
index 3f44b09..dbf8338ac 100644
--- a/media/java/android/media/PlayerBase.java
+++ b/media/java/android/media/PlayerBase.java
@@ -39,6 +39,8 @@
import com.android.internal.app.IAppOpsService;
import java.lang.ref.WeakReference;
+import java.util.Arrays;
+import java.util.List;
import java.util.Objects;
/**
@@ -91,7 +93,7 @@
@GuardedBy("mLock")
private float mVolMultiplier = 1.0f;
@GuardedBy("mLock")
- private int mDeviceId;
+ private @NonNull int[] mDeviceIds = AudioPlaybackConfiguration.PLAYER_DEVICEIDS_INVALID;
/**
* Constructor. Must be given audio attributes, as they are required for AppOps.
@@ -158,35 +160,36 @@
}
}
- void baseUpdateDeviceId(@Nullable AudioDeviceInfo deviceInfo) {
- int deviceId = 0;
- if (deviceInfo != null) {
- deviceId = deviceInfo.getId();
+ void baseUpdateDeviceIds(@NonNull List<AudioDeviceInfo> deviceInfos) {
+ int[] deviceIds = new int[deviceInfos.size()];
+ for (int i = 0; i < deviceInfos.size(); i++) {
+ deviceIds[i] = deviceInfos.get(i).getId();
}
+
int piid;
synchronized (mLock) {
piid = mPlayerIId;
- mDeviceId = deviceId;
+ mDeviceIds = deviceIds;
}
try {
getService().playerEvent(piid,
- AudioPlaybackConfiguration.PLAYER_UPDATE_DEVICE_ID, deviceId);
+ AudioPlaybackConfiguration.PLAYER_UPDATE_DEVICE_ID, deviceIds);
} catch (RemoteException e) {
Log.e(TAG, "Error talking to audio service, "
- + deviceId
+ + Arrays.toString(deviceIds)
+ " device id will not be tracked for piid=" + piid, e);
}
}
- private void updateState(int state, int deviceId) {
+ private void updateState(int state, @NonNull int[] deviceIds) {
final int piid;
synchronized (mLock) {
mState = state;
piid = mPlayerIId;
- mDeviceId = deviceId;
+ mDeviceIds = deviceIds;
}
try {
- getService().playerEvent(piid, state, deviceId);
+ getService().playerEvent(piid, state, deviceIds);
} catch (RemoteException e) {
Log.e(TAG, "Error talking to audio service, "
+ AudioPlaybackConfiguration.toLogFriendlyPlayerState(state)
@@ -194,11 +197,12 @@
}
}
- void baseStart(int deviceId) {
+ void baseStart(@NonNull int[] deviceIds) {
if (DEBUG) {
- Log.v(TAG, "baseStart() piid=" + mPlayerIId + " deviceId=" + deviceId);
+ Log.v(TAG, "baseStart() piid=" + mPlayerIId + " deviceId="
+ + Arrays.toString(deviceIds));
}
- updateState(AudioPlaybackConfiguration.PLAYER_STATE_STARTED, deviceId);
+ updateState(AudioPlaybackConfiguration.PLAYER_STATE_STARTED, deviceIds);
}
void baseSetStartDelayMs(int delayMs) {
@@ -215,12 +219,12 @@
void basePause() {
if (DEBUG) { Log.v(TAG, "basePause() piid=" + mPlayerIId); }
- updateState(AudioPlaybackConfiguration.PLAYER_STATE_PAUSED, 0);
+ updateState(AudioPlaybackConfiguration.PLAYER_STATE_PAUSED, new int[0]);
}
void baseStop() {
if (DEBUG) { Log.v(TAG, "baseStop() piid=" + mPlayerIId); }
- updateState(AudioPlaybackConfiguration.PLAYER_STATE_STOPPED, 0);
+ updateState(AudioPlaybackConfiguration.PLAYER_STATE_STOPPED, new int[0]);
}
void baseSetPan(float pan) {
diff --git a/media/java/android/media/SoundPool.java b/media/java/android/media/SoundPool.java
index 7b9ff23..f22cc9c 100644
--- a/media/java/android/media/SoundPool.java
+++ b/media/java/android/media/SoundPool.java
@@ -317,7 +317,7 @@
// FIXME: b/174876164 implement device id for soundpool
try {
Trace.traceBegin(Trace.TRACE_TAG_AUDIO, "SoundPool.play");
- baseStart(0);
+ baseStart(new int[0]);
return _play(soundID, leftVolume, rightVolume, priority, loop, rate, getPlayerIId());
} finally {
Trace.traceEnd(Trace.TRACE_TAG_AUDIO);
diff --git a/media/java/android/media/flags/media_better_together.aconfig b/media/java/android/media/flags/media_better_together.aconfig
index 7895eb2..d8a8c8b 100644
--- a/media/java/android/media/flags/media_better_together.aconfig
+++ b/media/java/android/media/flags/media_better_together.aconfig
@@ -1,13 +1,7 @@
package: "com.android.media.flags"
container: "system"
-flag {
- name: "enable_rlp_callbacks_in_media_router2"
- is_exported: true
- namespace: "media_solutions"
- description: "Make RouteListingPreference getter and callbacks public in MediaRouter2."
- bug: "281067101"
-}
+# Flags are ordered alphabetically by name.
flag {
name: "adjust_volume_for_foreground_app_playing_audio_without_media_session"
@@ -17,6 +11,13 @@
}
flag {
+ name: "enable_audio_input_device_routing_and_volume_control"
+ namespace: "media_better_together"
+ description: "Allows audio input devices routing and volume control via system settings."
+ bug: "355684672"
+}
+
+flag {
name: "enable_audio_policies_device_and_bluetooth_controller"
is_exported: true
namespace: "media_solutions"
@@ -25,17 +26,54 @@
}
flag {
- name: "fallback_to_default_handling_when_media_session_has_fixed_volume_handling"
- namespace: "media_solutions"
- 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"
+ name: "enable_built_in_speaker_route_suitability_statuses"
+ is_exported: true
+ namespace: "media_solutions"
+ description: "Make MediaRoute2Info provide information about routes suitability for transfer."
+ bug: "279555229"
}
flag {
- name: "enable_waiting_state_for_system_session_creation_request"
+ name: "enable_cross_user_routing_in_media_router2"
+ is_exported: true
namespace: "media_solutions"
- description: "Introduces a waiting state for the session creation request and prevents it from early failing when the selectedRoute from the bluetooth stack doesn't match the pending request route id."
- bug: "307723189"
+ description: "Allows clients of privileged MediaRouter2 that hold INTERACT_ACROSS_USERS_FULL to control routing across users."
+ bug: "288580225"
+}
+
+flag {
+ name: "enable_full_scan_with_media_content_control"
+ namespace: "media_better_together"
+ description: "Allows holders of the MEDIA_CONTENT_CONTROL permission to scan for routes while not in the foreground."
+ bug: "352401364"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
+ name: "enable_get_transferable_routes"
+ is_exported: true
+ namespace: "media_solutions"
+ description: "Exposes RoutingController#getTransferableRoutes() (previously hidden) to the public API."
+ bug: "323154573"
+}
+
+flag {
+ name: "enable_mirroring_in_media_router_2"
+ namespace: "media_better_together"
+ description: "Enables support for mirroring routes in the MediaRouter2 framework, allowing Output Switcher to offer mirroring routes."
+ bug: "362507305"
+}
+
+flag {
+ name: "enable_mr2_service_non_main_bg_thread"
+ namespace: "media_solutions"
+ description: "Enables the use of a background thread in the media routing framework, instead of using the main thread."
+ bug: "310145678"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
}
flag {
@@ -55,44 +93,6 @@
}
flag {
- name: "enable_privileged_routing_for_media_routing_control"
- is_exported: true
- namespace: "media_solutions"
- description: "Allow access to privileged routing capabilities to MEDIA_ROUTING_CONTROL holders."
- bug: "305919655"
-}
-
-flag {
- name: "enable_cross_user_routing_in_media_router2"
- is_exported: true
- namespace: "media_solutions"
- description: "Allows clients of privileged MediaRouter2 that hold INTERACT_ACROSS_USERS_FULL to control routing across users."
- bug: "288580225"
-}
-
-flag {
- name: "enable_use_of_bluetooth_device_get_alias_for_mr2info_get_name"
- namespace: "media_solutions"
- description: "Use BluetoothDevice.getAlias to populate the name of Bluetooth MediaRoute2Infos."
- bug: "314324170"
-}
-
-flag {
- name: "update_client_profile_priority"
- namespace: "media_solutions"
- description : "Feature flag to add updateResourcePriority api to MediaCas"
- bug: "300565729"
-}
-
-flag {
- name: "enable_built_in_speaker_route_suitability_statuses"
- is_exported: true
- namespace: "media_solutions"
- description: "Make MediaRoute2Info provide information about routes suitability for transfer."
- bug: "279555229"
-}
-
-flag {
name: "enable_notifying_activity_manager_with_media_session_status_change"
is_exported: true
namespace: "media_solutions"
@@ -101,11 +101,10 @@
}
flag {
- name: "enable_get_transferable_routes"
- is_exported: true
+ name: "enable_null_session_in_media_browser_service"
namespace: "media_solutions"
- description: "Exposes RoutingController#getTransferableRoutes() (previously hidden) to the public API."
- bug: "323154573"
+ description: "Enables apps owning a MediaBrowserService to disconnect all connected browsers."
+ bug: "185136506"
}
flag {
@@ -116,31 +115,6 @@
}
flag {
- name: "enable_mr2_service_non_main_bg_thread"
- namespace: "media_solutions"
- description: "Enables the use of a background thread in the media routing framework, instead of using the main thread."
- bug: "310145678"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
- name: "enable_screen_off_scanning"
- is_exported: true
- namespace: "media_solutions"
- description: "Enable new MediaRouter2 API to enable watch companion apps to scan while the phone screen is off."
- bug: "281072508"
-}
-
-flag {
- name: "enable_null_session_in_media_browser_service"
- namespace: "media_solutions"
- description: "Enables apps owning a MediaBrowserService to disconnect all connected browsers."
- bug: "185136506"
-}
-
-flag {
name: "enable_prevention_of_manager_scans_when_no_apps_scan"
namespace: "media_solutions"
description: "Prevents waking up route providers when no apps are scanning, even if SysUI or Settings are scanning."
@@ -151,25 +125,46 @@
}
flag {
- name: "enable_full_scan_with_media_content_control"
- namespace: "media_better_together"
- description: "Allows holders of the MEDIA_CONTENT_CONTROL permission to scan for routes while not in the foreground."
- bug: "352401364"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
+ name: "enable_privileged_routing_for_media_routing_control"
+ is_exported: true
+ namespace: "media_solutions"
+ description: "Allow access to privileged routing capabilities to MEDIA_ROUTING_CONTROL holders."
+ bug: "305919655"
}
flag {
- name: "enable_audio_input_device_routing_and_volume_control"
- namespace: "media_better_together"
- description: "Allows audio input devices routing and volume control via system settings."
- bug: "355684672"
+ name: "enable_rlp_callbacks_in_media_router2"
+ is_exported: true
+ namespace: "media_solutions"
+ description: "Make RouteListingPreference getter and callbacks public in MediaRouter2."
+ bug: "281067101"
}
flag {
- name: "enable_mirroring_in_media_router_2"
- namespace: "media_better_together"
- description: "Enables support for mirroring routes in the MediaRouter2 framework, allowing Output Switcher to offer mirroring routes."
- bug: "362507305"
+ name: "enable_screen_off_scanning"
+ is_exported: true
+ namespace: "media_solutions"
+ description: "Enable new MediaRouter2 API to enable watch companion apps to scan while the phone screen is off."
+ bug: "281072508"
+}
+
+flag {
+ name: "enable_use_of_bluetooth_device_get_alias_for_mr2info_get_name"
+ namespace: "media_solutions"
+ description: "Use BluetoothDevice.getAlias to populate the name of Bluetooth MediaRoute2Infos."
+ bug: "314324170"
+}
+
+flag {
+ name: "enable_waiting_state_for_system_session_creation_request"
+ namespace: "media_solutions"
+ description: "Introduces a waiting state for the session creation request and prevents it from early failing when the selectedRoute from the bluetooth stack doesn't match the pending request route id."
+ bug: "307723189"
+}
+
+flag {
+ name: "fallback_to_default_handling_when_media_session_has_fixed_volume_handling"
+ namespace: "media_solutions"
+ 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/flags/projection.aconfig b/media/java/android/media/flags/projection.aconfig
index 17d1ff6..1bb9a8e 100644
--- a/media/java/android/media/flags/projection.aconfig
+++ b/media/java/android/media/flags/projection.aconfig
@@ -18,3 +18,10 @@
bug: "362720120"
is_exported: true
}
+
+flag {
+ namespace: "media_projection"
+ name: "stop_media_projection_on_call_end"
+ description: "Stops MediaProjection sessions when a call ends"
+ bug: "368336349"
+}
\ No newline at end of file
diff --git a/media/java/android/media/quality/AmbientBacklightEvent.java b/media/java/android/media/quality/AmbientBacklightEvent.java
index 5c11def..273f21e 100644
--- a/media/java/android/media/quality/AmbientBacklightEvent.java
+++ b/media/java/android/media/quality/AmbientBacklightEvent.java
@@ -40,7 +40,7 @@
@IntDef({AMBIENT_BACKLIGHT_EVENT_ENABLED, AMBIENT_BACKLIGHT_EVENT_DISABLED,
AMBIENT_BACKLIGHT_EVENT_METADATA,
AMBIENT_BACKLIGHT_EVENT_INTERRUPTED})
- public @interface AmbientBacklightEventTypes {}
+ public @interface Type {}
/**
* Event type for ambient backlight events. The ambient backlight is enabled.
@@ -69,9 +69,9 @@
private final AmbientBacklightMetadata mMetadata;
/**
- * Constructor of AmbientBacklightEvent.
+ * Constructs AmbientBacklightEvent.
*/
- public AmbientBacklightEvent(int eventType,
+ public AmbientBacklightEvent(@Type int eventType,
@Nullable AmbientBacklightMetadata metadata) {
mEventType = eventType;
mMetadata = metadata;
@@ -85,6 +85,7 @@
/**
* Gets event type.
*/
+ @Type
public int getEventType() {
return mEventType;
}
diff --git a/media/java/android/media/quality/AmbientBacklightMetadata.java b/media/java/android/media/quality/AmbientBacklightMetadata.java
index 9c11f9a..5cea10d 100644
--- a/media/java/android/media/quality/AmbientBacklightMetadata.java
+++ b/media/java/android/media/quality/AmbientBacklightMetadata.java
@@ -29,6 +29,9 @@
/**
* Metadata of ambient backlight.
+ *
+ * <p>A metadata instance is sent from ambient backlight hardware in a {@link AmbientBacklightEvent}
+ * with {@link AmbientBacklightEvent#AMBIENT_BACKLIGHT_EVENT_METADATA}.
* @hide
*/
@FlaggedApi(Flags.FLAG_MEDIA_QUALITY_FW)
@@ -44,10 +47,15 @@
private final int[] mZonesColors;
/**
- * Constructor of AmbientBacklightMetadata.
+ * Constructs AmbientBacklightMetadata.
*/
- public AmbientBacklightMetadata(@NonNull String packageName, int compressAlgorithm,
- int source, int colorFormat, int horizontalZonesNumber, int verticalZonesNumber,
+ public AmbientBacklightMetadata(
+ @NonNull String packageName,
+ @AmbientBacklightSettings.CompressAlgorithm int compressAlgorithm,
+ @AmbientBacklightSettings.Source int source,
+ @PixelFormat.Format int colorFormat,
+ int horizontalZonesNumber,
+ int verticalZonesNumber,
@NonNull int[] zonesColors) {
mPackageName = packageName;
mCompressAlgorithm = compressAlgorithm;
@@ -69,7 +77,7 @@
}
/**
- * Gets package name.
+ * Gets package name of the metadata.
* @hide
*/
@NonNull
@@ -102,7 +110,9 @@
}
/**
- * Gets the number of lights in each horizontal zone.
+ * Gets the number of horizontal color zones.
+ *
+ * <p>A color zone is a group of lights that always display the same color.
*/
@IntRange(from = 0)
public int getHorizontalZonesNumber() {
@@ -110,7 +120,9 @@
}
/**
- * Gets the number of lights in each vertical zone.
+ * Gets the number of vertical color zones.
+ *
+ * <p>A color zone is a group of lights that always display the same color.
*/
@IntRange(from = 0)
public int getVerticalZonesNumber() {
@@ -118,10 +130,11 @@
}
/**
+ * Gets color data of vertical color zones.
* @hide
*/
@NonNull
- public int[] getZonesColors() {
+ public int[] getVerticalZonesColors() {
return mZonesColors;
}
diff --git a/media/java/android/media/quality/AmbientBacklightSettings.java b/media/java/android/media/quality/AmbientBacklightSettings.java
index 4ed7bc7..d904cf7 100644
--- a/media/java/android/media/quality/AmbientBacklightSettings.java
+++ b/media/java/android/media/quality/AmbientBacklightSettings.java
@@ -30,7 +30,7 @@
import java.lang.annotation.RetentionPolicy;
/**
- * Settings for ambient backlight.
+ * Settings to configure ambient backlight hardware.
* @hide
*/
@FlaggedApi(Flags.FLAG_MEDIA_QUALITY_FW)
@@ -124,8 +124,13 @@
/**
* Constructs AmbientBacklightSettings.
*/
- public AmbientBacklightSettings(int source, int maxFps, int colorFormat,
- int horizontalZonesNumber, int verticalZonesNumber, boolean isLetterboxOmitted,
+ public AmbientBacklightSettings(
+ @Source int source,
+ int maxFps,
+ @PixelFormat.Format int colorFormat,
+ int horizontalZonesNumber,
+ int verticalZonesNumber,
+ boolean isLetterboxOmitted,
int threshold) {
mSource = source;
mMaxFps = maxFps;
@@ -171,7 +176,9 @@
}
/**
- * Gets the number of lights in each horizontal zone.
+ * Gets the number of horizontal color zones.
+ *
+ * <p>A color zone is a group of lights that always display the same color.
*/
@IntRange(from = 0)
public int getHorizontalZonesNumber() {
@@ -179,7 +186,9 @@
}
/**
- * Gets the number of lights in each vertical zone.
+ * Gets the number of vertical color zones.
+ *
+ * <p>A color zone is a group of lights that always display the same color.
*/
@IntRange(from = 0)
public int getVerticalZonesNumber() {
@@ -187,7 +196,11 @@
}
/**
- * Returns {@code true} if letter box is omitted; {@code false} otherwise.
+ * Returns {@code true} if the black portion of the screen in letter box mode is omitted;
+ * {@code false} otherwise.
+ *
+ * <p>Letter-box is a technique to keep the original aspect ratio when displayed on a screen
+ * with different aspect ratio. Black bars are added to the top and bottom.
* @hide
*/
public boolean isLetterboxOmitted() {
@@ -195,6 +208,10 @@
}
/**
+ * Gets the detection threshold of the ambient light.
+ *
+ * <p>If the color of a color zone is changed by the difference is smaller than the threshold,
+ * the change is ignored.
* @hide
*/
public int getThreshold() {
diff --git a/media/java/android/media/quality/IMediaQualityManager.aidl b/media/java/android/media/quality/IMediaQualityManager.aidl
index 250d59b..1c85c7b 100644
--- a/media/java/android/media/quality/IMediaQualityManager.aidl
+++ b/media/java/android/media/quality/IMediaQualityManager.aidl
@@ -21,6 +21,7 @@
import android.media.quality.IPictureProfileCallback;
import android.media.quality.ISoundProfileCallback;
import android.media.quality.ParamCapability;
+import android.media.quality.PictureProfileHandle;
import android.media.quality.PictureProfile;
import android.media.quality.SoundProfile;
@@ -38,14 +39,17 @@
List<String> getPictureProfilePackageNames();
List<String> getPictureProfileAllowList();
void setPictureProfileAllowList(in List<String> packages);
+ PictureProfileHandle getPictureProfileHandle(in String id);
SoundProfile createSoundProfile(in SoundProfile pp);
void updateSoundProfile(in String id, in SoundProfile pp);
void removeSoundProfile(in String id);
- SoundProfile getSoundProfileById(in String id);
+ SoundProfile getSoundProfile(in int type, in String name);
List<SoundProfile> getSoundProfilesByPackage(in String packageName);
List<SoundProfile> getAvailableSoundProfiles();
List<String> getSoundProfilePackageNames();
+ List<String> getSoundProfileAllowList();
+ void setSoundProfileAllowList(in List<String> packages);
void registerPictureProfileCallback(in IPictureProfileCallback cb);
void registerSoundProfileCallback(in ISoundProfileCallback cb);
diff --git a/media/java/android/media/quality/ISoundProfileCallback.aidl b/media/java/android/media/quality/ISoundProfileCallback.aidl
index 72d1524..9043757 100644
--- a/media/java/android/media/quality/ISoundProfileCallback.aidl
+++ b/media/java/android/media/quality/ISoundProfileCallback.aidl
@@ -17,6 +17,7 @@
package android.media.quality;
+import android.media.quality.ParamCapability;
import android.media.quality.SoundProfile;
/**
@@ -24,7 +25,9 @@
* @hide
*/
oneway interface ISoundProfileCallback {
- void onSoundProfileAdded(in long id, in SoundProfile p);
- void onSoundProfileUpdated(in long id, in SoundProfile p);
- void onSoundProfileRemoved(in long id, in SoundProfile p);
+ void onSoundProfileAdded(in String id, in SoundProfile p);
+ void onSoundProfileUpdated(in String id, in SoundProfile p);
+ void onSoundProfileRemoved(in String id, in SoundProfile p);
+ void onParamCapabilitiesChanged(in String id, in List<ParamCapability> caps);
+ void onError(in int err);
}
diff --git a/media/java/android/media/quality/MediaQualityContract.java b/media/java/android/media/quality/MediaQualityContract.java
index f07ef87..5fec86a 100644
--- a/media/java/android/media/quality/MediaQualityContract.java
+++ b/media/java/android/media/quality/MediaQualityContract.java
@@ -23,7 +23,6 @@
/**
* The contract between the media quality service and applications. Contains definitions for the
* commonly used parameter names.
- * @hide
*/
@FlaggedApi(Flags.FLAG_MEDIA_QUALITY_FW)
public class MediaQualityContract {
@@ -42,9 +41,8 @@
/**
* Parameters picture quality.
- * @hide
*/
- public static final class PictureQuality implements BaseParameters {
+ public static final class PictureQuality {
/**
* The brightness.
*
diff --git a/media/java/android/media/quality/MediaQualityManager.java b/media/java/android/media/quality/MediaQualityManager.java
index 4d4526c..43e884a 100644
--- a/media/java/android/media/quality/MediaQualityManager.java
+++ b/media/java/android/media/quality/MediaQualityManager.java
@@ -20,6 +20,7 @@
import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.content.Context;
import android.media.tv.flags.Flags;
@@ -37,7 +38,6 @@
/**
* Central system API to the overall media quality, which arbitrates interaction between
* applications and media quality service.
- * @hide
*/
@FlaggedApi(Flags.FLAG_MEDIA_QUALITY_FW)
@SystemService(Context.MEDIA_QUALITY_SERVICE)
@@ -111,7 +111,7 @@
};
ISoundProfileCallback spCallback = new ISoundProfileCallback.Stub() {
@Override
- public void onSoundProfileAdded(long profileId, SoundProfile profile) {
+ public void onSoundProfileAdded(String profileId, SoundProfile profile) {
synchronized (mLock) {
for (SoundProfileCallbackRecord record : mSpCallbackRecords) {
// TODO: filter callback record
@@ -120,7 +120,7 @@
}
}
@Override
- public void onSoundProfileUpdated(long profileId, SoundProfile profile) {
+ public void onSoundProfileUpdated(String profileId, SoundProfile profile) {
synchronized (mLock) {
for (SoundProfileCallbackRecord record : mSpCallbackRecords) {
// TODO: filter callback record
@@ -129,7 +129,7 @@
}
}
@Override
- public void onSoundProfileRemoved(long profileId, SoundProfile profile) {
+ public void onSoundProfileRemoved(String profileId, SoundProfile profile) {
synchronized (mLock) {
for (SoundProfileCallbackRecord record : mSpCallbackRecords) {
// TODO: filter callback record
@@ -137,6 +137,24 @@
}
}
}
+ @Override
+ public void onParamCapabilitiesChanged(String profileId, List<ParamCapability> caps) {
+ synchronized (mLock) {
+ for (SoundProfileCallbackRecord record : mSpCallbackRecords) {
+ // TODO: filter callback record
+ record.postParamCapabilitiesChanged(profileId, caps);
+ }
+ }
+ }
+ @Override
+ public void onError(int err) {
+ synchronized (mLock) {
+ for (SoundProfileCallbackRecord record : mSpCallbackRecords) {
+ // TODO: filter callback record
+ record.postError(err);
+ }
+ }
+ }
};
IAmbientBacklightCallback abCallback = new IAmbientBacklightCallback.Stub() {
@Override
@@ -162,7 +180,6 @@
/**
* Registers a {@link PictureProfileCallback}.
- * @hide
*/
public void registerPictureProfileCallback(
@NonNull @CallbackExecutor Executor executor,
@@ -176,7 +193,6 @@
/**
* Unregisters the existing {@link PictureProfileCallback}.
- * @hide
*/
public void unregisterPictureProfileCallback(@NonNull final PictureProfileCallback callback) {
Preconditions.checkNotNull(callback);
@@ -198,7 +214,6 @@
*
* @return the corresponding picture profile if available; {@code null} if the name doesn't
* exist.
- * @hide
*/
@Nullable
public PictureProfile getPictureProfile(
@@ -214,8 +229,9 @@
/**
* Gets profiles that available to the given package.
*
- * @hide @SystemApi
+ * @hide
*/
+ @SystemApi
@NonNull
@RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_PICTURE_QUALITY_SERVICE)
public List<PictureProfile> getPictureProfilesByPackage(@NonNull String packageName) {
@@ -242,8 +258,9 @@
* Gets all package names whose picture profiles are available.
*
* @see #getPictureProfilesByPackage(String)
- * @hide @SystemApi
+ * @hide
*/
+ @SystemApi
@NonNull
@RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_PICTURE_QUALITY_SERVICE)
public List<String> getPictureProfilePackageNames() {
@@ -254,18 +271,27 @@
}
}
+ /**
+ * Gets picture profile handle by profile ID.
+ * @hide
+ */
+ public PictureProfileHandle getPictureProfileHandle(String id) {
+ try {
+ return mService.getPictureProfileHandle(id);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
/**
* Creates a picture profile and store it in the system.
*
- * @return the stored profile with an assigned profile ID. {@code null} if it's not created
- * successfully.
- * @hide
+ * <p>If the profile is created successfully,
+ * {@link PictureProfileCallback#onPictureProfileAdded(String, PictureProfile)} is invoked.
*/
- @Nullable
- public PictureProfile createPictureProfile(@NonNull PictureProfile pp) {
+ public void createPictureProfile(@NonNull PictureProfile pp) {
try {
- return mService.createPictureProfile(pp);
+ mService.createPictureProfile(pp);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -274,7 +300,6 @@
/**
* Updates an existing picture profile and store it in the system.
- * @hide
*/
public void updatePictureProfile(@NonNull String profileId, @NonNull PictureProfile pp) {
try {
@@ -287,7 +312,6 @@
/**
* Removes a picture profile from the system.
- * @hide
*/
public void removePictureProfile(@NonNull String profileId) {
try {
@@ -331,14 +355,17 @@
/**
- * Gets sound profile by given profile ID.
- * @return the corresponding sound profile if available; {@code null} if the ID doesn't
- * exist or the profile is not accessible to the caller.
+ * Gets sound profile by given profile type and name.
+ *
+ * @return the corresponding sound profile if available; {@code null} if the name doesn't
+ * exist.
* @hide
*/
- public SoundProfile getSoundProfileById(String profileId) {
+ @Nullable
+ public SoundProfile getSoundProfile(
+ @SoundProfile.ProfileType int type, @NonNull String name) {
try {
- return mService.getSoundProfileById(profileId);
+ return mService.getSoundProfile(type, name);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -349,8 +376,9 @@
* @SystemApi gets profiles that available to the given package
* @hide
*/
+ @NonNull
@RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_SOUND_QUALITY_SERVICE)
- public List<SoundProfile> getSoundProfilesByPackage(String packageName) {
+ public List<SoundProfile> getSoundProfilesByPackage(@NonNull String packageName) {
try {
return mService.getSoundProfilesByPackage(packageName);
} catch (RemoteException e) {
@@ -362,6 +390,7 @@
* Gets profiles that available to the caller package
* @hide
*/
+ @NonNull
public List<SoundProfile> getAvailableSoundProfiles() {
try {
return mService.getAvailableSoundProfiles();
@@ -371,9 +400,12 @@
}
/**
- * @SystemApi all stored sound profiles of all packages
+ * @SystemApi Gets all package names whose sound profiles are available.
+ *
+ * @see #getSoundProfilesByPackage(String)
* @hide
*/
+ @NonNull
@RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_SOUND_QUALITY_SERVICE)
public List<String> getSoundProfilePackageNames() {
try {
@@ -387,12 +419,13 @@
/**
* Creates a sound profile and store it in the system.
*
- * @return the stored profile with an assigned profile ID.
+ * <p>If the profile is created successfully,
+ * {@link SoundProfileCallback#onSoundProfileAdded(long, SoundProfile)} is invoked.
* @hide
*/
- public SoundProfile createSoundProfile(SoundProfile sp) {
+ public void createSoundProfile(@NonNull SoundProfile sp) {
try {
- return mService.createSoundProfile(sp);
+ mService.createSoundProfile(sp);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -403,7 +436,7 @@
* Updates an existing sound profile and store it in the system.
* @hide
*/
- public void updateSoundProfile(String profileId, SoundProfile sp) {
+ public void updateSoundProfile(@NonNull String profileId, @NonNull SoundProfile sp) {
try {
mService.updateSoundProfile(profileId, sp);
} catch (RemoteException e) {
@@ -416,7 +449,7 @@
* Removes a sound profile from the system.
* @hide
*/
- public void removeSoundProfile(String profileId) {
+ public void removeSoundProfile(@NonNull String profileId) {
try {
mService.removeSoundProfile(profileId);
} catch (RemoteException e) {
@@ -426,7 +459,6 @@
/**
* Gets capability information of the given parameters.
- * @hide
*/
@NonNull
public List<ParamCapability> getParamCapabilities(@NonNull List<String> names) {
@@ -444,6 +476,7 @@
* @see #removePictureProfile(String)
* @hide
*/
+ @SystemApi
@RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_PICTURE_QUALITY_SERVICE)
@NonNull
public List<String> getPictureProfileAllowList() {
@@ -458,6 +491,7 @@
* Sets the allowlist of packages that can create and removed picture profiles
* @hide
*/
+ @SystemApi
@RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_PICTURE_QUALITY_SERVICE)
public void setPictureProfileAllowList(@NonNull List<String> packageNames) {
try {
@@ -468,6 +502,36 @@
}
/**
+ * Gets the allowlist of packages that can create and removed sound profiles
+ *
+ * @see #createSoundProfile(SoundProfile)
+ * @see #removeSoundProfile(String)
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_SOUND_QUALITY_SERVICE)
+ @NonNull
+ public List<String> getSoundProfileAllowList() {
+ try {
+ return mService.getSoundProfileAllowList();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Sets the allowlist of packages that can create and removed sound profiles
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_SOUND_QUALITY_SERVICE)
+ public void setSoundProfileAllowList(@NonNull List<String> packageNames) {
+ try {
+ mService.setSoundProfileAllowList(packageNames);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Returns {@code true} if media quality HAL is implemented; {@code false} otherwise.
* @hide
*/
@@ -487,6 +551,7 @@
* @param enabled {@code true} to enable, {@code false} to disable.
* @hide
*/
+ @SystemApi
@RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_PICTURE_QUALITY_SERVICE)
public void setAutoPictureQualityEnabled(boolean enabled) {
try {
@@ -498,7 +563,6 @@
/**
* Returns {@code true} if auto picture quality is enabled; {@code false} otherwise.
- * @hide
*/
public boolean isAutoPictureQualityEnabled() {
try {
@@ -515,6 +579,7 @@
* @param enabled {@code true} to enable, {@code false} to disable.
* @hide
*/
+ @SystemApi
@RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_PICTURE_QUALITY_SERVICE)
public void setSuperResolutionEnabled(boolean enabled) {
try {
@@ -526,7 +591,6 @@
/**
* Returns {@code true} if super resolution is enabled; {@code false} otherwise.
- * @hide
*/
public boolean isSuperResolutionEnabled() {
try {
@@ -567,6 +631,7 @@
/**
* Registers a {@link AmbientBacklightCallback}.
+ * @hide
*/
public void registerAmbientBacklightCallback(
@NonNull @CallbackExecutor Executor executor,
@@ -580,6 +645,7 @@
/**
* Unregisters the existing {@link AmbientBacklightCallback}.
+ * @hide
*/
public void unregisterAmbientBacklightCallback(
@NonNull final AmbientBacklightCallback callback) {
@@ -600,6 +666,7 @@
* Set the ambient backlight settings.
*
* @param settings The settings to use for the backlight detector.
+ * @hide
*/
public void setAmbientBacklightSettings(
@NonNull AmbientBacklightSettings settings) {
@@ -615,6 +682,7 @@
* Enables or disables the ambient backlight detection.
*
* @param enabled {@code true} to enable, {@code false} to disable.
+ * @hide
*/
public void setAmbientBacklightEnabled(boolean enabled) {
try {
@@ -698,7 +766,7 @@
return mCallback;
}
- public void postSoundProfileAdded(final long id, SoundProfile profile) {
+ public void postSoundProfileAdded(final String id, SoundProfile profile) {
mExecutor.execute(new Runnable() {
@Override
@@ -708,7 +776,7 @@
});
}
- public void postSoundProfileUpdated(final long id, SoundProfile profile) {
+ public void postSoundProfileUpdated(final String id, SoundProfile profile) {
mExecutor.execute(new Runnable() {
@Override
public void run() {
@@ -717,7 +785,7 @@
});
}
- public void postSoundProfileRemoved(final long id, SoundProfile profile) {
+ public void postSoundProfileRemoved(final String id, SoundProfile profile) {
mExecutor.execute(new Runnable() {
@Override
public void run() {
@@ -725,6 +793,24 @@
}
});
}
+
+ public void postParamCapabilitiesChanged(final String id, List<ParamCapability> caps) {
+ mExecutor.execute(new Runnable() {
+ @Override
+ public void run() {
+ mCallback.onParamCapabilitiesChanged(id, caps);
+ }
+ });
+ }
+
+ public void postError(int error) {
+ mExecutor.execute(new Runnable() {
+ @Override
+ public void run() {
+ mCallback.onError(error);
+ }
+ });
+ }
}
private static final class AmbientBacklightCallbackRecord {
@@ -751,8 +837,7 @@
}
/**
- * Callback used to monitor status of picture profiles.
- * @hide
+ * Callback used to monitor status of picture profiles
*/
public abstract static class PictureProfileCallback {
/**
@@ -760,7 +845,6 @@
*
* @param profileId the ID of the profile.
* @param profile the newly added profile.
- * @hide
*/
public void onPictureProfileAdded(
@NonNull String profileId, @NonNull PictureProfile profile) {
@@ -771,7 +855,6 @@
*
* @param profileId the ID of the profile.
* @param profile the profile with updated info.
- * @hide
*/
public void onPictureProfileUpdated(
@NonNull String profileId, @NonNull PictureProfile profile) {
@@ -782,7 +865,6 @@
*
* @param profileId the ID of the profile.
* @param profile the removed profile.
- * @hide
*/
public void onPictureProfileRemoved(
@NonNull String profileId, @NonNull PictureProfile profile) {
@@ -792,7 +874,6 @@
* This is invoked when an issue has occurred.
*
* @param errorCode the error code
- * @hide
*/
public void onError(@PictureProfile.ErrorCode int errorCode) {
}
@@ -801,12 +882,12 @@
* This is invoked when parameter capabilities has been changed due to status changes of the
* content.
*
- * @param profileId the ID of the profile used by the media content.
+ * @param profileId the ID of the profile used by the media content. {@code null} if there
+ * is no associated profile
* @param updatedCaps the updated capabilities.
- * @hide
*/
public void onParamCapabilitiesChanged(
- @NonNull String profileId, @NonNull List<ParamCapability> updatedCaps) {
+ @Nullable String profileId, @NonNull List<ParamCapability> updatedCaps) {
}
}
@@ -816,29 +897,64 @@
*/
public abstract static class SoundProfileCallback {
/**
+ * This is invoked when a sound profile has been added.
+ *
+ * @param profileId the ID of the profile.
+ * @param profile the newly added profile.
* @hide
*/
- public void onSoundProfileAdded(long id, SoundProfile profile) {
+ public void onSoundProfileAdded(
+ @NonNull String profileId, @NonNull SoundProfile profile) {
}
+
/**
+ * This is invoked when a sound profile has been updated.
+ *
+ * @param profileId the ID of the profile.
+ * @param profile the profile with updated info.
* @hide
*/
- public void onSoundProfileUpdated(long id, SoundProfile profile) {
+ public void onSoundProfileUpdated(
+ @NonNull String profileId, @NonNull SoundProfile profile) {
}
+
/**
+ * This is invoked when a sound profile has been removed.
+ *
+ * @param profileId the ID of the profile.
+ * @param profile the removed profile.
* @hide
*/
- public void onSoundProfileRemoved(long id, SoundProfile profile) {
+ public void onSoundProfileRemoved(
+ @NonNull String profileId, @NonNull SoundProfile profile) {
}
+
/**
+ * This is invoked when an issue has occurred.
+ *
+ * @param errorCode the error code
* @hide
*/
- public void onError(int errorCode) {
+ public void onError(@SoundProfile.ErrorCode int errorCode) {
+ }
+
+ /**
+ * This is invoked when parameter capabilities has been changed due to status changes of the
+ * content.
+ *
+ * @param profileId the ID of the profile used by the media content. {@code null} if there
+ * is no associated profile
+ * @param updatedCaps the updated capabilities.
+ * @hide
+ */
+ public void onParamCapabilitiesChanged(
+ @Nullable String profileId, @NonNull List<ParamCapability> updatedCaps) {
}
}
/**
* Callback used to monitor status of ambient backlight.
+ * @hide
*/
public abstract static class AmbientBacklightCallback {
/**
diff --git a/media/java/android/media/quality/ParamCapability.java b/media/java/android/media/quality/ParamCapability.java
index 0b698a9..ed11abd 100644
--- a/media/java/android/media/quality/ParamCapability.java
+++ b/media/java/android/media/quality/ParamCapability.java
@@ -31,7 +31,6 @@
/**
* Capability info of media quality parameters
- * @hide
*/
@FlaggedApi(Flags.FLAG_MEDIA_QUALITY_FW)
public final class ParamCapability implements Parcelable {
diff --git a/media/java/android/media/quality/PictureProfile.java b/media/java/android/media/quality/PictureProfile.java
index 2be47dd..dcb4222 100644
--- a/media/java/android/media/quality/PictureProfile.java
+++ b/media/java/android/media/quality/PictureProfile.java
@@ -18,11 +18,12 @@
import android.annotation.FlaggedApi;
import android.annotation.IntDef;
+import android.annotation.SystemApi;
import android.media.tv.TvInputInfo;
import android.media.tv.flags.Flags;
-import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
+import android.os.PersistableBundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -33,7 +34,6 @@
/**
* Profile for picture quality.
- * @hide
*/
@FlaggedApi(Flags.FLAG_MEDIA_QUALITY_FW)
public final class PictureProfile implements Parcelable {
@@ -47,7 +47,7 @@
@NonNull
private final String mPackageName;
@NonNull
- private final Bundle mParams;
+ private final PersistableBundle mParams;
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@@ -59,14 +59,14 @@
/**
* System profile type.
*
- * <p>A profile of system type is managed by the system, and readable to the package define in
+ * <p>A profile of system type is managed by the system, and readable to the package returned by
* {@link #getPackageName()}.
*/
public static final int TYPE_SYSTEM = 1;
/**
* Application profile type.
*
- * <p>A profile of application type is managed by the package define in
+ * <p>A profile of application type is managed by the package returned by
* {@link #getPackageName()}.
*/
public static final int TYPE_APPLICATION = 2;
@@ -84,13 +84,11 @@
/**
* Error code for unknown errors.
- * @hide
*/
public static final int ERROR_UNKNOWN = 0;
/**
* Error code for missing necessary permission to handle the profiles.
- * @hide
*/
public static final int ERROR_NO_PERMISSION = 1;
@@ -99,13 +97,11 @@
*
* @see #getProfileType()
* @see #getName()
- * @hide
*/
public static final int ERROR_DUPLICATE = 2;
/**
* Error code for invalid argument.
- * @hide
*/
public static final int ERROR_INVALID_ARGUMENT = 3;
@@ -114,7 +110,6 @@
* list.
*
* @see MediaQualityManager#getPictureProfileAllowList()
- * @hide
*/
public static final int ERROR_NOT_ALLOWLISTED = 4;
@@ -125,7 +120,7 @@
mName = in.readString();
mInputId = in.readString();
mPackageName = in.readString();
- mParams = in.readBundle();
+ mParams = in.readPersistableBundle();
}
@Override
@@ -135,7 +130,7 @@
dest.writeString(mName);
dest.writeString(mInputId);
dest.writeString(mPackageName);
- dest.writeBundle(mParams);
+ dest.writePersistableBundle(mParams);
}
@Override
@@ -168,7 +163,7 @@
@NonNull String name,
@Nullable String inputId,
@NonNull String packageName,
- @NonNull Bundle params) {
+ @NonNull PersistableBundle params) {
this.mId = id;
this.mType = type;
this.mName = name;
@@ -251,13 +246,12 @@
* {@link MediaQualityContract.PictureQuality}.
*/
@NonNull
- public Bundle getParameters() {
- return new Bundle(mParams);
+ public PersistableBundle getParameters() {
+ return new PersistableBundle(mParams);
}
/**
* A builder for {@link PictureProfile}.
- * @hide
*/
public static final class Builder {
@Nullable
@@ -270,7 +264,7 @@
@NonNull
private String mPackageName;
@NonNull
- private Bundle mParams;
+ private PersistableBundle mParams;
/**
* Creates a new Builder.
@@ -291,8 +285,6 @@
mParams = p.getParameters();
}
- /* @hide using by MediaQualityService */
-
/**
* Only used by system to assign the ID.
* @hide
@@ -306,8 +298,9 @@
/**
* Sets profile type.
*
- * @hide @SystemApi
+ * @hide
*/
+ @SystemApi
@RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_PICTURE_QUALITY_SERVICE)
@NonNull
public Builder setProfileType(@ProfileType int value) {
@@ -320,8 +313,9 @@
*
* @see PictureProfile#getInputId()
*
- * @hide @SystemApi
+ * @hide
*/
+ @SystemApi
@RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_PICTURE_QUALITY_SERVICE)
@NonNull
public Builder setInputId(@NonNull String value) {
@@ -334,8 +328,9 @@
*
* @see PictureProfile#getPackageName()
*
- * @hide @SystemApi
+ * @hide
*/
+ @SystemApi
@RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_PICTURE_QUALITY_SERVICE)
@NonNull
public Builder setPackageName(@NonNull String value) {
@@ -349,8 +344,8 @@
* @see PictureProfile#getParameters()
*/
@NonNull
- public Builder setParameters(@NonNull Bundle params) {
- mParams = new Bundle(params);
+ public Builder setParameters(@NonNull PersistableBundle params) {
+ mParams = new PersistableBundle(params);
return this;
}
diff --git a/media/java/android/media/quality/SoundProfile.java b/media/java/android/media/quality/SoundProfile.java
index 20d117b..de93afe 100644
--- a/media/java/android/media/quality/SoundProfile.java
+++ b/media/java/android/media/quality/SoundProfile.java
@@ -17,54 +17,119 @@
package android.media.quality;
import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
+import android.media.tv.TvInputInfo;
import android.media.tv.flags.Flags;
-import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
+import android.os.PersistableBundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.annotation.RequiresPermission;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
/**
+ * Profile for sound quality.
* @hide
*/
@FlaggedApi(Flags.FLAG_MEDIA_QUALITY_FW)
public class SoundProfile implements Parcelable {
@Nullable
- private Long mId;
+ private String mId;
+ private final int mType;
@NonNull
private final String mName;
@Nullable
private final String mInputId;
- @Nullable
+ @NonNull
private final String mPackageName;
@NonNull
- private final Bundle mParams;
+ private final PersistableBundle mParams;
- protected SoundProfile(Parcel in) {
- if (in.readByte() == 0) {
- mId = null;
- } else {
- mId = in.readLong();
- }
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(flag = false, prefix = "TYPE_", value = {
+ TYPE_SYSTEM,
+ TYPE_APPLICATION})
+ public @interface ProfileType {}
+
+ /**
+ * System profile type.
+ *
+ * <p>A profile of system type is managed by the system, and readable to the package returned by
+ * {@link #getPackageName()}.
+ */
+ public static final int TYPE_SYSTEM = 1;
+ /**
+ * Application profile type.
+ *
+ * <p>A profile of application type is managed by the package returned by
+ * {@link #getPackageName()}.
+ */
+ public static final int TYPE_APPLICATION = 2;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(flag = false, prefix = "ERROR_", value = {
+ ERROR_UNKNOWN,
+ ERROR_NO_PERMISSION,
+ ERROR_DUPLICATE,
+ ERROR_INVALID_ARGUMENT,
+ ERROR_NOT_ALLOWLISTED
+ })
+ public @interface ErrorCode {}
+
+ /**
+ * Error code for unknown errors.
+ */
+ public static final int ERROR_UNKNOWN = 0;
+
+ /**
+ * Error code for missing necessary permission to handle the profiles.
+ */
+ public static final int ERROR_NO_PERMISSION = 1;
+
+ /**
+ * Error code for creating a profile with existing profile type and name.
+ *
+ * @see #getProfileType()
+ * @see #getName()
+ */
+ public static final int ERROR_DUPLICATE = 2;
+
+ /**
+ * Error code for invalid argument.
+ */
+ public static final int ERROR_INVALID_ARGUMENT = 3;
+
+ /**
+ * Error code for the case when an operation requires an allowlist but the caller is not in the
+ * list.
+ *
+ * @see MediaQualityManager#getSoundProfileAllowList()
+ */
+ public static final int ERROR_NOT_ALLOWLISTED = 4;
+
+ protected SoundProfile(@NonNull Parcel in) {
+ mId = in.readString();
+ mType = in.readInt();
mName = in.readString();
mInputId = in.readString();
mPackageName = in.readString();
- mParams = in.readBundle();
+ mParams = in.readPersistableBundle();
}
@Override
- public void writeToParcel(Parcel dest, int flags) {
- if (mId == null) {
- dest.writeByte((byte) 0);
- } else {
- dest.writeByte((byte) 1);
- dest.writeLong(mId);
- }
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeString(mId);
+ dest.writeInt(mType);
dest.writeString(mName);
dest.writeString(mInputId);
dest.writeString(mPackageName);
- dest.writeBundle(mParams);
+ dest.writePersistableBundle(mParams);
}
@Override
@@ -72,6 +137,7 @@
return 0;
}
+ @NonNull
public static final Creator<SoundProfile> CREATOR = new Creator<SoundProfile>() {
@Override
public SoundProfile createFromParcel(Parcel in) {
@@ -91,93 +157,164 @@
* @hide
*/
public SoundProfile(
- @Nullable Long id,
+ @Nullable String id,
+ int type,
@NonNull String name,
@Nullable String inputId,
- @Nullable String packageName,
- @NonNull Bundle params) {
+ @NonNull String packageName,
+ @NonNull PersistableBundle params) {
this.mId = id;
+ this.mType = type;
this.mName = name;
- com.android.internal.util.AnnotationValidations.validate(NonNull.class, null, name);
this.mInputId = inputId;
this.mPackageName = packageName;
this.mParams = params;
}
+ /**
+ * Gets profile ID.
+ *
+ * <p>A profile ID is a globally unique ID generated and assigned by the system. For profile
+ * objects retrieved from system (e.g {@link MediaQualityManager#getAvailableSoundProfiles()})
+ * this profile ID is non-null; For profiles built locally with {@link Builder}, it's
+ * {@code null}.
+ *
+ * @return the unique profile ID; {@code null} if the profile is built locally with
+ * {@link Builder}.
+ */
@Nullable
- public Long getProfileId() {
+ public String getProfileId() {
return mId;
}
+ /**
+ * Only used by system to assign the ID.
+ * @hide
+ */
+ public void setProfileId(String id) {
+ mId = id;
+ }
+
+ /**
+ * Gets profile type.
+ */
+ @ProfileType
+ public int getProfileType() {
+ return mType;
+ }
+
+ /**
+ * Gets the profile name.
+ */
@NonNull
public String getName() {
return mName;
}
+ /**
+ * Gets the input ID if the profile is for a TV input.
+ *
+ * @return the corresponding TV input ID; {@code null} if the profile is not associated with a
+ * TV input.
+ *
+ * @see TvInputInfo#getId()
+ */
@Nullable
public String getInputId() {
return mInputId;
}
+ /**
+ * Gets the package name of this profile.
+ *
+ * <p>The package name defines the user of a profile. Only this specific package and system app
+ * can access to this profile.
+ *
+ * @return the package name; {@code null} if the profile is built locally using
+ * {@link Builder} and the package is not set.
+ */
@Nullable
public String getPackageName() {
return mPackageName;
}
+
+ /**
+ * Gets the parameters of this profile.
+ *
+ * <p>The keys of commonly used parameters can be found in
+ * {@link MediaQualityContract.SoundQuality}.
+ */
@NonNull
- public Bundle getParameters() {
- return new Bundle(mParams);
+ public PersistableBundle getParameters() {
+ return new PersistableBundle(mParams);
}
/**
* A builder for {@link SoundProfile}
+ * @hide
*/
public static class Builder {
@Nullable
- private Long mId;
+ private String mId;
+ private int mType = TYPE_APPLICATION;
@NonNull
private String mName;
@Nullable
private String mInputId;
- @Nullable
+ @NonNull
private String mPackageName;
@NonNull
- private Bundle mParams;
+ private PersistableBundle mParams;
/**
* Creates a new Builder.
- *
- * @hide
*/
public Builder(@NonNull String name) {
mName = name;
- com.android.internal.util.AnnotationValidations.validate(NonNull.class, null, name);
}
/**
- * Copy constructor.
- *
- * @hide
+ * Copy constructor of builder.
*/
public Builder(@NonNull SoundProfile p) {
mId = null; // ID needs to be reset
+ mType = p.getProfileType();
mName = p.getName();
mPackageName = p.getPackageName();
mInputId = p.getInputId();
+ mParams = p.getParameters();
}
/**
- * Sets profile ID.
- * @hide using by MediaQualityService
+ * Only used by system to assign the ID.
+ * @hide
*/
@NonNull
- public Builder setProfileId(@Nullable Long id) {
+ public Builder setProfileId(@Nullable String id) {
mId = id;
return this;
}
/**
- * Sets input ID.
+ * Sets profile type.
+ *
+ * @hide
*/
+ @RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_SOUND_QUALITY_SERVICE)
+ @NonNull
+ public Builder setProfileType(@ProfileType int value) {
+ mType = value;
+ return this;
+ }
+
+ /**
+ * Sets input ID.
+ *
+ * @see SoundProfile#getInputId()
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_SOUND_QUALITY_SERVICE)
@NonNull
public Builder setInputId(@NonNull String value) {
mInputId = value;
@@ -186,7 +323,12 @@
/**
* Sets package name of the profile.
+ *
+ * @see SoundProfile#getPackageName()
+ *
+ * @hide
*/
+ @RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_SOUND_QUALITY_SERVICE)
@NonNull
public Builder setPackageName(@NonNull String value) {
mPackageName = value;
@@ -195,12 +337,15 @@
/**
* Sets profile parameters.
+ *
+ * @see SoundProfile#getParameters()
*/
@NonNull
- public Builder setParameters(@NonNull Bundle params) {
- mParams = new Bundle(params);
+ public Builder setParameters(@NonNull PersistableBundle params) {
+ mParams = new PersistableBundle(params);
return this;
}
+
/**
* Builds the instance.
*/
@@ -209,6 +354,7 @@
SoundProfile o = new SoundProfile(
mId,
+ mType,
mName,
mInputId,
mPackageName,
diff --git a/media/java/android/media/tv/TvInputServiceExtensionManager.java b/media/java/android/media/tv/TvInputServiceExtensionManager.java
index 9442726..b876bcf 100644
--- a/media/java/android/media/tv/TvInputServiceExtensionManager.java
+++ b/media/java/android/media/tv/TvInputServiceExtensionManager.java
@@ -194,96 +194,78 @@
public static final String ISCAN_INTERFACE = SCAN_PACKAGE + "IScanInterface";
/**
* Interface that handles scan session and get/store related information.
- * @hide
*/
public static final String ISCAN_SESSION = SCAN_PACKAGE + "IScanSession";
/**
- * Interface that notifies changes related to scan session.
- * @hide
+ * Interface that notifies changes related to a scan session.
*/
public static final String ISCAN_LISTENER = SCAN_PACKAGE + "IScanListener";
/**
* Interface for setting HDPlus information.
- * @hide
*/
public static final String IHDPLUS_INFO = SCAN_PACKAGE + "IHDPlusInfo";
/**
* Interface for handling operator detection for scanning.
- * @hide
*/
public static final String IOPERATOR_DETECTION = SCAN_PACKAGE + "IOperatorDetection";
/**
- * Interface for changes related to operator detection searches.
- * @hide
+ * Interface for notifying changes related to operator detection searches.
*/
public static final String IOPERATOR_DETECTION_LISTENER = SCAN_PACKAGE
+ "IOperatorDetectionListener";
/**
* Interface for handling region channel list for scanning.
- * @hide
*/
public static final String IREGION_CHANNEL_LIST = SCAN_PACKAGE + "IRegionChannelList";
/**
- * Interface for changes related to changes in region channel list search.
- * @hide
+ * Interface for notifying changes related to changes in region channel list search.
*/
public static final String IREGION_CHANNEL_LIST_LISTENER = SCAN_PACKAGE
+ "IRegionChannelListListener";
/**
* Interface for handling target region information.
- * @hide
*/
public static final String ITARGET_REGION = SCAN_PACKAGE + "ITargetRegion";
/**
- * Interface for changes related to target regions during scanning.
- * @hide
+ * Interface for detecting changes related to target regions.
*/
public static final String ITARGET_REGION_LISTENER = SCAN_PACKAGE + "ITargetRegionListener";
/**
- * Interface for handling LCN conflict groups.
- * @hide
+ * Interface for handling logical channel number conflict groups.
*/
public static final String ILCN_CONFLICT = SCAN_PACKAGE + "ILcnConflict";
/**
- * Interface for detecting LCN conflicts during scanning.
- * @hide
+ * Interface for notifying changes in handling logical channel number conflicts.
*/
public static final String ILCN_CONFLICT_LISTENER = SCAN_PACKAGE + "ILcnConflictListener";
/**
- * Interface for handling LCN V2 channel list information.
- * @hide
+ * Interface for handling the updated standard for assigning logical channel numbers.
*/
public static final String ILCNV2_CHANNEL_LIST = SCAN_PACKAGE + "ILcnV2ChannelList";
/**
- * Interface for detecting LCN V2 channel list during scanning.
- * @hide
+ * Interface for notifying changes in assigning logical channel numbers with updated standard.
*/
public static final String ILCNV2_CHANNEL_LIST_LISTENER = SCAN_PACKAGE
+ "ILcnV2ChannelListListener";
/**
* Interface for handling favorite network related information.
- * @hide
*/
public static final String IFAVORITE_NETWORK = SCAN_PACKAGE + "IFavoriteNetwork";
/**
- * Interface for detecting favorite network during scanning.
- * @hide
+ * Interface for notifying changes favorite network during scanning.
*/
public static final String IFAVORITE_NETWORK_LISTENER = SCAN_PACKAGE
+ "IFavoriteNetworkListener";
/**
- * Interface for handling Turksat channel update system service.
- * @hide
+ * Interface for handling Turksat(TKGS) channel update system service.
*/
public static final String ITKGS_INFO = SCAN_PACKAGE + "ITkgsInfo";
/**
- * Interface for changes related to TKGS information.
- * @hide
+ * Interface for notifying changes related to Turksat(TKGS) information.
*/
public static final String ITKGS_INFO_LISTENER = SCAN_PACKAGE + "ITkgsInfoListener";
/**
* Interface for satellite search related to low noise block downconverter.
- * @hide
*/
public static final String ISCAN_SAT_SEARCH = SCAN_PACKAGE + "IScanSatSearch";
/**
@@ -295,113 +277,94 @@
*/
public static final String ICAM_APP_INFO_SERVICE = CAM_PACKAGE + "ICamAppInfoService";
/**
- * Interface for changes on conditional access module app related information.
- * @hide
+ * Interface for notifying changes on conditional access module app related information.
*/
public static final String ICAM_APP_INFO_LISTENER = CAM_PACKAGE + "ICamAppInfoListener";
/**
* Interface for handling conditional access module related information.
- * @hide
*/
public static final String ICAM_MONITORING_SERVICE = CAM_PACKAGE + "ICamMonitoringService";
/**
- * Interface for changes on conditional access module related information.
- * @hide
+ * Interface for notifying changes on conditional access module related information.
*/
public static final String ICAM_INFO_LISTENER = CAM_PACKAGE + "ICamInfoListener";
/**
- * Interface for handling control of CI+ operations.
- * @hide
+ * Interface for handling control of common interface plus operations.
*/
public static final String ICI_OPERATOR_INTERFACE = CAM_PACKAGE + "ICiOperatorInterface";
/**
- * Interfaces for changes on CI+ operations.
- * @hide
+ * Interfaces for notifying changes on common interface plus operations.
*/
public static final String ICI_OPERATOR_LISTENER = CAM_PACKAGE + "ICiOperatorListener";
/**
* Interface for handling conditional access module profile related information.
- * @hide
*/
public static final String ICAM_PROFILE_INTERFACE = CAM_PACKAGE + "ICamProfileInterface";
/**
- * Interface for handling conditional access module DRM related information.
- * @hide
+ * Interface for handling conditional access module digital rights management (DRM)
+ * related information.
*/
public static final String ICONTENT_CONTROL_SERVICE = CAM_PACKAGE + "IContentControlService";
/**
- * Interface for changes on DRM.
- * @hide
+ * Interface for notifying changes on digital rights management (DRM).
*/
public static final String ICAM_DRM_INFO_LISTENER = CAM_PACKAGE + "ICamDrmInfoListener";
/**
* Interface for handling conditional access module pin related information.
- * @hide
*/
public static final String ICAM_PIN_SERVICE = CAM_PACKAGE + "ICamPinService";
/**
- * Interface for changes on conditional access module pin capability.
- * @hide
+ * Interface for notifying changes on conditional access module pin capability.
*/
public static final String ICAM_PIN_CAPABILITY_LISTENER = CAM_PACKAGE
+ "ICamPinCapabilityListener";
/**
- * Interface for changes on conditional access module pin status.
- * @hide
+ * Interface for notifying changes on conditional access module pin status.
*/
public static final String ICAM_PIN_STATUS_LISTENER = CAM_PACKAGE + "ICamPinStatusListener";
/**
* Interface for handling conditional access module host control service.
- * @hide
*/
public static final String ICAM_HOST_CONTROL_SERVICE = CAM_PACKAGE + "ICamHostControlService";
/**
* Interface for handling conditional access module ask release reply.
- * @hide
*/
public static final String ICAM_HOST_CONTROL_ASK_RELEASE_REPLY_CALLBACK = CAM_PACKAGE
+ "ICamHostControlAskReleaseReplyCallback";
/**
- * Interface for changes on conditional access module host control service.
- * @hide
+ * Interface for notifying changes on conditional access module host control service.
*/
public static final String ICAM_HOST_CONTROL_INFO_LISTENER = CAM_PACKAGE
+ "ICamHostControlInfoListener";
/**
* Interface for handling conditional access module host control service tune_quietly_flag.
- * @hide
*/
public static final String ICAM_HOST_CONTROL_TUNE_QUIETLY_FLAG = CAM_PACKAGE
+ "ICamHostControlTuneQuietlyFlag";
/**
- * Interface for changes on conditional access module host control service tune_quietly_flag.
- * @hide
+ * Interface for notifying changes on conditional access module host control service
+ * tune_quietly_flag.
*/
public static final String ICAM_HOST_CONTROL_TUNE_QUIETLY_FLAG_LISTENER = CAM_PACKAGE
+ "ICamHostControlTuneQuietlyFlagListener";
/**
- * Interface for handling conditional access module multi media interface.
- * @hide
+ * Interface for handling conditional access module multi-media interface.
*/
public static final String IMMI_INTERFACE = CAM_PACKAGE + "IMmiInterface";
/**
- * Interface for controlling conditional access module multi media session.
- * @hide
+ * Interface for controlling conditional access module multi-media session.
*/
public static final String IMMI_SESSION = CAM_PACKAGE + "IMmiSession";
/**
- * Interface for changes on conditional access module multi media session status.
- * @hide
+ * Interface for notifying changes on conditional access module multi-media session status.
*/
public static final String IMMI_STATUS_CALLBACK = CAM_PACKAGE + "IMmiStatusCallback";
/**
- * Interface for changes on conditional access app info related to entering menu.
- * @hide
+ * Interface for notifying changes on conditional access app info related to entering menu.
*/
public static final String IENTER_MENU_ERROR_CALLBACK = CAM_PACKAGE + "IEnterMenuErrorCallback";
/**
- * Interface for handling RRT downloadable rating data.
- * @hide
+ * Interface for handling Region Rating Table downloadable rating data.
*/
public static final String IDOWNLOADABLE_RATING_TABLE_MONITOR = RATING_PACKAGE
+ "IDownloadableRatingTableMonitor";
@@ -410,64 +373,54 @@
*/
public static final String IRATING_INTERFACE = RATING_PACKAGE + "IRatingInterface";
/**
- * Interface for handling PMT rating related information.
- * @hide
+ * Interface for handling Program Map Table rating related information.
*/
public static final String IPMT_RATING_INTERFACE = RATING_PACKAGE + "IPmtRatingInterface";
/**
- * Interface for changes on PMT rating related information.
- * @hide
+ * Interface for notifying changes on Program Map Table rating related information.
*/
public static final String IPMT_RATING_LISTENER = RATING_PACKAGE + "IPmtRatingListener";
/**
- * Interface for handling IVBI rating related information.
- * @hide
+ * Interface for handling Vertical Blanking Interval rating related information.
*/
public static final String IVBI_RATING_INTERFACE = RATING_PACKAGE + "IVbiRatingInterface";
/**
- * Interface for changes on IVBI rating related information.
- * @hide
+ * Interface for notifying changes on Vertical Blanking Interval rating related information.
*/
public static final String IVBI_RATING_LISTENER = RATING_PACKAGE + "IVbiRatingListener";
/**
* Interface for handling program rating related information.
- * @hide
*/
public static final String IPROGRAM_INFO = RATING_PACKAGE + "IProgramInfo";
/**
- * Interface for changes on program rating related information.
- * @hide
+ * Interface for notifying changes on program rating related information.
*/
public static final String IPROGRAM_INFO_LISTENER = RATING_PACKAGE + "IProgramInfoListener";
/**
* Interface for getting broadcast time related information.
*/
- public static final String IBROADCAST_TIME = TIME_PACKAGE + "BroadcastTime";
+ public static final String IBROADCAST_TIME = TIME_PACKAGE + "IBroadcastTime";
/**
* Interface for handling data service signal information on teletext.
*/
public static final String IDATA_SERVICE_SIGNAL_INFO = TELETEXT_PACKAGE
+ "IDataServiceSignalInfo";
/**
- * Interface for changes on data service signal information on teletext.
- * @hide
+ * Interface for notifying changes on data service signal information on teletext.
*/
public static final String IDATA_SERVICE_SIGNAL_INFO_LISTENER = TELETEXT_PACKAGE
+ "IDataServiceSignalInfoListener";
/**
* Interface for handling teletext page information.
- * @hide
*/
public static final String ITELETEXT_PAGE_SUB_CODE = TELETEXT_PACKAGE + "ITeletextPageSubCode";
/**
* Interface for handling scan background service update.
- * @hide
*/
public static final String ISCAN_BACKGROUND_SERVICE_UPDATE = SCAN_BSU_PACKAGE
+ "IScanBackgroundServiceUpdate";
/**
- * Interface for changes on background service update
- * @hide
+ * Interface for notifying changes on background service update
*/
public static final String ISCAN_BACKGROUND_SERVICE_UPDATE_LISTENER = SCAN_BSU_PACKAGE
+ "IScanBackgroundServiceUpdateListener";
@@ -484,98 +437,82 @@
*/
public static final String IHDMI_SIGNAL_INTERFACE = SIGNAL_PACKAGE + "IHdmiSignalInterface";
/**
- * Interfaces for changes on HDMI signal information update.
- * @hide
+ * Interfaces for notifying changes on HDMI signal information update.
*/
public static final String IHDMI_SIGNAL_INFO_LISTENER = SIGNAL_PACKAGE
+ "IHdmiSignalInfoListener";
/**
* Interfaces for handling audio signal information update.
- * @hide
*/
public static final String IAUDIO_SIGNAL_INFO = SIGNAL_PACKAGE + "IAudioSignalInfo";
/**
* Interfaces for handling analog audio signal information update.
- * @hide
*/
public static final String IANALOG_AUDIO_INFO = SIGNAL_PACKAGE + "IAnalogAudioInfo";
/**
- * Interfaces for change on audio signal information update.
- * @hide
+ * Interfaces for notifying changes on audio signal information update.
*/
public static final String IAUDIO_SIGNAL_INFO_LISTENER = SIGNAL_PACKAGE
+ "IAudioSignalInfoListener";
/**
* Interfaces for handling video signal information update.
- * @hide
*/
public static final String IVIDEO_SIGNAL_INFO = SIGNAL_PACKAGE + "IVideoSignalInfo";
/**
- * Interfaces for changes on video signal information update.
- * @hide
+ * Interfaces for notifying changes on video signal information update.
*/
public static final String IVIDEO_SIGNAL_INFO_LISTENER = SIGNAL_PACKAGE
+ "IVideoSignalInfoListener";
/**
* Interfaces for handling service database updates.
- * @hide
*/
public static final String ISERVICE_LIST_EDIT = SERVICE_DATABASE_PACKAGE + "IServiceListEdit";
/**
- * Interfaces for changes on service database updates.
+ * Interfaces for notifying changes on service database updates.
*/
public static final String ISERVICE_LIST_EDIT_LISTENER = SERVICE_DATABASE_PACKAGE
+ "IServiceListEditListener";
/**
* Interfaces for getting service database related information.
- * @hide
*/
public static final String ISERVICE_LIST = SERVICE_DATABASE_PACKAGE + "IServiceList";
/**
* Interfaces for transferring service database related information.
- * @hide
*/
public static final String ISERVICE_LIST_TRANSFER_INTERFACE = SERVICE_DATABASE_PACKAGE
+ "IServiceListTransferInterface";
/**
* Interfaces for exporting service database session.
- * @hide
*/
public static final String ISERVICE_LIST_EXPORT_SESSION = SERVICE_DATABASE_PACKAGE
+ "IServiceListExportSession";
/**
- * Interfaces for changes on exporting service database session.
- * @hide
+ * Interfaces for notifying changes on exporting service database session.
*/
public static final String ISERVICE_LIST_EXPORT_LISTENER = SERVICE_DATABASE_PACKAGE
+ "IServiceListExportListener";
/**
* Interfaces for importing service database session.
- * @hide
*/
public static final String ISERVICE_LIST_IMPORT_SESSION = SERVICE_DATABASE_PACKAGE
+ "IServiceListImportSession";
/**
- * Interfaces for changes on importing service database session.
- * @hide
+ * Interfaces for notifying changes on importing service database session.
*/
public static final String ISERVICE_LIST_IMPORT_LISTENER = SERVICE_DATABASE_PACKAGE
+ "IServiceListImportListener";
/**
* Interfaces for setting channel list resources.
- * @hide
*/
public static final String ISERVICE_LIST_SET_CHANNEL_LIST_SESSION = SERVICE_DATABASE_PACKAGE
+ "IServiceListSetChannelListSession";
/**
- * Interfaces for changes on setting channel list resources.
- * @hide
+ * Interfaces for notifying changes on setting channel list resources.
*/
public static final String ISERVICE_LIST_SET_CHANNEL_LIST_LISTENER = SERVICE_DATABASE_PACKAGE
+ "IServiceListSetChannelListListener";
/**
* Interfaces for transferring channel list resources.
- * @hide
*/
public static final String ICHANNEL_LIST_TRANSFER = SERVICE_DATABASE_PACKAGE
+ "IChannelListTransfer";
@@ -584,14 +521,12 @@
*/
public static final String IRECORDED_CONTENTS = PVR_PACKAGE + "IRecordedContents";
/**
- * Interfaces for changes on deleting record contents.
- * @hide
+ * Interfaces for notifying changes on deleting record contents.
*/
public static final String IDELETE_RECORDED_CONTENTS_CALLBACK = PVR_PACKAGE
+ "IDeleteRecordedContentsCallback";
/**
- * Interfaces for changes on getting record contents.
- * @hide
+ * Interfaces for notifying changes on getting record contents.
*/
public static final String IGET_INFO_RECORDED_CONTENTS_CALLBACK = PVR_PACKAGE
+ "IGetInfoRecordedContentsCallback";
@@ -600,61 +535,51 @@
*/
public static final String IEVENT_MONITOR = EVENT_PACKAGE + "IEventMonitor";
/**
- * Interfaces for changes on present event information.
- * @hide
+ * Interfaces for notifying changes on present event information.
*/
public static final String IEVENT_MONITOR_LISTENER = EVENT_PACKAGE + "IEventMonitorListener";
/**
* Interfaces for handling download event information.
- * @hide
*/
public static final String IEVENT_DOWNLOAD = EVENT_PACKAGE + "IEventDownload";
/**
- * Interfaces for changes on downloading event information.
- * @hide
+ * Interfaces for notifying changes on downloading event information.
*/
public static final String IEVENT_DOWNLOAD_LISTENER = EVENT_PACKAGE + "IEventDownloadListener";
/**
- * Interfaces for handling download event information for DVB and DTMB.
- * @hide
+ * Interfaces for handling download event information for Digital Video Broadcast
+ * and Digital Terrestrial Multimedia Broadcast.
*/
public static final String IEVENT_DOWNLOAD_SESSION = EVENT_PACKAGE + "IEventDownloadSession";
/**
* Interfaces for handling analog color system.
- * @hide
*/
public static final String IANALOG_ATTRIBUTE_INTERFACE = ANALOG_PACKAGE
+ "IAnalogAttributeInterface";
/**
* Interfaces for monitoring channel tuned information.
- * @hide
*/
public static final String ICHANNEL_TUNED_INTERFACE = TUNE_PACKAGE + "IChannelTunedInterface";
/**
- * Interfaces for changes on channel tuned information.
- * @hide
+ * Interfaces for notifying changes on channel tuned information.
*/
public static final String ICHANNEL_TUNED_LISTENER = TUNE_PACKAGE + "IChannelTunedListener";
/**
* Interfaces for handling tuner frontend signal info.
- * @hide
*/
public static final String ITUNER_FRONTEND_SIGNAL_INFO_INTERFACE = SIGNAL_PACKAGE
+ "ITunerFrontendSignalInfoInterface";
/**
- * Interfaces for changes on tuner frontend signal info.
- * @hide
+ * Interfaces for notifying changes on tuner frontend signal info.
*/
public static final String ITUNER_FRONTEND_SIGNAL_INFO_LISTENER = SIGNAL_PACKAGE
+ "ITunerFrontendSignalInfoListener";
/**
* Interfaces for handling mux tune operations.
- * @hide
*/
public static final String IMUX_TUNE_SESSION = TUNE_PACKAGE + "IMuxTuneSession";
/**
* Interfaces for initing mux tune session.
- * @hide
*/
public static final String IMUX_TUNE = TUNE_PACKAGE + "IMuxTune";
diff --git a/media/java/android/media/tv/ad/TvAdView.java b/media/java/android/media/tv/ad/TvAdView.java
index dd2a534..ff0279f 100644
--- a/media/java/android/media/tv/ad/TvAdView.java
+++ b/media/java/android/media/tv/ad/TvAdView.java
@@ -240,6 +240,38 @@
}
}
+ /**
+ * Controls whether the TvAdView's surface is placed on top of other regular surface views in
+ * the window (but still behind the window itself).
+ *
+ * <p>Calling this overrides any previous call to {@link #setZOrderOnTop}.
+ *
+ * @param isMediaOverlay {@code true} to be on top of another regular surface, {@code false}
+ * otherwise.
+ */
+ @FlaggedApi(Flags.FLAG_ENABLE_AD_SERVICE_FW)
+ public void setZOrderMediaOverlay(boolean isMediaOverlay) {
+ if (mSurfaceView != null) {
+ mSurfaceView.setZOrderOnTop(false);
+ mSurfaceView.setZOrderMediaOverlay(isMediaOverlay);
+ }
+ }
+
+ /**
+ * Controls whether the TvAdView's surface is placed on top of its window.
+ *
+ * <p>Calling this overrides any previous call to {@link #setZOrderMediaOverlay}.
+ *
+ * @param onTop {@code true} to be on top of its window, {@code false} otherwise.
+ */
+ @FlaggedApi(Flags.FLAG_ENABLE_AD_SERVICE_FW)
+ public void setZOrderOnTop(boolean onTop) {
+ if (mSurfaceView != null) {
+ mSurfaceView.setZOrderMediaOverlay(false);
+ mSurfaceView.setZOrderOnTop(onTop);
+ }
+ }
+
private void resetSurfaceView() {
if (mSurfaceView != null) {
mSurfaceView.getHolder().removeCallback(mSurfaceHolderCallback);
diff --git a/media/java/android/media/tv/extension/oad/IOadUpdateInterface.aidl b/media/java/android/media/tv/extension/oad/IOadUpdateInterface.aidl
new file mode 100644
index 0000000..2a2e71a
--- /dev/null
+++ b/media/java/android/media/tv/extension/oad/IOadUpdateInterface.aidl
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.extension.oad;
+
+/**
+ * @hide
+ */
+interface IOadUpdateInterface {
+ // Enable or disable the OAD function.
+ void setOadStatus(boolean enable);
+ // Get status of OAD function.
+ boolean getOadStatus();
+ // Start OAD scan of all frequency in the program list.
+ void startScan();
+ // Stop OAD scan of all frequency in the program list.
+ void stopScan();
+ // Start OAD detect for the current channel.
+ void startDetect();
+ // Stop OAD detect for the current channel.
+ void stopDetect();
+ // Start OAD download after it has been detected or scanned.
+ void startDownload();
+ // Stop OAD download.
+ void stopDownload();
+ // Retrieves current OAD software version.
+ int getSoftwareVersion();
+}
diff --git a/core/java/android/text/ClientFlags.java b/media/java/android/media/tv/extension/pvr/IDeleteRecordedContentsCallback.aidl
similarity index 62%
copy from core/java/android/text/ClientFlags.java
copy to media/java/android/media/tv/extension/pvr/IDeleteRecordedContentsCallback.aidl
index ca88764..62f1511 100644
--- a/core/java/android/text/ClientFlags.java
+++ b/media/java/android/media/tv/extension/pvr/IDeleteRecordedContentsCallback.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,16 +14,11 @@
* limitations under the License.
*/
-package android.text;
+package android.media.tv.extension.pvr;
/**
- * An aconfig feature flags that can be accessible from application process without
- * ContentProvider IPCs.
- *
- * When you add new flags, you have to add flag string to {@link TextFlags#TEXT_ACONFIGS_FLAGS}.
- *
- * TODO(nona): Remove this class.
* @hide
*/
-public class ClientFlags {
+oneway interface IDeleteRecordedContentsCallback {
+ void onRecordedContentsDeleted(in String[] contentUri, in int[] result);
}
diff --git a/core/java/android/text/ClientFlags.java b/media/java/android/media/tv/extension/pvr/IGetInfoRecordedContentsCallback.aidl
similarity index 62%
copy from core/java/android/text/ClientFlags.java
copy to media/java/android/media/tv/extension/pvr/IGetInfoRecordedContentsCallback.aidl
index ca88764..64f8fc2 100644
--- a/core/java/android/text/ClientFlags.java
+++ b/media/java/android/media/tv/extension/pvr/IGetInfoRecordedContentsCallback.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,16 +14,11 @@
* limitations under the License.
*/
-package android.text;
+package android.media.tv.extension.pvr;
/**
- * An aconfig feature flags that can be accessible from application process without
- * ContentProvider IPCs.
- *
- * When you add new flags, you have to add flag string to {@link TextFlags#TEXT_ACONFIGS_FLAGS}.
- *
- * TODO(nona): Remove this class.
* @hide
*/
-public class ClientFlags {
+interface IGetInfoRecordedContentsCallback {
+ void onRecordedContentsGetInfo(int result);
}
diff --git a/media/java/android/media/tv/extension/pvr/IRecordedContents.aidl b/media/java/android/media/tv/extension/pvr/IRecordedContents.aidl
new file mode 100644
index 0000000..74a15b8
--- /dev/null
+++ b/media/java/android/media/tv/extension/pvr/IRecordedContents.aidl
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.extension.pvr;
+
+import android.media.tv.extension.pvr.IDeleteRecordedContentsCallback;
+import android.media.tv.extension.pvr.IGetInfoRecordedContentsCallback;
+
+
+/**
+ * @hide
+ */
+interface IRecordedContents {
+ // Delete recorded contents by URIs
+ // using callback to notify the result or any errors during the deletion process.
+ void deleteRecordedContents(in String[] contentUri,
+ in IDeleteRecordedContentsCallback callback);
+ // Get the channel lock status for recorded content identified by the URI provided in sync way.
+ int getRecordedContentsLockInfoSync(String contentUri);
+ // Get the channel lock status for recorded content identified by the URI provided in async way.
+ void getRecordedContentsLockInfoAsync(String contentUri,
+ in IGetInfoRecordedContentsCallback callback);
+}
diff --git a/core/java/android/text/ClientFlags.java b/media/java/android/media/tv/extension/rating/IDownloadableRatingTableMonitor.aidl
similarity index 62%
copy from core/java/android/text/ClientFlags.java
copy to media/java/android/media/tv/extension/rating/IDownloadableRatingTableMonitor.aidl
index ca88764..bf1a385 100644
--- a/core/java/android/text/ClientFlags.java
+++ b/media/java/android/media/tv/extension/rating/IDownloadableRatingTableMonitor.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,16 +14,14 @@
* limitations under the License.
*/
-package android.text;
+package android.media.tv.extension.rating;
+
+import android.os.Bundle;
/**
- * An aconfig feature flags that can be accessible from application process without
- * ContentProvider IPCs.
- *
- * When you add new flags, you have to add flag string to {@link TextFlags#TEXT_ACONFIGS_FLAGS}.
- *
- * TODO(nona): Remove this class.
* @hide
*/
-public class ClientFlags {
+interface IDownloadableRatingTableMonitor {
+ // Get RRT rating info on downloadable rating data
+ Bundle[] getTable();
}
diff --git a/media/java/android/media/tv/extension/rating/IPmtRatingInterface.aidl b/media/java/android/media/tv/extension/rating/IPmtRatingInterface.aidl
new file mode 100644
index 0000000..06cac3d
--- /dev/null
+++ b/media/java/android/media/tv/extension/rating/IPmtRatingInterface.aidl
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.extension.rating;
+
+import android.media.tv.extension.rating.IPmtRatingListener;
+
+/**
+ * @hide
+ */
+interface IPmtRatingInterface {
+ // Get Pmt rating information.
+ String getPmtRating(String sessionToken);
+ // Register a listener for pmt rating updates.
+ void addPmtRatingListener(String clientToken, in IPmtRatingListener listener);
+ // Remove the previously added IPmtRatingListener.
+ void removePmtRatingListener(in IPmtRatingListener listener);
+}
diff --git a/core/java/android/text/ClientFlags.java b/media/java/android/media/tv/extension/rating/IPmtRatingListener.aidl
similarity index 62%
copy from core/java/android/text/ClientFlags.java
copy to media/java/android/media/tv/extension/rating/IPmtRatingListener.aidl
index ca88764..d88ae94 100644
--- a/core/java/android/text/ClientFlags.java
+++ b/media/java/android/media/tv/extension/rating/IPmtRatingListener.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,16 +14,11 @@
* limitations under the License.
*/
-package android.text;
+package android.media.tv.extension.rating;
/**
- * An aconfig feature flags that can be accessible from application process without
- * ContentProvider IPCs.
- *
- * When you add new flags, you have to add flag string to {@link TextFlags#TEXT_ACONFIGS_FLAGS}.
- *
- * TODO(nona): Remove this class.
* @hide
*/
-public class ClientFlags {
+oneway interface IPmtRatingListener {
+ void onPmtRatingChanged(String sessionToken, String newTvContentRating);
}
diff --git a/media/java/android/media/tv/extension/rating/IProgramRatingInfo.aidl b/media/java/android/media/tv/extension/rating/IProgramRatingInfo.aidl
new file mode 100644
index 0000000..a490491
--- /dev/null
+++ b/media/java/android/media/tv/extension/rating/IProgramRatingInfo.aidl
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.extension.rating;
+
+import android.media.tv.extension.rating.IProgramRatingInfoListener;
+import android.os.Bundle;
+
+/**
+ * @hide
+ */
+interface IProgramRatingInfo {
+ // Register a listener to receive notifications when ProgramRatingInfo is updated.
+ void addProgramRatingInfoListener(String clientToken, in IProgramRatingInfoListener listener);
+ // Remove a listener for ProgramRatingInfo update notifications.
+ void removeProgramRatingInfoListener(in IProgramRatingInfoListener listener);
+ // Get ProgramRatingInfo that may only be obtained when viewing.
+ Bundle getProgramRatingInfo(String sessionToken);
+}
diff --git a/core/java/android/text/ClientFlags.java b/media/java/android/media/tv/extension/rating/IProgramRatingInfoListener.aidl
similarity index 62%
copy from core/java/android/text/ClientFlags.java
copy to media/java/android/media/tv/extension/rating/IProgramRatingInfoListener.aidl
index ca88764..6777cd3 100644
--- a/core/java/android/text/ClientFlags.java
+++ b/media/java/android/media/tv/extension/rating/IProgramRatingInfoListener.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,16 +14,13 @@
* limitations under the License.
*/
-package android.text;
+package android.media.tv.extension.rating;
+
+import android.os.Bundle;
/**
- * An aconfig feature flags that can be accessible from application process without
- * ContentProvider IPCs.
- *
- * When you add new flags, you have to add flag string to {@link TextFlags#TEXT_ACONFIGS_FLAGS}.
- *
- * TODO(nona): Remove this class.
* @hide
*/
-public class ClientFlags {
+interface IProgramRatingInfoListener {
+ void onProgramInfoChanged(String sessionToken,in Bundle changedProgramInfo);
}
diff --git a/media/java/android/media/tv/extension/rating/IRatingInterface.aidl b/media/java/android/media/tv/extension/rating/IRatingInterface.aidl
new file mode 100644
index 0000000..d68fe76
--- /dev/null
+++ b/media/java/android/media/tv/extension/rating/IRatingInterface.aidl
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.extension.rating;
+
+import android.os.Bundle;
+
+/**
+ * @hide
+ */
+interface IRatingInterface {
+ // Get RRT rating information
+ Bundle getRRTRatingInfo();
+ // Set RRT rating information when user select
+ boolean setRRTRatingInfo(in Bundle param);
+ // Reset RRT5 to clear information
+ boolean setResetRrt5();
+}
diff --git a/media/java/android/media/tv/extension/rating/IVbiRatingInterface.aidl b/media/java/android/media/tv/extension/rating/IVbiRatingInterface.aidl
new file mode 100644
index 0000000..bad4067
--- /dev/null
+++ b/media/java/android/media/tv/extension/rating/IVbiRatingInterface.aidl
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.extension.rating;
+
+import android.media.tv.extension.rating.IVbiRatingListener;
+
+/**
+ * @hide
+ */
+interface IVbiRatingInterface {
+ // Get Vbi rating.
+ String getVbiRating(String sessionToken);
+ // Register a listener for Vbi rating updates.
+ void addVbiRatingListener(String clientToken, in IVbiRatingListener listener);
+ // Remove the previously added VbiRatingListener.
+ void removeVbiRatingListener(in IVbiRatingListener listener);
+}
diff --git a/core/java/android/text/ClientFlags.java b/media/java/android/media/tv/extension/rating/IVbiRatingListener.aidl
similarity index 62%
copy from core/java/android/text/ClientFlags.java
copy to media/java/android/media/tv/extension/rating/IVbiRatingListener.aidl
index ca88764..36d523f 100644
--- a/core/java/android/text/ClientFlags.java
+++ b/media/java/android/media/tv/extension/rating/IVbiRatingListener.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,16 +14,11 @@
* limitations under the License.
*/
-package android.text;
+package android.media.tv.extension.rating;
/**
- * An aconfig feature flags that can be accessible from application process without
- * ContentProvider IPCs.
- *
- * When you add new flags, you have to add flag string to {@link TextFlags#TEXT_ACONFIGS_FLAGS}.
- *
- * TODO(nona): Remove this class.
* @hide
*/
-public class ClientFlags {
+oneway interface IVbiRatingListener {
+ void onVbiRatingChanged(String sessionToken, String newTvContentRating);
}
diff --git a/media/java/android/media/tv/extension/scan/IFavoriteNetwork.aidl b/media/java/android/media/tv/extension/scan/IFavoriteNetwork.aidl
new file mode 100644
index 0000000..ff78aa4
--- /dev/null
+++ b/media/java/android/media/tv/extension/scan/IFavoriteNetwork.aidl
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.extension.scan;
+
+import android.media.tv.extension.scan.IFavoriteNetworkListener;
+import android.os.Bundle;
+
+/**
+ * Country: Norway
+ * Broadcast Type: BROADCAST_TYPE_DVB_T
+ * (Operator: RiksTV)
+ *
+ * @hide
+ */
+interface IFavoriteNetwork {
+ // Get the favorite network information,If there are no conflicts, the array of Bundle is empty.
+ Bundle[] getFavoriteNetworks();
+ // Select and set one of two or more favorite networks detected by the service scan.
+ int setFavoriteNetwork(in Bundle favoriteNetworkSettings);
+ // Set the listener to be invoked when two or more favorite networks are detected.
+ int setListener(in IFavoriteNetworkListener listener);
+}
diff --git a/core/java/android/text/ClientFlags.java b/media/java/android/media/tv/extension/scan/IFavoriteNetworkListener.aidl
similarity index 62%
copy from core/java/android/text/ClientFlags.java
copy to media/java/android/media/tv/extension/scan/IFavoriteNetworkListener.aidl
index ca88764..6994224 100644
--- a/core/java/android/text/ClientFlags.java
+++ b/media/java/android/media/tv/extension/scan/IFavoriteNetworkListener.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,16 +14,13 @@
* limitations under the License.
*/
-package android.text;
+package android.media.tv.extension.scan;
+
+import android.os.Bundle;
/**
- * An aconfig feature flags that can be accessible from application process without
- * ContentProvider IPCs.
- *
- * When you add new flags, you have to add flag string to {@link TextFlags#TEXT_ACONFIGS_FLAGS}.
- *
- * TODO(nona): Remove this class.
* @hide
*/
-public class ClientFlags {
+oneway interface IFavoriteNetworkListener {
+ void onDetectFavoriteNetwork(in Bundle detectFavoriteNetworks);
}
diff --git a/core/java/android/text/ClientFlags.java b/media/java/android/media/tv/extension/scan/IHDPlusInfo.aidl
similarity index 62%
copy from core/java/android/text/ClientFlags.java
copy to media/java/android/media/tv/extension/scan/IHDPlusInfo.aidl
index ca88764..cdf6e23 100644
--- a/core/java/android/text/ClientFlags.java
+++ b/media/java/android/media/tv/extension/scan/IHDPlusInfo.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,16 +14,12 @@
* limitations under the License.
*/
-package android.text;
+package android.media.tv.extension.scan;
/**
- * An aconfig feature flags that can be accessible from application process without
- * ContentProvider IPCs.
- *
- * When you add new flags, you have to add flag string to {@link TextFlags#TEXT_ACONFIGS_FLAGS}.
- *
- * TODO(nona): Remove this class.
* @hide
*/
-public class ClientFlags {
+interface IHDPlusInfo {
+ // Specifying a HDPlusInfo and start a network scan.
+ int setHDPlusInfo(String isBlindScanContinue, String isHDMode);
}
diff --git a/media/java/android/media/tv/extension/scan/ILcnConflict.aidl b/media/java/android/media/tv/extension/scan/ILcnConflict.aidl
new file mode 100644
index 0000000..5dff39e
--- /dev/null
+++ b/media/java/android/media/tv/extension/scan/ILcnConflict.aidl
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.extension.scan;
+
+import android.media.tv.extension.scan.ILcnConflictListener;
+import android.os.Bundle;
+
+/**
+ * Country: Italy, France
+ * Broadcast Type: BROADCAST_TYPE_DVB_T
+ *
+ * @hide
+ */
+interface ILcnConflict {
+ // Get the LCN conflict groups information, If there are no conflicts, the array of Bundle is empty.
+ Bundle[] getLcnConflictGroups();
+ // Resolve LCN conflicts caused by service scans.
+ int resolveLcnConflict(in Bundle[] lcnConflictSettings);
+ // Set the listener to be invoked the LCN conflict event.
+ int setListener(in ILcnConflictListener listener);
+}
diff --git a/core/java/android/text/ClientFlags.java b/media/java/android/media/tv/extension/scan/ILcnConflictListener.aidl
similarity index 62%
copy from core/java/android/text/ClientFlags.java
copy to media/java/android/media/tv/extension/scan/ILcnConflictListener.aidl
index ca88764..6bbbeb8 100644
--- a/core/java/android/text/ClientFlags.java
+++ b/media/java/android/media/tv/extension/scan/ILcnConflictListener.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,16 +14,13 @@
* limitations under the License.
*/
-package android.text;
+package android.media.tv.extension.scan;
+
+import android.os.Bundle;
/**
- * An aconfig feature flags that can be accessible from application process without
- * ContentProvider IPCs.
- *
- * When you add new flags, you have to add flag string to {@link TextFlags#TEXT_ACONFIGS_FLAGS}.
- *
- * TODO(nona): Remove this class.
* @hide
*/
-public class ClientFlags {
+oneway interface ILcnConflictListener {
+ void onDetectLcnConflict(in Bundle detectLcnConflicts);
}
diff --git a/media/java/android/media/tv/extension/scan/ILcnV2ChannelList.aidl b/media/java/android/media/tv/extension/scan/ILcnV2ChannelList.aidl
new file mode 100644
index 0000000..f9a9d34
--- /dev/null
+++ b/media/java/android/media/tv/extension/scan/ILcnV2ChannelList.aidl
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.extension.scan;
+
+import android.media.tv.extension.scan.ILcnV2ChannelListListener;
+import android.os.Bundle;
+
+/**
+ * Country: (NorDig etc.)
+ * Broadcast Type: BROADCAST_TYPE_DVB_T, BROADCAST_TYPE_DVB_C
+ *
+ * @hide
+ */
+interface ILcnV2ChannelList {
+ // Get the LCN V2 channel list information. If there are no conflicts, the array of Bundle is empty.
+ Bundle[] getLcnV2ChannelLists();
+ // Select and set one of two or more LCN V2 channel list detected by the service scan.
+ int setLcnV2ChannelList(in Bundle lcnV2ChannelListSettings);
+ // Set the listener to be invoked when two or more LCN V2 channel list are detected.
+ int setListener(in ILcnV2ChannelListListener listener);
+}
diff --git a/core/java/android/text/ClientFlags.java b/media/java/android/media/tv/extension/scan/ILcnV2ChannelListListener.aidl
similarity index 62%
copy from core/java/android/text/ClientFlags.java
copy to media/java/android/media/tv/extension/scan/ILcnV2ChannelListListener.aidl
index ca88764..cbdb83c 100644
--- a/core/java/android/text/ClientFlags.java
+++ b/media/java/android/media/tv/extension/scan/ILcnV2ChannelListListener.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,16 +14,13 @@
* limitations under the License.
*/
-package android.text;
+package android.media.tv.extension.scan;
+
+import android.os.Bundle;
/**
- * An aconfig feature flags that can be accessible from application process without
- * ContentProvider IPCs.
- *
- * When you add new flags, you have to add flag string to {@link TextFlags#TEXT_ACONFIGS_FLAGS}.
- *
- * TODO(nona): Remove this class.
* @hide
*/
-public class ClientFlags {
+oneway interface ILcnV2ChannelListListener {
+ void onDetectLcnV2ChannelList(in Bundle detectLcnV2ChannelList);
}
diff --git a/media/java/android/media/tv/extension/scan/IOperatorDetection.aidl b/media/java/android/media/tv/extension/scan/IOperatorDetection.aidl
new file mode 100644
index 0000000..770f866
--- /dev/null
+++ b/media/java/android/media/tv/extension/scan/IOperatorDetection.aidl
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.extension.scan;
+
+import android.media.tv.extension.scan.IOperatorDetectionListener;
+import android.os.Bundle;
+
+/**
+ * Country: Any
+ * Broadcast Type: BROADCAST_TYPE_DVB_S
+ * (Operator: M7)
+ *
+ * @hide
+ */
+interface IOperatorDetection {
+ // Set the operator selected info for scanning.
+ int setOperatorDetection(in Bundle operatorSelected);
+ // Set the listener to be invoked when one or more operator detection has been detected by
+ // operator detection searches.
+ int setListener(in IOperatorDetectionListener listener);
+}
diff --git a/core/java/android/text/ClientFlags.java b/media/java/android/media/tv/extension/scan/IOperatorDetectionListener.aidl
similarity index 62%
copy from core/java/android/text/ClientFlags.java
copy to media/java/android/media/tv/extension/scan/IOperatorDetectionListener.aidl
index ca88764..7dcd461 100644
--- a/core/java/android/text/ClientFlags.java
+++ b/media/java/android/media/tv/extension/scan/IOperatorDetectionListener.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,16 +14,14 @@
* limitations under the License.
*/
-package android.text;
+package android.media.tv.extension.scan;
+
+import android.os.Bundle;
+
/**
- * An aconfig feature flags that can be accessible from application process without
- * ContentProvider IPCs.
- *
- * When you add new flags, you have to add flag string to {@link TextFlags#TEXT_ACONFIGS_FLAGS}.
- *
- * TODO(nona): Remove this class.
* @hide
*/
-public class ClientFlags {
+oneway interface IOperatorDetectionListener {
+ void onDetectOperatorDetectionList(in Bundle[] detectOperatorDetectionList);
}
diff --git a/media/java/android/media/tv/extension/scan/IRegionChannelList.aidl b/media/java/android/media/tv/extension/scan/IRegionChannelList.aidl
new file mode 100644
index 0000000..fe755f8
--- /dev/null
+++ b/media/java/android/media/tv/extension/scan/IRegionChannelList.aidl
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.extension.scan;
+
+import android.media.tv.extension.scan.IRegionChannelListListener;
+
+/**
+ * @hide
+ */
+interface IRegionChannelList {
+ // Set the region channel list for scanning.
+ int setRegionChannelList(String regionChannelList);
+ // Set the listener to be invoked when one or more region channel list has been detected by
+ // region channel list searches.
+ int setListener(in IRegionChannelListListener listener);
+}
diff --git a/core/java/android/text/ClientFlags.java b/media/java/android/media/tv/extension/scan/IRegionChannelListListener.aidl
similarity index 62%
copy from core/java/android/text/ClientFlags.java
copy to media/java/android/media/tv/extension/scan/IRegionChannelListListener.aidl
index ca88764..06b0eb5 100644
--- a/core/java/android/text/ClientFlags.java
+++ b/media/java/android/media/tv/extension/scan/IRegionChannelListListener.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,16 +14,11 @@
* limitations under the License.
*/
-package android.text;
+package android.media.tv.extension.scan;
/**
- * An aconfig feature flags that can be accessible from application process without
- * ContentProvider IPCs.
- *
- * When you add new flags, you have to add flag string to {@link TextFlags#TEXT_ACONFIGS_FLAGS}.
- *
- * TODO(nona): Remove this class.
* @hide
*/
-public class ClientFlags {
+oneway interface IRegionChannelListListener {
+ void onDetectRegionChannelList(in String[] detectRegionChannelList);
}
diff --git a/media/java/android/media/tv/extension/scan/IScanInterface.aidl b/media/java/android/media/tv/extension/scan/IScanInterface.aidl
new file mode 100644
index 0000000..b44d1d2
--- /dev/null
+++ b/media/java/android/media/tv/extension/scan/IScanInterface.aidl
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.extension.scan;
+
+import android.media.tv.extension.scan.IScanListener;
+import android.os.Bundle;
+
+/**
+ * @hide
+ */
+interface IScanInterface {
+ IBinder createSession(int broadcastType, String countryCode, String operator,
+ in IScanListener listener);
+ Bundle getParameters(int broadcastType, String countryCode, String operator,
+ in Bundle params);
+}
diff --git a/media/java/android/media/tv/extension/scan/IScanListener.aidl b/media/java/android/media/tv/extension/scan/IScanListener.aidl
new file mode 100644
index 0000000..2c4807f
--- /dev/null
+++ b/media/java/android/media/tv/extension/scan/IScanListener.aidl
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.extension.scan;
+
+import android.os.Bundle;
+
+/**
+ * @hide
+ */
+oneway interface IScanListener {
+ // notify events during scan.
+ void onEvent(in Bundle eventArgs);
+ // notify the scan progress.
+ void onScanProgress(String scanProgress, in Bundle scanProgressInfo);
+ // notify the scan completion.
+ void onScanCompleted(int scanResult);
+ // notify that the temporaily held channel list is stored.
+ void onStoreCompleted(int storeResult);
+}
diff --git a/core/java/android/text/ClientFlags.java b/media/java/android/media/tv/extension/scan/IScanSatSearch.aidl
similarity index 62%
copy from core/java/android/text/ClientFlags.java
copy to media/java/android/media/tv/extension/scan/IScanSatSearch.aidl
index ca88764..b8074fc 100644
--- a/core/java/android/text/ClientFlags.java
+++ b/media/java/android/media/tv/extension/scan/IScanSatSearch.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,16 +14,13 @@
* limitations under the License.
*/
-package android.text;
+package android.media.tv.extension.scan;
/**
- * An aconfig feature flags that can be accessible from application process without
- * ContentProvider IPCs.
- *
- * When you add new flags, you have to add flag string to {@link TextFlags#TEXT_ACONFIGS_FLAGS}.
- *
- * TODO(nona): Remove this class.
+ * For satellite search function.
* @hide
*/
-public class ClientFlags {
+interface IScanSatSearch {
+ // Set currecnt LNB as customized LNB, default LNB is universal LNB
+ int setCustomizedLnb(String customizedLnb);
}
diff --git a/media/java/android/media/tv/extension/scan/IScanSession.aidl b/media/java/android/media/tv/extension/scan/IScanSession.aidl
new file mode 100644
index 0000000..d42eca1
--- /dev/null
+++ b/media/java/android/media/tv/extension/scan/IScanSession.aidl
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.extension.scan;
+
+import android.os.Bundle;
+
+/**
+ * @hide
+ */
+interface IScanSession {
+ // Start a service scan.
+ int startScan(int broadcastType, String countryCode, String operator, in int[] frequency,
+ String scanType, String languageCode);
+ // Reset the scan information held in TIS.
+ int resetScan();
+ // Cancel scan.
+ int cancelScan();
+
+ // Get available interface for created ScanExtension interface.
+ String[] getAvailableExtensionInterfaceNames();
+ // Get extension interface for Scan.
+ IBinder getExtensionInterface(String name);
+
+ // Clear the results of the service scan from the service database.
+ int clearServiceList(in Bundle optionalClearParams);
+ // Store the results of the service scan from the service database.
+ int storeServiceList();
+ // Get a service information specified by the service information ID.
+ Bundle getServiceInfo(String serviceInfoId, in String[] keys);
+ // Get a service information ID list.
+ String[] getServiceInfoIdList();
+ // Get a list of service info by the filter.
+ Bundle getServiceInfoList(in Bundle filterInfo, in String[] keys);
+ // Update the service information.
+ int updateServiceInfo(in Bundle serviceInfo);
+ // Updates the service information for the specified service information ID in array list.
+ int updateServiceInfoByList(in Bundle[] serviceInfo);
+
+ /* DVBI specific functions */
+ // Get all of the serviceLists, parsed from Local TV storage, Broadcast, USB file discovery.
+ Bundle getServiceLists();
+ // Users choose one serviceList from the serviceLists, and install the services.
+ int setServiceList(int serviceListRecId);
+ // Get all of the packageData, parsed from the selected serviceList XML.
+ Bundle getPackageData();
+ // Choose the package using package id and install the corresponding services.
+ int setPackage(String packageId);
+ // Get all of the countryRegionData, parsed from the selected serviceList XML.
+ Bundle getCountryRegionData();
+ // Choose the countryRegion using countryRegion id, and install the corresponding services.
+ int setCountryRegion(String regionId);
+ // Get all of the regionData, parsed from the selected serviceList XML.
+ Bundle getRegionData();
+ // Choose the region using the regionData id, and install the corresponding services.
+ int setRegion(String regionId);
+
+ // Get unique session token for the scan.
+ String getSessionToken();
+ // Release scan resource, the register listener will be released.
+ int release();
+}
diff --git a/media/java/android/media/tv/extension/scan/ITargetRegion.aidl b/media/java/android/media/tv/extension/scan/ITargetRegion.aidl
new file mode 100644
index 0000000..417e122
--- /dev/null
+++ b/media/java/android/media/tv/extension/scan/ITargetRegion.aidl
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.extension.scan;
+
+import android.media.tv.extension.scan.ITargetRegionListener;
+
+import android.os.Bundle;
+
+/**
+ * Country: U.K.
+ * Broadcast Type: BROADCAST_TYPE_DVB_T
+ *
+ * @hide
+ */
+interface ITargetRegion {
+ // Get the target regions information. If there are no conflicts, the array of Bundle is empty.
+ Bundle[] getTargetRegions();
+ // Select and set one of two or more target region detected by the service scan.
+ int setTargetRegion(in Bundle targetRegionSettings);
+ // Set the listener to be invoked when two or more regions are detected.
+ int setListener(in ITargetRegionListener listener);
+}
diff --git a/core/java/android/text/ClientFlags.java b/media/java/android/media/tv/extension/scan/ITargetRegionListener.aidl
similarity index 62%
copy from core/java/android/text/ClientFlags.java
copy to media/java/android/media/tv/extension/scan/ITargetRegionListener.aidl
index ca88764..9d6aa8e 100644
--- a/core/java/android/text/ClientFlags.java
+++ b/media/java/android/media/tv/extension/scan/ITargetRegionListener.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,16 +14,13 @@
* limitations under the License.
*/
-package android.text;
+package android.media.tv.extension.scan;
+
+import android.os.Bundle;
/**
- * An aconfig feature flags that can be accessible from application process without
- * ContentProvider IPCs.
- *
- * When you add new flags, you have to add flag string to {@link TextFlags#TEXT_ACONFIGS_FLAGS}.
- *
- * TODO(nona): Remove this class.
* @hide
*/
-public class ClientFlags {
+oneway interface ITargetRegionListener {
+ void onDetectTargetRegion(in Bundle detectTargetRegions);
}
diff --git a/core/java/android/text/ClientFlags.java b/media/java/android/media/tv/extension/scan/ITkgsInfo.aidl
similarity index 62%
copy from core/java/android/text/ClientFlags.java
copy to media/java/android/media/tv/extension/scan/ITkgsInfo.aidl
index ca88764..f25952c 100644
--- a/core/java/android/text/ClientFlags.java
+++ b/media/java/android/media/tv/extension/scan/ITkgsInfo.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,16 +14,15 @@
* limitations under the License.
*/
-package android.text;
+package android.media.tv.extension.scan;
+
+import android.media.tv.extension.scan.ITkgsInfoListener;
+import android.os.Bundle;
/**
- * An aconfig feature flags that can be accessible from application process without
- * ContentProvider IPCs.
- *
- * When you add new flags, you have to add flag string to {@link TextFlags#TEXT_ACONFIGS_FLAGS}.
- *
- * TODO(nona): Remove this class.
* @hide
*/
-public class ClientFlags {
+interface ITkgsInfo {
+ int setPrefServiceList(String prefServiceList);
+ int setTkgsInfoListener(in ITkgsInfoListener listener);
}
diff --git a/core/java/android/text/ClientFlags.java b/media/java/android/media/tv/extension/scan/ITkgsInfoListener.aidl
similarity index 62%
copy from core/java/android/text/ClientFlags.java
copy to media/java/android/media/tv/extension/scan/ITkgsInfoListener.aidl
index ca88764..e3dcf2d 100644
--- a/core/java/android/text/ClientFlags.java
+++ b/media/java/android/media/tv/extension/scan/ITkgsInfoListener.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,16 +14,13 @@
* limitations under the License.
*/
-package android.text;
+package android.media.tv.extension.scan;
/**
- * An aconfig feature flags that can be accessible from application process without
- * ContentProvider IPCs.
- *
- * When you add new flags, you have to add flag string to {@link TextFlags#TEXT_ACONFIGS_FLAGS}.
- *
- * TODO(nona): Remove this class.
* @hide
*/
-public class ClientFlags {
+oneway interface ITkgsInfoListener {
+ void onServiceList(in String[] serviceList);
+ void onTableVersionUpdate(int tableVersion);
+ void onUserMessage(String strMessage);
}
diff --git a/core/java/android/text/ClientFlags.java b/media/java/android/media/tv/extension/signal/IAnalogAudioInfo.aidl
similarity index 62%
copy from core/java/android/text/ClientFlags.java
copy to media/java/android/media/tv/extension/signal/IAnalogAudioInfo.aidl
index ca88764..742191f 100644
--- a/core/java/android/text/ClientFlags.java
+++ b/media/java/android/media/tv/extension/signal/IAnalogAudioInfo.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,16 +14,13 @@
* limitations under the License.
*/
-package android.text;
+package android.media.tv.extension.signal;
+
+import android.os.Bundle;
/**
- * An aconfig feature flags that can be accessible from application process without
- * ContentProvider IPCs.
- *
- * When you add new flags, you have to add flag string to {@link TextFlags#TEXT_ACONFIGS_FLAGS}.
- *
- * TODO(nona): Remove this class.
* @hide
*/
-public class ClientFlags {
+interface IAnalogAudioInfo {
+ Bundle getAnalogAudioInfo(String sessionToken);
}
diff --git a/media/java/android/media/tv/extension/signal/IAudioSignalInfo.aidl b/media/java/android/media/tv/extension/signal/IAudioSignalInfo.aidl
new file mode 100644
index 0000000..4c953a0
--- /dev/null
+++ b/media/java/android/media/tv/extension/signal/IAudioSignalInfo.aidl
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.extension.signal;
+
+import android.media.tv.extension.signal.IAudioSignalInfoListener;
+import android.os.Bundle;
+
+/**
+ * @hide
+ */
+interface IAudioSignalInfo {
+ // Get audio signal information.
+ Bundle getAudioSignalInfo(String sessionToken);
+ // Notify TIS whether user selects audio track via mts button on the remote control.
+ void notifyMtsSelectTrackFlag(boolean mtsFlag);
+ // Get the audio track id selected via mts.
+ String getMtsSelectedTrackId();
+ // Register a listener to receive the updated audio signal information.
+ void addAudioSignalInfoListener(String clientToken, in IAudioSignalInfoListener listener);
+ // Remove a listener for audio signal information update notifications.
+ void removeAudioSignalInfoListener(in IAudioSignalInfoListener listener);
+}
diff --git a/core/java/android/text/ClientFlags.java b/media/java/android/media/tv/extension/signal/IAudioSignalInfoListener.aidl
similarity index 62%
copy from core/java/android/text/ClientFlags.java
copy to media/java/android/media/tv/extension/signal/IAudioSignalInfoListener.aidl
index ca88764..adf239a 100644
--- a/core/java/android/text/ClientFlags.java
+++ b/media/java/android/media/tv/extension/signal/IAudioSignalInfoListener.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,16 +14,13 @@
* limitations under the License.
*/
-package android.text;
+package android.media.tv.extension.signal;
+
+import android.os.Bundle;
/**
- * An aconfig feature flags that can be accessible from application process without
- * ContentProvider IPCs.
- *
- * When you add new flags, you have to add flag string to {@link TextFlags#TEXT_ACONFIGS_FLAGS}.
- *
- * TODO(nona): Remove this class.
* @hide
*/
-public class ClientFlags {
+oneway interface IAudioSignalInfoListener {
+ void onAudioSignalInfoChanged(String sessionToken, in Bundle changedSignalInfo);
}
diff --git a/core/java/android/text/ClientFlags.java b/media/java/android/media/tv/extension/signal/IHdmiSignalInfoListener.aidl
similarity index 62%
copy from core/java/android/text/ClientFlags.java
copy to media/java/android/media/tv/extension/signal/IHdmiSignalInfoListener.aidl
index ca88764..bd468b2 100644
--- a/core/java/android/text/ClientFlags.java
+++ b/media/java/android/media/tv/extension/signal/IHdmiSignalInfoListener.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,16 +14,12 @@
* limitations under the License.
*/
-package android.text;
+package android.media.tv.extension.signal;
/**
- * An aconfig feature flags that can be accessible from application process without
- * ContentProvider IPCs.
- *
- * When you add new flags, you have to add flag string to {@link TextFlags#TEXT_ACONFIGS_FLAGS}.
- *
- * TODO(nona): Remove this class.
* @hide
*/
-public class ClientFlags {
+oneway interface IHdmiSignalInfoListener {
+ void onSignalInfoChanged(String sessionToken);
+ void onLowLatencyModeChanged(int enable);
}
diff --git a/media/java/android/media/tv/extension/signal/IHdmiSignalInterface.aidl b/media/java/android/media/tv/extension/signal/IHdmiSignalInterface.aidl
new file mode 100644
index 0000000..39625e3
--- /dev/null
+++ b/media/java/android/media/tv/extension/signal/IHdmiSignalInterface.aidl
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.extension.signal;
+
+import android.media.tv.extension.signal.IHdmiSignalInfoListener;
+import android.os.Bundle;
+
+/**
+ * @hide
+ */
+interface IHdmiSignalInterface {
+ // Register a listener for Hdmi Signal Info updates.
+ void addHdmiSignalInfoListener(String inputId, in IHdmiSignalInfoListener listener);
+ // Remove a listener for Hdmi Signal Info update notifications.
+ void removeHdmiSignalInfoListener(String inputId, in IHdmiSignalInfoListener listener);
+ // Obtain HdmiSignalInfo based on the inputId and sessionToken.
+ Bundle getHdmiSignalInfo(String sessionToken);
+ // Enable/disable low-latency decoding mode.
+ void setLowLatency(String sessionToken, int mode);
+ // Enable/disable force-VRR mode.
+ void setForceVrr(String sessionToken, int mode);
+}
diff --git a/media/java/android/media/tv/extension/signal/ITunerFrontendSignalInfoInterface.aidl b/media/java/android/media/tv/extension/signal/ITunerFrontendSignalInfoInterface.aidl
new file mode 100644
index 0000000..7f05e70
--- /dev/null
+++ b/media/java/android/media/tv/extension/signal/ITunerFrontendSignalInfoInterface.aidl
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.extension.signal;
+
+import android.media.tv.extension.signal.ITunerFrontendSignalInfoListener;
+import android.os.Bundle;
+
+/**
+* @hide
+*/
+interface ITunerFrontendSignalInfoInterface {
+ Bundle getFrontendSignalInfo(String sessionToken);
+ void setFrontendSignalInfoListener(in ITunerFrontendSignalInfoListener listener);
+}
diff --git a/core/java/android/text/ClientFlags.java b/media/java/android/media/tv/extension/signal/ITunerFrontendSignalInfoListener.aidl
similarity index 60%
copy from core/java/android/text/ClientFlags.java
copy to media/java/android/media/tv/extension/signal/ITunerFrontendSignalInfoListener.aidl
index ca88764..9c22a35 100644
--- a/core/java/android/text/ClientFlags.java
+++ b/media/java/android/media/tv/extension/signal/ITunerFrontendSignalInfoListener.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,16 +14,11 @@
* limitations under the License.
*/
-package android.text;
+package android.media.tv.extension.signal;
/**
- * An aconfig feature flags that can be accessible from application process without
- * ContentProvider IPCs.
- *
- * When you add new flags, you have to add flag string to {@link TextFlags#TEXT_ACONFIGS_FLAGS}.
- *
- * TODO(nona): Remove this class.
- * @hide
- */
-public class ClientFlags {
+* @hide
+*/
+oneway interface ITunerFrontendSignalInfoListener {
+ void onFrontendStatusChanged(int frontendStatus);
}
diff --git a/media/java/android/media/tv/extension/signal/IVideoSignalInfo.aidl b/media/java/android/media/tv/extension/signal/IVideoSignalInfo.aidl
new file mode 100644
index 0000000..b17142a
--- /dev/null
+++ b/media/java/android/media/tv/extension/signal/IVideoSignalInfo.aidl
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.extension.signal;
+
+import android.media.tv.extension.signal.IVideoSignalInfoListener;
+import android.os.Bundle;
+
+
+/**
+ * @hide
+ */
+interface IVideoSignalInfo {
+ void addVideoSignalInfoListener(String clientToken, in IVideoSignalInfoListener listener);
+ void removeVideoSignalInfoListener(in IVideoSignalInfoListener listener);
+ Bundle getVideoSignalInfo(String sessionToken);
+}
diff --git a/core/java/android/text/ClientFlags.java b/media/java/android/media/tv/extension/signal/IVideoSignalInfoListener.aidl
similarity index 62%
copy from core/java/android/text/ClientFlags.java
copy to media/java/android/media/tv/extension/signal/IVideoSignalInfoListener.aidl
index ca88764..aafc192 100644
--- a/core/java/android/text/ClientFlags.java
+++ b/media/java/android/media/tv/extension/signal/IVideoSignalInfoListener.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,16 +14,13 @@
* limitations under the License.
*/
-package android.text;
+package android.media.tv.extension.signal;
+
+import android.os.Bundle;
/**
- * An aconfig feature flags that can be accessible from application process without
- * ContentProvider IPCs.
- *
- * When you add new flags, you have to add flag string to {@link TextFlags#TEXT_ACONFIGS_FLAGS}.
- *
- * TODO(nona): Remove this class.
* @hide
*/
-public class ClientFlags {
+oneway interface IVideoSignalInfoListener {
+ void onVideoSignalInfoChanged(String sessionToken, in Bundle changedSignalInfo);
}
diff --git a/core/java/android/text/ClientFlags.java b/media/java/android/media/tv/extension/time/IBroadcastTime.aidl
similarity index 61%
copy from core/java/android/text/ClientFlags.java
copy to media/java/android/media/tv/extension/time/IBroadcastTime.aidl
index ca88764..123d00f 100644
--- a/core/java/android/text/ClientFlags.java
+++ b/media/java/android/media/tv/extension/time/IBroadcastTime.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,16 +14,17 @@
* limitations under the License.
*/
-package android.text;
+package android.media.tv.extension.time;
+
+import android.os.Bundle;
/**
- * An aconfig feature flags that can be accessible from application process without
- * ContentProvider IPCs.
- *
- * When you add new flags, you have to add flag string to {@link TextFlags#TEXT_ACONFIGS_FLAGS}.
- *
- * TODO(nona): Remove this class.
* @hide
*/
-public class ClientFlags {
-}
+interface IBroadcastTime {
+ long getUtcTime();
+ long getLocalTime();
+ Bundle getTimeZoneInfo();
+ long getUtcTimePerStream(String SessionToken);
+ long getLocalTimePerStream(String SessionToken);
+}
\ No newline at end of file
diff --git a/media/java/android/media/tv/flags/media_tv.aconfig b/media/java/android/media/tv/flags/media_tv.aconfig
index 6441652..4b832ae 100644
--- a/media/java/android/media/tv/flags/media_tv.aconfig
+++ b/media/java/android/media/tv/flags/media_tv.aconfig
@@ -2,6 +2,22 @@
container: "system"
flag {
+ name: "enable_le_audio_broadcast_ui"
+ is_exported: true
+ namespace: "media_tv"
+ description: "Enable Broadcast UI for LE Audio on TV."
+ bug: "378732734"
+}
+
+flag {
+ name: "enable_le_audio_unicast_ui"
+ is_exported: true
+ namespace: "media_tv"
+ description: "Enable Unicast UI for LE Audio on TV."
+ bug: "378732734"
+}
+
+flag {
name: "broadcast_visibility_types"
is_exported: true
namespace: "media_tv"
@@ -77,11 +93,19 @@
name: "set_resource_holder_retain"
is_exported: true
namespace: "media_tv"
- description : "Feature flag to add setResourceHolderRetain api to MediaCas and Tuner JAVA."
+ description: "Feature flag to add setResourceHolderRetain api to MediaCas and Tuner JAVA."
bug: "372973197"
}
flag {
+ name: "mediacas_update_client_profile_priority"
+ is_exported: true
+ namespace: "media_tv"
+ description: "Feature flag to add updateResourcePriority api to MediaCas"
+ bug: "372971241"
+}
+
+flag {
name: "apply_picture_profiles"
is_exported: true
namespace: "media_tv"
diff --git a/media/java/android/media/tv/interactive/TvInteractiveAppView.java b/media/java/android/media/tv/interactive/TvInteractiveAppView.java
index 635572d..9e9699f 100644
--- a/media/java/android/media/tv/interactive/TvInteractiveAppView.java
+++ b/media/java/android/media/tv/interactive/TvInteractiveAppView.java
@@ -271,6 +271,38 @@
}
}
+ /**
+ * Controls whether the TvInteractiveAppView's surface is placed on top of other regular surface
+ * views in the window (but still behind the window itself).
+ *
+ * <p>Calling this overrides any previous call to {@link #setZOrderOnTop}.
+ *
+ * @param isMediaOverlay {@code true} to be on top of another regular surface, {@code false}
+ * otherwise.
+ */
+ @FlaggedApi(Flags.FLAG_TIAF_V_APIS)
+ public void setZOrderMediaOverlay(boolean isMediaOverlay) {
+ if (mSurfaceView != null) {
+ mSurfaceView.setZOrderOnTop(false);
+ mSurfaceView.setZOrderMediaOverlay(isMediaOverlay);
+ }
+ }
+
+ /**
+ * Controls whether the TvInterActiveAppView's surface is placed on top of its window.
+ *
+ * <p>Calling this overrides any previous call to {@link #setZOrderMediaOverlay}.
+ *
+ * @param onTop {@code true} to be on top of its window, {@code false} otherwise.
+ */
+ @FlaggedApi(Flags.FLAG_TIAF_V_APIS)
+ public void setZOrderOnTop(boolean onTop) {
+ if (mSurfaceView != null) {
+ mSurfaceView.setZOrderMediaOverlay(false);
+ mSurfaceView.setZOrderOnTop(onTop);
+ }
+ }
+
private void resetSurfaceView() {
if (mSurfaceView != null) {
mSurfaceView.getHolder().removeCallback(mSurfaceHolderCallback);
diff --git a/media/java/android/media/tv/tuner/frontend/FrontendStatus.java b/media/java/android/media/tv/tuner/frontend/FrontendStatus.java
index 898a8bf..4de7123 100644
--- a/media/java/android/media/tv/tuner/frontend/FrontendStatus.java
+++ b/media/java/android/media/tv/tuner/frontend/FrontendStatus.java
@@ -63,7 +63,7 @@
FRONTEND_STATUS_TYPE_DVBT_CELL_IDS, FRONTEND_STATUS_TYPE_ATSC3_ALL_PLP_INFO,
FRONTEND_STATUS_TYPE_IPTV_CONTENT_URL, FRONTEND_STATUS_TYPE_IPTV_PACKETS_LOST,
FRONTEND_STATUS_TYPE_IPTV_PACKETS_RECEIVED, FRONTEND_STATUS_TYPE_IPTV_WORST_JITTER_MS,
- FRONTEND_STATUS_TYPE_IPTV_AVERAGE_JITTER_MS, FRONTEND_STATUS_TYPE_STANDARD_EXT})
+ FRONTEND_STATUS_TYPE_IPTV_AVERAGE_JITTER_MS, FRONTEND_STATUS_TYPE_STANDARD_EXTENSION})
@Retention(RetentionPolicy.SOURCE)
public @interface FrontendStatusType {}
@@ -317,7 +317,7 @@
* Standard extension.
*/
@FlaggedApi(Flags.FLAG_TUNER_W_APIS)
- public static final int FRONTEND_STATUS_TYPE_STANDARD_EXT =
+ public static final int FRONTEND_STATUS_TYPE_STANDARD_EXTENSION =
android.hardware.tv.tuner.FrontendStatusType.STANDARD_EXT;
/** @hide */
@@ -567,7 +567,7 @@
private Long mIptvPacketsReceived;
private Integer mIptvWorstJitterMs;
private Integer mIptvAverageJitterMs;
- private StandardExt mStandardExt;
+ private StandardExtension mStandardExtension;
// Constructed and fields set by JNI code.
private FrontendStatus() {
@@ -1298,12 +1298,12 @@
*/
@NonNull
@FlaggedApi(Flags.FLAG_TUNER_W_APIS)
- public StandardExt getStandardExt() {
+ public StandardExtension getStandardExtension() {
TunerVersionChecker.checkHigherOrEqualVersionTo(
- TunerVersionChecker.TUNER_VERSION_4_0, "StandardExt status");
- if (mStandardExt == null) {
- throw new IllegalStateException("StandardExt status is empty");
+ TunerVersionChecker.TUNER_VERSION_4_0, "StandardExtension status");
+ if (mStandardExtension == null) {
+ throw new IllegalStateException("StandardExtension status is empty");
}
- return mStandardExt;
+ return mStandardExtension;
}
}
diff --git a/media/java/android/media/tv/tuner/frontend/StandardExt.java b/media/java/android/media/tv/tuner/frontend/StandardExtension.java
similarity index 74%
rename from media/java/android/media/tv/tuner/frontend/StandardExt.java
rename to media/java/android/media/tv/tuner/frontend/StandardExtension.java
index 4907272..8bff3dd 100644
--- a/media/java/android/media/tv/tuner/frontend/StandardExt.java
+++ b/media/java/android/media/tv/tuner/frontend/StandardExtension.java
@@ -29,16 +29,16 @@
*/
@SystemApi
@FlaggedApi(Flags.FLAG_TUNER_W_APIS)
-public final class StandardExt {
- private final int mDvbsStandardExt;
- private final int mDvbtStandardExt;
+public final class StandardExtension {
+ private final int mDvbsStandardExtension;
+ private final int mDvbtStandardExtension;
/**
* Private constructor called by JNI only.
*/
- private StandardExt(int dvbsStandardExt, int dvbtStandardExt) {
- mDvbsStandardExt = dvbsStandardExt;
- mDvbtStandardExt = dvbtStandardExt;
+ private StandardExtension(int dvbsStandardExtension, int dvbtStandardExtension) {
+ mDvbsStandardExtension = dvbsStandardExtension;
+ mDvbtStandardExtension = dvbtStandardExtension;
}
/**
@@ -50,11 +50,11 @@
* @see android.media.tv.tuner.frontend.DvbsFrontendSettings
*/
@DvbsFrontendSettings.Standard
- public int getDvbsStandardExt() {
- if (mDvbsStandardExt == FrontendDvbsStandard.UNDEFINED) {
+ public int getDvbsStandardExtension() {
+ if (mDvbsStandardExtension == FrontendDvbsStandard.UNDEFINED) {
throw new IllegalStateException("No DVB-S standard transition");
}
- return mDvbsStandardExt;
+ return mDvbsStandardExtension;
}
/**
@@ -66,10 +66,10 @@
* @see android.media.tv.tuner.frontend.DvbtFrontendSettings
*/
@DvbtFrontendSettings.Standard
- public int getDvbtStandardExt() {
- if (mDvbtStandardExt == FrontendDvbtStandard.UNDEFINED) {
+ public int getDvbtStandardExtension() {
+ if (mDvbtStandardExtension == FrontendDvbtStandard.UNDEFINED) {
throw new IllegalStateException("No DVB-T standard transition");
}
- return mDvbtStandardExt;
+ return mDvbtStandardExtension;
}
}
diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp
index d05ee55..a942300 100644
--- a/media/jni/android_media_MediaPlayer.cpp
+++ b/media/jni/android_media_MediaPlayer.cpp
@@ -1362,13 +1362,26 @@
return mp->setOutputDevice(device_id) == NO_ERROR;
}
-static jint android_media_MediaPlayer_getRoutedDeviceId(JNIEnv *env, jobject thiz)
+static jintArray android_media_MediaPlayer_getRoutedDeviceIds(JNIEnv *env, jobject thiz)
{
sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
if (mp == NULL) {
- return AUDIO_PORT_HANDLE_NONE;
+ return NULL;
}
- return mp->getRoutedDeviceId();
+ DeviceIdVector deviceIds;
+ // TODO: b/379161379 - Should we throw an exception if the result is not ok?
+ mp->getRoutedDeviceIds(deviceIds);
+ jintArray result;
+ result = env->NewIntArray(deviceIds.size());
+ if (result == NULL) {
+ return NULL;
+ }
+ jint* values = env->GetIntArrayElements(result, 0);
+ for (unsigned int i = 0; i < deviceIds.size(); i++) {
+ values[i++] = static_cast<jint>(deviceIds[i]);
+ }
+ env->ReleaseIntArrayElements(result, values, 0);
+ return result;
}
static void android_media_MediaPlayer_enableDeviceCallback(
@@ -1452,7 +1465,8 @@
// AudioRouting
{"native_setOutputDevice", "(I)Z", (void *)android_media_MediaPlayer_setOutputDevice},
- {"native_getRoutedDeviceId", "()I", (void *)android_media_MediaPlayer_getRoutedDeviceId},
+ {"native_getRoutedDeviceIds", "()[I",
+ (void *)android_media_MediaPlayer_getRoutedDeviceIds},
{"native_enableDeviceCallback", "(Z)V", (void *)android_media_MediaPlayer_enableDeviceCallback},
};
diff --git a/media/jni/android_media_MediaRecorder.cpp b/media/jni/android_media_MediaRecorder.cpp
index 9a6d5d7..643fc8a 100644
--- a/media/jni/android_media_MediaRecorder.cpp
+++ b/media/jni/android_media_MediaRecorder.cpp
@@ -722,21 +722,31 @@
return true;
}
-static jint
-android_media_MediaRecorder_getRoutedDeviceId(JNIEnv *env, jobject thiz)
+static jintArray
+android_media_MediaRecorder_getRoutedDeviceIds(JNIEnv *env, jobject thiz)
{
- ALOGV("android_media_MediaRecorder_getRoutedDeviceId");
+ ALOGV("android_media_MediaRecorder_getRoutedDeviceIds");
sp<MediaRecorder> mr = getMediaRecorder(env, thiz);
if (mr == NULL) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
- return AUDIO_PORT_HANDLE_NONE;
+ return NULL;
}
- audio_port_handle_t deviceId;
- process_media_recorder_call(env, mr->getRoutedDeviceId(&deviceId),
- "java/lang/RuntimeException", "getRoutedDeviceId failed.");
- return (jint) deviceId;
+ DeviceIdVector deviceIds;
+ process_media_recorder_call(env, mr->getRoutedDeviceIds(deviceIds),
+ "java/lang/RuntimeException", "getRoutedDeviceIds failed.");
+ jintArray result;
+ result = env->NewIntArray(deviceIds.size());
+ if (result == NULL) {
+ return NULL;
+ }
+ jint* values = env->GetIntArrayElements(result, 0);
+ for (unsigned int i = 0; i < deviceIds.size(); i++) {
+ values[i++] = static_cast<jint>(deviceIds[i]);
+ }
+ env->ReleaseIntArrayElements(result, values, 0);
+ return result;
}
static void
@@ -880,7 +890,8 @@
{"native_getMetrics", "()Landroid/os/PersistableBundle;", (void *)android_media_MediaRecorder_native_getMetrics},
{"native_setInputDevice", "(I)Z", (void *)android_media_MediaRecorder_setInputDevice},
- {"native_getRoutedDeviceId", "()I", (void *)android_media_MediaRecorder_getRoutedDeviceId},
+ {"native_getRoutedDeviceIds", "()[I",
+ (void *)android_media_MediaRecorder_getRoutedDeviceIds},
{"native_enableDeviceCallback", "(Z)V", (void *)android_media_MediaRecorder_enableDeviceCallback},
{"native_getActiveMicrophones", "(Ljava/util/ArrayList;)I", (void *)android_media_MediaRecord_getActiveMicrophones},
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index 80ca4f2..2fe069a 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -2940,10 +2940,10 @@
break;
}
case FrontendStatus::Tag::standardExt: {
- jfieldID field = env->GetFieldID(clazz, "mStandardExt",
- "Landroid/media/tv/tuner/frontend/StandardExt;");
+ jfieldID field = env->GetFieldID(clazz, "mStandardExtension",
+ "Landroid/media/tv/tuner/frontend/StandardExtension;");
ScopedLocalRef standardExtClazz(env,
- env->FindClass("android/media/tv/tuner/frontend/StandardExt"));
+ env->FindClass("android/media/tv/tuner/frontend/StandardExtension"));
jmethodID initStandardExt = env->GetMethodID(standardExtClazz.get(), "<init>",
"(II)V");
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java
index ac85ab7..88c1c43 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java
@@ -257,6 +257,16 @@
public void onRepeatingRequestError(long lastFrameNumber, int repeatingRequestId) {
// TODO Auto-generated method stub
}
+
+ /*
+ * (non-Javadoc)
+ * @see android.hardware.camera2.ICameraDeviceCallbacks#onClientSharedAccessPriorityChanged
+ */
+ @Override
+ public void onClientSharedAccessPriorityChanged(boolean primaryClient) {
+ // TODO Auto-generated method stub
+ }
+
}
@SmallTest
@@ -276,7 +286,7 @@
0 /*oomScoreOffset*/,
getContext().getApplicationInfo().targetSdkVersion,
ICameraService.ROTATION_OVERRIDE_NONE, clientAttribution,
- DEVICE_POLICY_DEFAULT);
+ DEVICE_POLICY_DEFAULT, false/*sharedMode*/);
assertNotNull(String.format("Camera %s was null", cameraId), cameraUser);
Log.v(TAG, String.format("Camera %s connected", cameraId));
@@ -320,6 +330,13 @@
Log.v(TAG, String.format("Camera " + cameraId + " torch strength level changed to "
+ torchStrength ));
}
+ @Override
+ public void onCameraOpenedInSharedMode(String cameraId, String clientPackageName,
+ int deviceId, boolean primaryClient) {
+ Log.v(TAG, "Camera " + cameraId + " is opened in shared mode by "
+ + "client package " + clientPackageName + " as primary client="
+ + primaryClient);
+ }
}
/**
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java
index 35ad924..3758c51 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java
@@ -175,6 +175,15 @@
public void onRepeatingRequestError(long lastFrameNumber, int repeatingRequestId) {
// TODO Auto-generated method stub
}
+
+ /**
+ * (non-Javadoc)
+ * @see android.hardware.camera2.ICameraDeviceCallbacks#onClientSharedAccessPriorityChanged
+ */
+ @Override
+ public void onClientSharedAccessPriorityChanged(boolean primaryClient) {
+ // TODO Auto-generated method stub
+ }
}
class IsMetadataNotEmpty implements ArgumentMatcher<CameraMetadataNative> {
@@ -250,7 +259,8 @@
mCameraUser = mUtils.getCameraService().connectDevice(mMockCb, mCameraId,
/*oomScoreOffset*/0, getContext().getApplicationInfo().targetSdkVersion,
- ICameraService.ROTATION_OVERRIDE_NONE, clientAttribution, DEVICE_POLICY_DEFAULT);
+ ICameraService.ROTATION_OVERRIDE_NONE, clientAttribution, DEVICE_POLICY_DEFAULT,
+ /*sharedMode*/false);
assertNotNull(String.format("Camera %s was null", mCameraId), mCameraUser);
mHandlerThread = new HandlerThread(TAG);
mHandlerThread.start();
diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt
index a046057..6b41ddd 100644
--- a/native/android/libandroid.map.txt
+++ b/native/android/libandroid.map.txt
@@ -361,6 +361,9 @@
APerformanceHint_setThreads; # introduced=UpsideDownCake
APerformanceHint_setPreferPowerEfficiency; # introduced=VanillaIceCream
APerformanceHint_reportActualWorkDuration2; # introduced=VanillaIceCream
+ APerformanceHint_notifyWorkloadIncrease; # introduced=36
+ APerformanceHint_notifyWorkloadReset; # introduced=36
+ APerformanceHint_borrowSessionFromJava; # introduced=36
AWorkDuration_create; # introduced=VanillaIceCream
AWorkDuration_release; # introduced=VanillaIceCream
AWorkDuration_setWorkPeriodStartTimestampNanos; # introduced=VanillaIceCream
@@ -379,6 +382,10 @@
APerformanceHint_getThreadIds;
APerformanceHint_createSessionInternal;
APerformanceHint_setUseFMQForTesting;
+ APerformanceHint_getRateLimiterPropertiesForTesting;
+ APerformanceHint_setUseNewLoadHintBehaviorForTesting;
+ APerformanceHint_closeSessionFromJava;
+ APerformanceHint_createSessionFromJava;
extern "C++" {
ASurfaceControl_registerSurfaceStatsListener*;
ASurfaceControl_unregisterSurfaceStatsListener*;
diff --git a/native/android/performance_hint.cpp b/native/android/performance_hint.cpp
index 15f77ce..bc1945e 100644
--- a/native/android/performance_hint.cpp
+++ b/native/android/performance_hint.cpp
@@ -33,12 +33,15 @@
#include <android/performance_hint.h>
#include <android/trace.h>
#include <android_os.h>
+#include <cutils/trace.h>
#include <fmq/AidlMessageQueue.h>
#include <inttypes.h>
+#include <jni_wrappers.h>
#include <performance_hint_private.h>
#include <utils/SystemClock.h>
#include <chrono>
+#include <format>
#include <future>
#include <set>
#include <utility>
@@ -63,6 +66,22 @@
constexpr int64_t SEND_HINT_TIMEOUT = std::chrono::nanoseconds(100ms).count();
struct AWorkDuration : public hal::WorkDuration {};
+// A pair of values that determine the behavior of the
+// load hint rate limiter, to only allow "X hints every Y seconds"
+constexpr double kLoadHintInterval = std::chrono::nanoseconds(2s).count();
+constexpr double kMaxLoadHintsPerInterval = 20;
+constexpr double kReplenishRate = kMaxLoadHintsPerInterval / kLoadHintInterval;
+bool kForceNewHintBehavior = false;
+
+template <class T>
+constexpr int32_t enum_size() {
+ return static_cast<int32_t>(*(ndk::enum_range<T>().end() - 1)) + 1;
+}
+
+bool useNewLoadHintBehavior() {
+ return android::os::adpf_use_load_hints() || kForceNewHintBehavior;
+}
+
// Shared lock for the whole PerformanceHintManager and sessions
static std::mutex sHintMutex = std::mutex{};
class FMQWrapper {
@@ -76,7 +95,8 @@
hal::WorkDuration* durations, size_t count) REQUIRES(sHintMutex);
bool updateTargetWorkDuration(std::optional<hal::SessionConfig>& config,
int64_t targetDurationNanos) REQUIRES(sHintMutex);
- bool sendHint(std::optional<hal::SessionConfig>& config, SessionHint hint) REQUIRES(sHintMutex);
+ bool sendHints(std::optional<hal::SessionConfig>& config, std::vector<hal::SessionHint>& hint,
+ int64_t now) REQUIRES(sHintMutex);
bool setMode(std::optional<hal::SessionConfig>& config, hal::SessionMode, bool enabled)
REQUIRES(sHintMutex);
void setToken(ndk::SpAIBinder& token);
@@ -86,10 +106,11 @@
private:
template <HalChannelMessageContents::Tag T, bool urgent = false,
class C = HalChannelMessageContents::_at<T>>
- bool sendMessages(std::optional<hal::SessionConfig>& config, C* message, size_t count = 1)
- REQUIRES(sHintMutex);
+ bool sendMessages(std::optional<hal::SessionConfig>& config, C* message, size_t count = 1,
+ int64_t now = ::android::uptimeNanos()) REQUIRES(sHintMutex);
template <HalChannelMessageContents::Tag T, class C = HalChannelMessageContents::_at<T>>
- void writeBuffer(C* message, hal::SessionConfig& config, size_t count) REQUIRES(sHintMutex);
+ void writeBuffer(C* message, hal::SessionConfig& config, size_t count, int64_t now)
+ REQUIRES(sHintMutex);
bool isActiveLocked() REQUIRES(sHintMutex);
bool updatePersistentTransaction() REQUIRES(sHintMutex);
@@ -117,9 +138,14 @@
APerformanceHintSession* createSession(const int32_t* threadIds, size_t size,
int64_t initialTargetWorkDurationNanos,
- hal::SessionTag tag = hal::SessionTag::APP);
+ hal::SessionTag tag = hal::SessionTag::APP,
+ bool isJava = false);
+ APerformanceHintSession* getSessionFromJava(JNIEnv* _Nonnull env, jobject _Nonnull sessionObj);
+
int64_t getPreferredRateNanos() const;
FMQWrapper& getFMQWrapper();
+ bool canSendLoadHints(std::vector<hal::SessionHint>& hints, int64_t now) REQUIRES(sHintMutex);
+ void initJava(JNIEnv* _Nonnull env);
private:
// Necessary to create an empty binder object
@@ -138,24 +164,32 @@
ndk::SpAIBinder mToken;
const int64_t mPreferredRateNanos;
FMQWrapper mFMQWrapper;
+ double mHintBudget = kMaxLoadHintsPerInterval;
+ int64_t mLastBudgetReplenish = 0;
+ bool mJavaInitialized = false;
+ jclass mJavaSessionClazz;
+ jfieldID mJavaSessionNativePtr;
};
struct APerformanceHintSession {
public:
APerformanceHintSession(std::shared_ptr<IHintManager> hintManager,
std::shared_ptr<IHintSession> session, int64_t preferredRateNanos,
- int64_t targetDurationNanos,
+ int64_t targetDurationNanos, bool isJava,
std::optional<hal::SessionConfig> sessionConfig);
APerformanceHintSession() = delete;
~APerformanceHintSession();
int updateTargetWorkDuration(int64_t targetDurationNanos);
int reportActualWorkDuration(int64_t actualDurationNanos);
- int sendHint(SessionHint hint);
+ int sendHints(std::vector<hal::SessionHint>& hints, int64_t now, const char* debugName);
+ int notifyWorkloadIncrease(bool cpu, bool gpu, const char* debugName);
+ int notifyWorkloadReset(bool cpu, bool gpu, const char* debugName);
int setThreads(const int32_t* threadIds, size_t size);
int getThreadIds(int32_t* const threadIds, size_t* size);
int setPreferPowerEfficiency(bool enabled);
int reportActualWorkDuration(AWorkDuration* workDuration);
+ bool isJava();
private:
friend struct APerformanceHintManager;
@@ -173,9 +207,13 @@
// Last target hit timestamp
int64_t mLastTargetMetTimestamp GUARDED_BY(sHintMutex);
// Last hint reported from sendHint indexed by hint value
+ // This is only used by the old rate limiter impl and is replaced
+ // with the new rate limiter under a flag
std::vector<int64_t> mLastHintSentTimestamp GUARDED_BY(sHintMutex);
// Cached samples
std::vector<hal::WorkDuration> mActualWorkDurations GUARDED_BY(sHintMutex);
+ // Is this session backing an SDK wrapper object
+ const bool mIsJava;
std::string mSessionName;
static int64_t sIDCounter GUARDED_BY(sHintMutex);
// The most recent set of thread IDs
@@ -255,9 +293,24 @@
return new APerformanceHintManager(manager, preferredRateNanos);
}
+bool APerformanceHintManager::canSendLoadHints(std::vector<hal::SessionHint>& hints, int64_t now) {
+ mHintBudget =
+ std::max(kMaxLoadHintsPerInterval,
+ mHintBudget +
+ static_cast<double>(now - mLastBudgetReplenish) * kReplenishRate);
+ mLastBudgetReplenish = now;
+
+ // If this youngest timestamp isn't older than the timeout time, we can't send
+ if (hints.size() > mHintBudget) {
+ return false;
+ }
+ mHintBudget -= hints.size();
+ return true;
+}
+
APerformanceHintSession* APerformanceHintManager::createSession(
const int32_t* threadIds, size_t size, int64_t initialTargetWorkDurationNanos,
- hal::SessionTag tag) {
+ hal::SessionTag tag, bool isJava) {
std::vector<int32_t> tids(threadIds, threadIds + size);
std::shared_ptr<IHintSession> session;
ndk::ScopedAStatus ret;
@@ -270,7 +323,7 @@
return nullptr;
}
auto out = new APerformanceHintSession(mHintManager, std::move(session), mPreferredRateNanos,
- initialTargetWorkDurationNanos,
+ initialTargetWorkDurationNanos, isJava,
sessionConfig.id == -1
? std::nullopt
: std::make_optional<hal::SessionConfig>(
@@ -282,6 +335,18 @@
return out;
}
+APerformanceHintSession* APerformanceHintManager::getSessionFromJava(JNIEnv* env,
+ jobject sessionObj) {
+ initJava(env);
+ LOG_ALWAYS_FATAL_IF(!env->IsInstanceOf(sessionObj, mJavaSessionClazz),
+ "Wrong java type passed to APerformanceHint_getSessionFromJava");
+ APerformanceHintSession* out = reinterpret_cast<APerformanceHintSession*>(
+ env->GetLongField(sessionObj, mJavaSessionNativePtr));
+ LOG_ALWAYS_FATAL_IF(out == nullptr, "Java-wrapped native hint session is nullptr");
+ LOG_ALWAYS_FATAL_IF(!out->isJava(), "Unmanaged native hint session returned from Java SDK");
+ return out;
+}
+
int64_t APerformanceHintManager::getPreferredRateNanos() const {
return mPreferredRateNanos;
}
@@ -290,15 +355,23 @@
return mFMQWrapper;
}
+void APerformanceHintManager::initJava(JNIEnv* _Nonnull env) {
+ if (mJavaInitialized) {
+ return;
+ }
+ jclass sessionClazz = FindClassOrDie(env, "android/os/PerformanceHintManager$Session");
+ mJavaSessionClazz = MakeGlobalRefOrDie(env, sessionClazz);
+ mJavaSessionNativePtr = GetFieldIDOrDie(env, mJavaSessionClazz, "mNativeSessionPtr", "J");
+ mJavaInitialized = true;
+}
+
// ===================================== APerformanceHintSession implementation
-constexpr int kNumEnums =
- ndk::enum_range<hal::SessionHint>().end() - ndk::enum_range<hal::SessionHint>().begin();
-
+constexpr int kNumEnums = enum_size<hal::SessionHint>();
APerformanceHintSession::APerformanceHintSession(std::shared_ptr<IHintManager> hintManager,
std::shared_ptr<IHintSession> session,
int64_t preferredRateNanos,
- int64_t targetDurationNanos,
+ int64_t targetDurationNanos, bool isJava,
std::optional<hal::SessionConfig> sessionConfig)
: mHintManager(hintManager),
mHintSession(std::move(session)),
@@ -307,6 +380,7 @@
mFirstTargetMetTimestamp(0),
mLastTargetMetTimestamp(0),
mLastHintSentTimestamp(std::vector<int64_t>(kNumEnums, 0)),
+ mIsJava(isJava),
mSessionConfig(sessionConfig) {
if (sessionConfig->id > INT32_MAX) {
ALOGE("Session ID too large, must fit 32-bit integer");
@@ -361,31 +435,87 @@
return reportActualWorkDurationInternal(static_cast<AWorkDuration*>(&workDuration));
}
-int APerformanceHintSession::sendHint(SessionHint hint) {
+bool APerformanceHintSession::isJava() {
+ return mIsJava;
+}
+
+int APerformanceHintSession::sendHints(std::vector<hal::SessionHint>& hints, int64_t now,
+ const char*) {
std::scoped_lock lock(sHintMutex);
- if (hint < 0 || hint >= static_cast<int32_t>(mLastHintSentTimestamp.size())) {
- ALOGE("%s: invalid session hint %d", __FUNCTION__, hint);
+ if (hints.empty()) {
return EINVAL;
}
- int64_t now = uptimeNanos();
-
- // Limit sendHint to a pre-detemined rate for safety
- if (now < (mLastHintSentTimestamp[hint] + SEND_HINT_TIMEOUT)) {
- return 0;
- }
-
- if (!getFMQ().sendHint(mSessionConfig, hint)) {
- ndk::ScopedAStatus ret = mHintSession->sendHint(hint);
-
- if (!ret.isOk()) {
- ALOGE("%s: HintSession sendHint failed: %s", __FUNCTION__, ret.getMessage());
- return EPIPE;
+ for (auto&& hint : hints) {
+ if (static_cast<int32_t>(hint) < 0 || static_cast<int32_t>(hint) >= kNumEnums) {
+ ALOGE("%s: invalid session hint %d", __FUNCTION__, hint);
+ return EINVAL;
}
}
- mLastHintSentTimestamp[hint] = now;
+
+ if (useNewLoadHintBehavior()) {
+ if (!APerformanceHintManager::getInstance()->canSendLoadHints(hints, now)) {
+ return EBUSY;
+ }
+ }
+ // keep old rate limiter behavior for legacy flag
+ else {
+ for (auto&& hint : hints) {
+ if (now < (mLastHintSentTimestamp[static_cast<int32_t>(hint)] + SEND_HINT_TIMEOUT)) {
+ return EBUSY;
+ }
+ }
+ }
+
+ if (!getFMQ().sendHints(mSessionConfig, hints, now)) {
+ for (auto&& hint : hints) {
+ ndk::ScopedAStatus ret = mHintSession->sendHint(static_cast<int32_t>(hint));
+
+ if (!ret.isOk()) {
+ ALOGE("%s: HintSession sendHint failed: %s", __FUNCTION__, ret.getMessage());
+ return EPIPE;
+ }
+ }
+ }
+
+ if (!useNewLoadHintBehavior()) {
+ for (auto&& hint : hints) {
+ mLastHintSentTimestamp[static_cast<int32_t>(hint)] = now;
+ }
+ }
+
+ if (ATrace_isEnabled()) {
+ ATRACE_INSTANT("Sending load hint");
+ }
+
return 0;
}
+int APerformanceHintSession::notifyWorkloadIncrease(bool cpu, bool gpu, const char* debugName) {
+ std::vector<hal::SessionHint> hints(2);
+ hints.clear();
+ if (cpu) {
+ hints.push_back(hal::SessionHint::CPU_LOAD_UP);
+ }
+ if (gpu) {
+ hints.push_back(hal::SessionHint::GPU_LOAD_UP);
+ }
+ int64_t now = ::android::uptimeNanos();
+ return sendHints(hints, now, debugName);
+}
+
+int APerformanceHintSession::notifyWorkloadReset(bool cpu, bool gpu, const char* debugName) {
+ std::vector<hal::SessionHint> hints(2);
+ hints.clear();
+ if (cpu) {
+ hints.push_back(hal::SessionHint::CPU_LOAD_RESET);
+ }
+ if (gpu) {
+ hints.push_back(hal::SessionHint::GPU_LOAD_RESET);
+ }
+ int64_t now = ::android::uptimeNanos();
+ return sendHints(hints, now, debugName);
+}
+
int APerformanceHintSession::setThreads(const int32_t* threadIds, size_t size) {
if (size == 0) {
ALOGE("%s: the list of thread ids must not be empty.", __FUNCTION__);
@@ -565,24 +695,25 @@
}
template <HalChannelMessageContents::Tag T, class C>
-void FMQWrapper::writeBuffer(C* message, hal::SessionConfig& config, size_t) {
- new (mFmqTransaction.getSlot(0)) hal::ChannelMessage{
- .sessionID = static_cast<int32_t>(config.id),
- .timeStampNanos = ::android::uptimeNanos(),
- .data = HalChannelMessageContents::make<T, C>(std::move(*message)),
- };
+void FMQWrapper::writeBuffer(C* message, hal::SessionConfig& config, size_t count, int64_t now) {
+ for (size_t i = 0; i < count; ++i) {
+ new (mFmqTransaction.getSlot(i)) hal::ChannelMessage{
+ .sessionID = static_cast<int32_t>(config.id),
+ .timeStampNanos = now,
+ .data = HalChannelMessageContents::make<T, C>(std::move(*(message + i))),
+ };
+ }
}
template <>
void FMQWrapper::writeBuffer<HalChannelMessageContents::workDuration>(hal::WorkDuration* messages,
hal::SessionConfig& config,
- size_t count) {
+ size_t count, int64_t now) {
for (size_t i = 0; i < count; ++i) {
hal::WorkDuration& message = messages[i];
new (mFmqTransaction.getSlot(i)) hal::ChannelMessage{
.sessionID = static_cast<int32_t>(config.id),
- .timeStampNanos =
- (i == count - 1) ? ::android::uptimeNanos() : message.timeStampNanos,
+ .timeStampNanos = (i == count - 1) ? now : message.timeStampNanos,
.data = HalChannelMessageContents::make<HalChannelMessageContents::workDuration,
hal::WorkDurationFixedV1>({
.durationNanos = message.cpuDurationNanos,
@@ -595,7 +726,8 @@
}
template <HalChannelMessageContents::Tag T, bool urgent, class C>
-bool FMQWrapper::sendMessages(std::optional<hal::SessionConfig>& config, C* message, size_t count) {
+bool FMQWrapper::sendMessages(std::optional<hal::SessionConfig>& config, C* message, size_t count,
+ int64_t now) {
if (!isActiveLocked() || !config.has_value() || mCorrupted) {
return false;
}
@@ -609,7 +741,7 @@
return false;
}
}
- writeBuffer<T, C>(message, *config, count);
+ writeBuffer<T, C>(message, *config, count, now);
mQueue->commitWrite(count);
mEventFlag->wake(mWriteMask);
// Re-create the persistent transaction after writing
@@ -641,10 +773,9 @@
return sendMessages<HalChannelMessageContents::targetDuration>(config, &targetDurationNanos);
}
-bool FMQWrapper::sendHint(std::optional<hal::SessionConfig>& config, SessionHint hint) {
- return sendMessages<HalChannelMessageContents::hint>(config,
- reinterpret_cast<hal::SessionHint*>(
- &hint));
+bool FMQWrapper::sendHints(std::optional<hal::SessionConfig>& config,
+ std::vector<hal::SessionHint>& hints, int64_t now) {
+ return sendMessages<HalChannelMessageContents::hint>(config, hints.data(), hints.size(), now);
}
bool FMQWrapper::setMode(std::optional<hal::SessionConfig>& config, hal::SessionMode mode,
@@ -733,6 +864,22 @@
static_cast<hal::SessionTag>(tag));
}
+APerformanceHintSession* APerformanceHint_createSessionFromJava(
+ APerformanceHintManager* manager, const int32_t* threadIds, size_t size,
+ int64_t initialTargetWorkDurationNanos) {
+ VALIDATE_PTR(manager)
+ VALIDATE_PTR(threadIds)
+ return manager->createSession(threadIds, size, initialTargetWorkDurationNanos,
+ hal::SessionTag::APP, true);
+}
+
+APerformanceHintSession* APerformanceHint_borrowSessionFromJava(JNIEnv* env,
+ jobject sessionObj) {
+ VALIDATE_PTR(env)
+ VALIDATE_PTR(sessionObj)
+ return APerformanceHintManager::getInstance()->getSessionFromJava(env, sessionObj);
+}
+
int64_t APerformanceHint_getPreferredUpdateRateNanos(APerformanceHintManager* manager) {
VALIDATE_PTR(manager)
return manager->getPreferredRateNanos();
@@ -753,12 +900,24 @@
void APerformanceHint_closeSession(APerformanceHintSession* session) {
VALIDATE_PTR(session)
+ if (session->isJava()) {
+ LOG_ALWAYS_FATAL("%s: Java-owned PerformanceHintSession cannot be closed in native",
+ __FUNCTION__);
+ return;
+ }
+ delete session;
+}
+
+void APerformanceHint_closeSessionFromJava(APerformanceHintSession* session) {
+ VALIDATE_PTR(session)
delete session;
}
int APerformanceHint_sendHint(APerformanceHintSession* session, SessionHint hint) {
VALIDATE_PTR(session)
- return session->sendHint(hint);
+ std::vector<hal::SessionHint> hints{static_cast<hal::SessionHint>(hint)};
+ int64_t now = ::android::uptimeNanos();
+ return session->sendHints(hints, now, "HWUI hint");
}
int APerformanceHint_setThreads(APerformanceHintSession* session, const pid_t* threadIds,
@@ -791,6 +950,26 @@
return session->reportActualWorkDuration(workDurationPtr);
}
+int APerformanceHint_notifyWorkloadIncrease(APerformanceHintSession* session, bool cpu, bool gpu,
+ const char* debugName) {
+ VALIDATE_PTR(session)
+ VALIDATE_PTR(debugName)
+ if (!useNewLoadHintBehavior()) {
+ return ENOTSUP;
+ }
+ return session->notifyWorkloadIncrease(cpu, gpu, debugName);
+}
+
+int APerformanceHint_notifyWorkloadReset(APerformanceHintSession* session, bool cpu, bool gpu,
+ const char* debugName) {
+ VALIDATE_PTR(session)
+ VALIDATE_PTR(debugName)
+ if (!useNewLoadHintBehavior()) {
+ return ENOTSUP;
+ }
+ return session->notifyWorkloadReset(cpu, gpu, debugName);
+}
+
AWorkDuration* AWorkDuration_create() {
return new AWorkDuration();
}
@@ -838,3 +1017,13 @@
void APerformanceHint_setUseFMQForTesting(bool enabled) {
gForceFMQEnabled = enabled;
}
+
+void APerformanceHint_getRateLimiterPropertiesForTesting(int32_t* maxLoadHintsPerInterval,
+ int64_t* loadHintInterval) {
+ *maxLoadHintsPerInterval = kMaxLoadHintsPerInterval;
+ *loadHintInterval = kLoadHintInterval;
+}
+
+void APerformanceHint_setUseNewLoadHintBehaviorForTesting(bool newBehavior) {
+ kForceNewHintBehavior = newBehavior;
+}
diff --git a/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp b/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp
index 9de3a6f..f707a0e 100644
--- a/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp
+++ b/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp
@@ -66,6 +66,18 @@
std::optional<hal::ChannelConfig>* _aidl_return),
(override));
MOCK_METHOD(ScopedAStatus, closeSessionChannel, (), (override));
+ MOCK_METHOD(ScopedAStatus, getCpuHeadroom,
+ (const ::aidl::android::os::CpuHeadroomParamsInternal& in_params,
+ std::vector<float>* _aidl_return),
+ (override));
+ MOCK_METHOD(ScopedAStatus, getCpuHeadroomMinIntervalMillis, (int64_t* _aidl_return),
+ (override));
+ MOCK_METHOD(ScopedAStatus, getGpuHeadroom,
+ (const ::aidl::android::os::GpuHeadroomParamsInternal& in_params,
+ float* _aidl_return),
+ (override));
+ MOCK_METHOD(ScopedAStatus, getGpuHeadroomMinIntervalMillis, (int64_t* _aidl_return),
+ (override));
MOCK_METHOD(SpAIBinder, asBinder, (), (override));
MOCK_METHOD(bool, isRemote, (), (override));
};
@@ -90,7 +102,10 @@
public:
void SetUp() override {
mMockIHintManager = ndk::SharedRefBase::make<NiceMock<MockIHintManager>>();
+ APerformanceHint_getRateLimiterPropertiesForTesting(&mMaxLoadHintsPerInterval,
+ &mLoadHintInterval);
APerformanceHint_setIHintManagerForTesting(&mMockIHintManager);
+ APerformanceHint_setUseNewLoadHintBehaviorForTesting(true);
}
void TearDown() override {
@@ -176,6 +191,9 @@
int kMockQueueSize = 20;
bool mUsingFMQ = false;
+ int32_t mMaxLoadHintsPerInterval;
+ int64_t mLoadHintInterval;
+
template <HalChannelMessageContents::Tag T, class C = HalChannelMessageContents::_at<T>>
void expectToReadFromFmq(C expected) {
hal::ChannelMessage readData;
@@ -218,7 +236,6 @@
EXPECT_CALL(*mMockSession, reportActualWorkDuration2(_)).Times(Exactly(1));
result = APerformanceHint_reportActualWorkDuration(session, actualDurationNanos);
EXPECT_EQ(0, result);
-
result = APerformanceHint_updateTargetWorkDuration(session, -1L);
EXPECT_EQ(EINVAL, result);
result = APerformanceHint_reportActualWorkDuration(session, -1L);
@@ -228,18 +245,28 @@
EXPECT_CALL(*mMockSession, sendHint(Eq(hintId))).Times(Exactly(1));
result = APerformanceHint_sendHint(session, hintId);
EXPECT_EQ(0, result);
- usleep(110000); // Sleep for longer than the update timeout.
- EXPECT_CALL(*mMockSession, sendHint(Eq(hintId))).Times(Exactly(1));
- result = APerformanceHint_sendHint(session, hintId);
+ EXPECT_CALL(*mMockSession, sendHint(Eq(SessionHint::CPU_LOAD_UP))).Times(Exactly(1));
+ result = APerformanceHint_notifyWorkloadIncrease(session, true, false, "Test hint");
EXPECT_EQ(0, result);
- // Expect to get rate limited if we try to send faster than the limiter allows
- EXPECT_CALL(*mMockSession, sendHint(Eq(hintId))).Times(Exactly(0));
- result = APerformanceHint_sendHint(session, hintId);
+ EXPECT_CALL(*mMockSession, sendHint(Eq(SessionHint::CPU_LOAD_RESET))).Times(Exactly(1));
+ EXPECT_CALL(*mMockSession, sendHint(Eq(SessionHint::GPU_LOAD_RESET))).Times(Exactly(1));
+ result = APerformanceHint_notifyWorkloadReset(session, true, true, "Test hint");
EXPECT_EQ(0, result);
result = APerformanceHint_sendHint(session, static_cast<SessionHint>(-1));
EXPECT_EQ(EINVAL, result);
+ Mock::VerifyAndClearExpectations(mMockSession.get());
+ for (int i = 0; i < mMaxLoadHintsPerInterval; ++i) {
+ APerformanceHint_sendHint(session, hintId);
+ }
+
+ // Expect to get rate limited if we try to send faster than the limiter allows
+ EXPECT_CALL(*mMockSession, sendHint(_)).Times(Exactly(0));
+ result = APerformanceHint_notifyWorkloadIncrease(session, true, true, "Test hint");
+ EXPECT_EQ(result, EBUSY);
+ EXPECT_CALL(*mMockSession, sendHint(_)).Times(Exactly(0));
+ result = APerformanceHint_notifyWorkloadReset(session, true, true, "Test hint");
EXPECT_CALL(*mMockSession, close()).Times(Exactly(1));
APerformanceHint_closeSession(session);
}
diff --git a/native/graphics/jni/Android.bp b/native/graphics/jni/Android.bp
index 23dd9b7..4180710 100644
--- a/native/graphics/jni/Android.bp
+++ b/native/graphics/jni/Android.bp
@@ -45,8 +45,10 @@
header_libs: [
"jni_headers",
+ "native_headers",
"libhwui_internal_headers",
],
+ export_header_lib_headers: ["native_headers"],
static_libs: [
"libarect",
diff --git a/nfc/api/current.txt b/nfc/api/current.txt
index 00812042..9a7a39f 100644
--- a/nfc/api/current.txt
+++ b/nfc/api/current.txt
@@ -81,11 +81,14 @@
method @FlaggedApi("android.nfc.enable_nfc_reader_option") public boolean isReaderOptionSupported();
method public boolean isSecureNfcEnabled();
method public boolean isSecureNfcSupported();
+ method @FlaggedApi("android.nfc.nfc_check_tag_intent_preference") public boolean isTagIntentAllowed();
+ method @FlaggedApi("android.nfc.nfc_check_tag_intent_preference") public boolean isTagIntentAppPreferenceSupported();
method @FlaggedApi("android.nfc.enable_nfc_charging") public boolean isWlcEnabled();
method @FlaggedApi("android.nfc.enable_nfc_set_discovery_tech") public void resetDiscoveryTechnology(@NonNull android.app.Activity);
method @FlaggedApi("android.nfc.enable_nfc_set_discovery_tech") public void setDiscoveryTechnology(@NonNull android.app.Activity, int, int);
method @FlaggedApi("android.nfc.nfc_observe_mode") public boolean setObserveModeEnabled(boolean);
field public static final String ACTION_ADAPTER_STATE_CHANGED = "android.nfc.action.ADAPTER_STATE_CHANGED";
+ field @FlaggedApi("android.nfc.nfc_check_tag_intent_preference") public static final String ACTION_CHANGE_TAG_INTENT_PREFERENCE = "android.nfc.action.CHANGE_TAG_INTENT_PREFERENCE";
field public static final String ACTION_NDEF_DISCOVERED = "android.nfc.action.NDEF_DISCOVERED";
field @RequiresPermission(android.Manifest.permission.NFC_PREFERRED_PAYMENT_INFO) public static final String ACTION_PREFERRED_PAYMENT_CHANGED = "android.nfc.action.PREFERRED_PAYMENT_CHANGED";
field public static final String ACTION_TAG_DISCOVERED = "android.nfc.action.TAG_DISCOVERED";
diff --git a/nfc/api/system-current.txt b/nfc/api/system-current.txt
index c25f77b..15814ed 100644
--- a/nfc/api/system-current.txt
+++ b/nfc/api/system-current.txt
@@ -11,7 +11,6 @@
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();
method @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public boolean isControllerAlwaysOnSupported();
- method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean isTagIntentAppPreferenceSupported();
method @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public void registerControllerAlwaysOnListener(@NonNull java.util.concurrent.Executor, @NonNull android.nfc.NfcAdapter.ControllerAlwaysOnListener);
method @FlaggedApi("android.nfc.nfc_vendor_cmd") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void registerNfcVendorNciCallback(@NonNull java.util.concurrent.Executor, @NonNull android.nfc.NfcAdapter.NfcVendorNciCallback);
method @FlaggedApi("android.nfc.enable_nfc_charging") public void registerWlcStateListener(@NonNull java.util.concurrent.Executor, @NonNull android.nfc.NfcAdapter.WlcStateListener);
@@ -57,6 +56,7 @@
@FlaggedApi("android.nfc.nfc_oem_extension") public final class NfcOemExtension {
method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void clearPreference();
+ method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public int forceRoutingTableCommit();
method @FlaggedApi("android.nfc.nfc_oem_extension") @NonNull public java.util.List<java.lang.String> getActiveNfceeList();
method @FlaggedApi("android.nfc.nfc_oem_extension") @NonNull @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public android.nfc.RoutingStatus getRoutingStatus();
method @FlaggedApi("android.nfc.nfc_oem_extension") @NonNull @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public java.util.List<android.nfc.NfcRoutingTableEntry> getRoutingTable();
@@ -73,6 +73,9 @@
method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void synchronizeScreenState();
method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void triggerInitialization();
method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void unregisterCallback(@NonNull android.nfc.NfcOemExtension.Callback);
+ field public static final int COMMIT_ROUTING_STATUS_FAILED = 3; // 0x3
+ field public static final int COMMIT_ROUTING_STATUS_FAILED_UPDATE_IN_PROGRESS = 6; // 0x6
+ field public static final int COMMIT_ROUTING_STATUS_OK = 0; // 0x0
field @FlaggedApi("android.nfc.nfc_oem_extension") public static final int DISABLE = 0; // 0x0
field @FlaggedApi("android.nfc.nfc_oem_extension") public static final int ENABLE_DEFAULT = 1; // 0x1
field @FlaggedApi("android.nfc.nfc_oem_extension") public static final int ENABLE_EE = 3; // 0x3
@@ -89,13 +92,15 @@
method public void onBootFinished(int);
method public void onBootStarted();
method public void onCardEmulationActivated(boolean);
- method public void onDisable(@NonNull java.util.function.Consumer<java.lang.Boolean>);
method public void onDisableFinished(int);
+ method public void onDisableRequested(@NonNull java.util.function.Consumer<java.lang.Boolean>);
method public void onDisableStarted();
method public void onEeListenActivated(boolean);
- method public void onEnable(@NonNull java.util.function.Consumer<java.lang.Boolean>);
+ method public void onEeUpdated();
method public void onEnableFinished(int);
+ method public void onEnableRequested(@NonNull java.util.function.Consumer<java.lang.Boolean>);
method public void onEnableStarted();
+ method public void onExtractOemPackages(@NonNull android.nfc.NdefMessage, @NonNull java.util.function.Consumer<java.util.List<java.lang.String>>);
method public void onGetOemAppSearchIntent(@NonNull java.util.List<java.lang.String>, @NonNull java.util.function.Consumer<android.content.Intent>);
method public void onHceEventReceived(int);
method public void onLaunchHceAppChooserActivity(@NonNull String, @NonNull java.util.List<android.nfc.cardemulation.ApduServiceInfo>, @NonNull android.content.ComponentName, @NonNull String);
@@ -106,7 +111,7 @@
method public void onReaderOptionChanged(boolean);
method public void onRfDiscoveryStarted(boolean);
method public void onRfFieldActivated(boolean);
- method public void onRoutingChanged();
+ method public void onRoutingChanged(@NonNull java.util.function.Consumer<java.lang.Boolean>);
method public void onRoutingTableFull();
method public void onStateUpdated(int);
method public void onTagConnected(boolean);
@@ -183,6 +188,12 @@
method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public java.util.List<android.nfc.cardemulation.ApduServiceInfo> getServices(@NonNull String, int);
method @FlaggedApi("android.nfc.nfc_override_recover_routing_table") public void overrideRoutingTable(@NonNull android.app.Activity, int, int);
method @FlaggedApi("android.nfc.nfc_override_recover_routing_table") public void recoverRoutingTable(@NonNull android.app.Activity);
+ method @FlaggedApi("android.nfc.nfc_set_service_enabled_for_category_other") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public int setServiceEnabledForCategoryOther(@NonNull android.content.ComponentName, boolean);
+ field @FlaggedApi("android.nfc.nfc_set_service_enabled_for_category_other") public static final int SET_SERVICE_ENABLED_STATUS_FAILURE_ALREADY_SET = 3; // 0x3
+ field @FlaggedApi("android.nfc.nfc_set_service_enabled_for_category_other") public static final int SET_SERVICE_ENABLED_STATUS_FAILURE_FEATURE_UNSUPPORTED = 1; // 0x1
+ field @FlaggedApi("android.nfc.nfc_set_service_enabled_for_category_other") public static final int SET_SERVICE_ENABLED_STATUS_FAILURE_INVALID_SERVICE = 2; // 0x2
+ field @FlaggedApi("android.nfc.nfc_set_service_enabled_for_category_other") public static final int SET_SERVICE_ENABLED_STATUS_FAILURE_UNKNOWN_ERROR = 4; // 0x4
+ field @FlaggedApi("android.nfc.nfc_set_service_enabled_for_category_other") public static final int SET_SERVICE_ENABLED_STATUS_OK = 0; // 0x0
}
}
diff --git a/nfc/java/android/nfc/INfcAdapter.aidl b/nfc/java/android/nfc/INfcAdapter.aidl
index 40fd068..a08b55f 100644
--- a/nfc/java/android/nfc/INfcAdapter.aidl
+++ b/nfc/java/android/nfc/INfcAdapter.aidl
@@ -120,4 +120,6 @@
boolean isTagPresent();
List<Entry> getRoutingTableEntryList();
void indicateDataMigration(boolean inProgress, String pkg);
+ int commitRouting();
+ boolean isTagIntentAllowed(in String pkg, in int Userid);
}
diff --git a/nfc/java/android/nfc/INfcCardEmulation.aidl b/nfc/java/android/nfc/INfcCardEmulation.aidl
index 5e2e92d..633d8bf 100644
--- a/nfc/java/android/nfc/INfcCardEmulation.aidl
+++ b/nfc/java/android/nfc/INfcCardEmulation.aidl
@@ -47,7 +47,7 @@
boolean unsetPreferredService();
boolean supportsAidPrefixRegistration();
ApduServiceInfo getPreferredPaymentService(int userHandle);
- boolean setServiceEnabledForCategoryOther(int userHandle, in ComponentName app, boolean status);
+ int setServiceEnabledForCategoryOther(int userHandle, in ComponentName app, boolean status);
boolean isDefaultPaymentRegistered();
void overrideRoutingTable(int userHandle, String protocol, String technology, in String pkg);
diff --git a/nfc/java/android/nfc/INfcOemExtensionCallback.aidl b/nfc/java/android/nfc/INfcOemExtensionCallback.aidl
index fb793b0..e5eac0b 100644
--- a/nfc/java/android/nfc/INfcOemExtensionCallback.aidl
+++ b/nfc/java/android/nfc/INfcOemExtensionCallback.aidl
@@ -41,17 +41,19 @@
void onEnableFinished(int status);
void onDisableFinished(int status);
void onTagDispatch(in ResultReceiver isSkipped);
- void onRoutingChanged();
+ void onRoutingChanged(in ResultReceiver isSkipped);
void onHceEventReceived(int action);
void onReaderOptionChanged(boolean enabled);
void onCardEmulationActivated(boolean isActivated);
void onRfFieldActivated(boolean isActivated);
void onRfDiscoveryStarted(boolean isDiscoveryStarted);
void onEeListenActivated(boolean isActivated);
+ void onEeUpdated();
void onGetOemAppSearchIntent(in List<String> firstPackage, in ResultReceiver intentConsumer);
void onNdefMessage(in Tag tag, in NdefMessage message, in ResultReceiver hasOemExecutableContent);
void onLaunchHceAppChooserActivity(in String selectedAid, in List<ApduServiceInfo> services, in ComponentName failedComponent, in String category);
void onLaunchHceTapAgainActivity(in ApduServiceInfo service, in String category);
void onRoutingTableFull();
void onLogEventNotified(in OemLogItems item);
+ void onExtractOemPackages(in NdefMessage message, in ResultReceiver packageReceiver);
}
diff --git a/nfc/java/android/nfc/NfcAdapter.java b/nfc/java/android/nfc/NfcAdapter.java
index c5d8191..056844f 100644
--- a/nfc/java/android/nfc/NfcAdapter.java
+++ b/nfc/java/android/nfc/NfcAdapter.java
@@ -49,6 +49,7 @@
import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.util.Log;
import java.io.IOException;
@@ -2505,22 +2506,22 @@
}
/**
- * Checks if the device supports Tag application preference.
+ * Checks if the device supports Tag Intent App Preference functionality.
+ *
+ * When supported, {@link #ACTION_NDEF_DISCOVERED}, {@link #ACTION_TECH_DISCOVERED} or
+ * {@link #ACTION_TAG_DISCOVERED} will not be dispatched to an Activity if
+ * {@link isTagIntentAllowed} returns {@code false}.
*
* @return {@code true} if the device supports Tag application preference, {@code false}
* otherwise
* @throws UnsupportedOperationException if FEATURE_NFC is unavailable
- *
- * @hide
*/
- @SystemApi
- @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
+ @FlaggedApi(Flags.FLAG_NFC_CHECK_TAG_INTENT_PREFERENCE)
public boolean isTagIntentAppPreferenceSupported() {
if (!sHasNfcFeature) {
throw new UnsupportedOperationException();
}
return callServiceReturn(() -> sService.isTagIntentAppPreferenceSupported(), false);
-
}
/**
@@ -2895,4 +2896,42 @@
}
return mNfcOemExtension;
}
+
+ /**
+ * Activity action: Bring up the settings page that allows the user to enable or disable tag
+ * intent reception for apps.
+ *
+ * <p>This will direct user to the settings page shows a list that asks users whether
+ * they want to allow or disallow the package to start an activity when a tag is discovered.
+ *
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ @FlaggedApi(Flags.FLAG_NFC_CHECK_TAG_INTENT_PREFERENCE)
+ public static final String ACTION_CHANGE_TAG_INTENT_PREFERENCE =
+ "android.nfc.action.CHANGE_TAG_INTENT_PREFERENCE";
+
+ /**
+ * Checks whether the user has disabled the calling app from receiving NFC tag intents.
+ *
+ * <p>This method checks whether the caller package name is either not present in the user
+ * disabled list or is explicitly allowed by the user.
+ *
+ * @return {@code true} if an app is either not present in the list or is added to the list
+ * with the flag set to {@code true}. Otherwise, it returns {@code false}.
+ * It also returns {@code true} if {@link isTagIntentAppPreferenceSupported} returns
+ * {@code false}.
+ *
+ * @throws UnsupportedOperationException if FEATURE_NFC is unavailable.
+ */
+ @FlaggedApi(Flags.FLAG_NFC_CHECK_TAG_INTENT_PREFERENCE)
+ public boolean isTagIntentAllowed() {
+ if (!sHasNfcFeature) {
+ throw new UnsupportedOperationException();
+ }
+ if (!isTagIntentAppPreferenceSupported()) {
+ return true;
+ }
+ return callServiceReturn(() -> sService.isTagIntentAllowed(mContext.getPackageName(),
+ UserHandle.myUserId()), false);
+ }
}
diff --git a/nfc/java/android/nfc/NfcOemExtension.java b/nfc/java/android/nfc/NfcOemExtension.java
index 57ee981..9ed678f 100644
--- a/nfc/java/android/nfc/NfcOemExtension.java
+++ b/nfc/java/android/nfc/NfcOemExtension.java
@@ -23,11 +23,11 @@
import android.Manifest;
import android.annotation.CallbackExecutor;
+import android.annotation.DurationMillisLong;
import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
-import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.content.ComponentName;
import android.content.Context;
@@ -194,6 +194,30 @@
public @interface StatusCode {}
/**
+ * Routing commit succeeded.
+ */
+ public static final int COMMIT_ROUTING_STATUS_OK = 0;
+ /**
+ * Routing commit failed.
+ */
+ public static final int COMMIT_ROUTING_STATUS_FAILED = 3;
+ /**
+ * Routing commit failed due to the update is in progress.
+ */
+ public static final int COMMIT_ROUTING_STATUS_FAILED_UPDATE_IN_PROGRESS = 6;
+
+ /**
+ * Status codes returned when calling {@link #forceRoutingTableCommit()}
+ * @hide
+ */
+ @IntDef(prefix = "COMMIT_ROUTING_STATUS_", value = {
+ COMMIT_ROUTING_STATUS_OK,
+ COMMIT_ROUTING_STATUS_FAILED,
+ COMMIT_ROUTING_STATUS_FAILED_UPDATE_IN_PROGRESS,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface CommitRoutingStatusCode {}
+ /**
* Interface for Oem extensions for NFC.
*/
public interface Callback {
@@ -233,8 +257,7 @@
* {@link Boolean#TRUE}, otherwise call with {@link Boolean#FALSE}.
* false if NFC cannot be enabled at this time.
*/
- @SuppressLint("MethodNameTense")
- void onEnable(@NonNull Consumer<Boolean> isAllowed);
+ void onEnableRequested(@NonNull Consumer<Boolean> isAllowed);
/**
* Method to check if Nfc is allowed to be disabled by OEMs.
* @param isAllowed The {@link Consumer} to be completed. If disabling NFC is allowed,
@@ -242,7 +265,7 @@
* {@link Boolean#TRUE}, otherwise call with {@link Boolean#FALSE}.
* false if NFC cannot be disabled at this time.
*/
- void onDisable(@NonNull Consumer<Boolean> isAllowed);
+ void onDisableRequested(@NonNull Consumer<Boolean> isAllowed);
/**
* Callback to indicate that Nfc starts to boot.
@@ -255,7 +278,7 @@
void onEnableStarted();
/**
- * Callback to indicate that Nfc starts to enable.
+ * Callback to indicate that Nfc starts to disable.
*/
void onDisableStarted();
@@ -287,8 +310,12 @@
/**
* Notifies routing configuration is changed.
+ * @param isCommitRoutingSkipped The {@link Consumer} to be
+ * completed. If routing commit should be skipped,
+ * the {@link Consumer#accept(Object)} should be called with
+ * {@link Boolean#TRUE}, otherwise call with {@link Boolean#FALSE}.
*/
- void onRoutingChanged();
+ void onRoutingChanged(@NonNull Consumer<Boolean> isCommitRoutingSkipped);
/**
* API to activate start stop cpu boost on hce event.
@@ -340,6 +367,15 @@
void onEeListenActivated(boolean isActivated);
/**
+ * Notifies that some NFCEE (NFC Execution Environment) has been updated.
+ *
+ * <p> This indicates that some applet has been installed/updated/removed in
+ * one of the NFCEE's.
+ * </p>
+ */
+ void onEeUpdated();
+
+ /**
* Gets the intent to find the OEM package in the OEM App market. If the consumer returns
* {@code null} or a timeout occurs, the intent from the first available package will be
* used instead.
@@ -405,6 +441,19 @@
* @param item the log items that contains log information of NFC event.
*/
void onLogEventNotified(@NonNull OemLogItems item);
+
+ /**
+ * Callback to to extract OEM defined packages from given NDEF message when
+ * a NFC tag is detected. These are used to handle NFC tags encoded with a
+ * proprietary format for storing app name (Android native app format).
+ *
+ * @param message NDEF message containing OEM package names
+ * @param packageConsumer The {@link Consumer} to be completed.
+ * The {@link Consumer#accept(Object)} should be called with
+ * the list of package names.
+ */
+ void onExtractOemPackages(@NonNull NdefMessage message,
+ @NonNull Consumer<List<String>> packageConsumer);
}
@@ -605,12 +654,12 @@
/**
* Pauses NFC tag reader mode polling for a {@code timeoutInMs} millisecond.
* In case of {@code timeoutInMs} is zero or invalid polling will be stopped indefinitely
- * use {@link #resumePolling() to resume the polling.
- * @param timeoutInMs the pause polling duration in millisecond
+ * use {@link #resumePolling()} to resume the polling.
+ * @param timeoutInMs the pause polling duration in millisecond, ranging from 0 to 40000.
*/
@FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION)
@RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
- public void pausePolling(int timeoutInMs) {
+ public void pausePolling(@DurationMillisLong int timeoutInMs) {
NfcAdapter.callService(() -> NfcAdapter.sService.pausePolling(timeoutInMs));
}
@@ -741,6 +790,18 @@
return result;
}
+ /**
+ * API to force a routing table commit.
+ * @return a {@link StatusCode} to indicate if commit routing succeeded or not
+ */
+ @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
+ @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION)
+ @CommitRoutingStatusCode
+ public int forceRoutingTableCommit() {
+ return NfcAdapter.callServiceReturn(
+ () -> NfcAdapter.sService.commitRouting(), COMMIT_ROUTING_STATUS_FAILED);
+ }
+
private final class NfcOemExtensionCallback extends INfcOemExtensionCallback.Stub {
@Override
@@ -778,6 +839,12 @@
}
@Override
+ public void onEeUpdated() throws RemoteException {
+ mCallbackMap.forEach((cb, ex) ->
+ handleVoidCallback(null, (Object input) -> cb.onEeUpdated(), ex));
+ }
+
+ @Override
public void onStateUpdated(int state) throws RemoteException {
mCallbackMap.forEach((cb, ex) ->
handleVoidCallback(state, cb::onStateUpdated, ex));
@@ -799,13 +866,13 @@
public void onEnable(ResultReceiver isAllowed) throws RemoteException {
mCallbackMap.forEach((cb, ex) ->
handleVoidCallback(
- new ReceiverWrapper<>(isAllowed), cb::onEnable, ex));
+ new ReceiverWrapper<>(isAllowed), cb::onEnableRequested, ex));
}
@Override
public void onDisable(ResultReceiver isAllowed) throws RemoteException {
mCallbackMap.forEach((cb, ex) ->
handleVoidCallback(
- new ReceiverWrapper<>(isAllowed), cb::onDisable, ex));
+ new ReceiverWrapper<>(isAllowed), cb::onDisableRequested, ex));
}
@Override
public void onBootStarted() throws RemoteException {
@@ -844,9 +911,10 @@
new ReceiverWrapper<>(isSkipped), cb::onTagDispatch, ex));
}
@Override
- public void onRoutingChanged() throws RemoteException {
+ public void onRoutingChanged(ResultReceiver isSkipped) throws RemoteException {
mCallbackMap.forEach((cb, ex) ->
- handleVoidCallback(null, (Object input) -> cb.onRoutingChanged(), ex));
+ handleVoidCallback(
+ new ReceiverWrapper<>(isSkipped), cb::onRoutingChanged, ex));
}
@Override
public void onHceEventReceived(int action) throws RemoteException {
@@ -925,6 +993,15 @@
handleVoidCallback(item, cb::onLogEventNotified, ex));
}
+ @Override
+ public void onExtractOemPackages(NdefMessage message, ResultReceiver packageConsumer)
+ throws RemoteException {
+ mCallbackMap.forEach((cb, ex) ->
+ handleVoid2ArgCallback(message,
+ new ReceiverWrapper<>(packageConsumer),
+ cb::onExtractOemPackages, ex));
+ }
+
private <T> void handleVoidCallback(
T input, Consumer<T> callbackMethod, Executor executor) {
synchronized (mLock) {
@@ -1035,8 +1112,14 @@
Bundle bundle = new Bundle();
bundle.putParcelable("intent", (Intent) result);
mResultReceiver.send(0, bundle);
+ } else if (result instanceof List<?> list) {
+ if (list.stream().allMatch(String.class::isInstance)) {
+ Bundle bundle = new Bundle();
+ bundle.putStringArray("packageNames",
+ list.stream().map(pkg -> (String) pkg).toArray(String[]::new));
+ mResultReceiver.send(0, bundle);
+ }
}
-
}
@Override
diff --git a/nfc/java/android/nfc/cardemulation/CardEmulation.java b/nfc/java/android/nfc/cardemulation/CardEmulation.java
index eb28c3b..8917524 100644
--- a/nfc/java/android/nfc/cardemulation/CardEmulation.java
+++ b/nfc/java/android/nfc/cardemulation/CardEmulation.java
@@ -185,6 +185,65 @@
@FlaggedApi(Flags.FLAG_NFC_OVERRIDE_RECOVER_ROUTING_TABLE)
public static final int PROTOCOL_AND_TECHNOLOGY_ROUTE_UNSET = -1;
+ /**
+ * Status code returned when {@link #setServiceEnabledForCategoryOther(ComponentName, boolean)}
+ * succeeded.
+ * @hide
+ */
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_NFC_SET_SERVICE_ENABLED_FOR_CATEGORY_OTHER)
+ public static final int SET_SERVICE_ENABLED_STATUS_OK = 0;
+
+ /**
+ * Status code returned when {@link #setServiceEnabledForCategoryOther(ComponentName, boolean)}
+ * failed due to the unsupported feature.
+ * @hide
+ */
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_NFC_SET_SERVICE_ENABLED_FOR_CATEGORY_OTHER)
+ public static final int SET_SERVICE_ENABLED_STATUS_FAILURE_FEATURE_UNSUPPORTED = 1;
+
+ /**
+ * Status code returned when {@link #setServiceEnabledForCategoryOther(ComponentName, boolean)}
+ * failed due to the invalid service.
+ * @hide
+ */
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_NFC_SET_SERVICE_ENABLED_FOR_CATEGORY_OTHER)
+ public static final int SET_SERVICE_ENABLED_STATUS_FAILURE_INVALID_SERVICE = 2;
+
+ /**
+ * Status code returned when {@link #setServiceEnabledForCategoryOther(ComponentName, boolean)}
+ * failed due to the service is already set to the requested status.
+ * @hide
+ */
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_NFC_SET_SERVICE_ENABLED_FOR_CATEGORY_OTHER)
+ public static final int SET_SERVICE_ENABLED_STATUS_FAILURE_ALREADY_SET = 3;
+
+ /**
+ * Status code returned when {@link #setServiceEnabledForCategoryOther(ComponentName, boolean)}
+ * failed due to unknown error.
+ * @hide
+ */
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_NFC_SET_SERVICE_ENABLED_FOR_CATEGORY_OTHER)
+ public static final int SET_SERVICE_ENABLED_STATUS_FAILURE_UNKNOWN_ERROR = 4;
+
+ /**
+ * Status code returned by {@link #setServiceEnabledForCategoryOther(ComponentName, boolean)}
+ * @hide
+ */
+ @IntDef(prefix = "SET_SERVICE_ENABLED_STATUS_", value = {
+ SET_SERVICE_ENABLED_STATUS_OK,
+ SET_SERVICE_ENABLED_STATUS_FAILURE_FEATURE_UNSUPPORTED,
+ SET_SERVICE_ENABLED_STATUS_FAILURE_INVALID_SERVICE,
+ SET_SERVICE_ENABLED_STATUS_FAILURE_ALREADY_SET,
+ SET_SERVICE_ENABLED_STATUS_FAILURE_UNKNOWN_ERROR
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface SetServiceEnabledStatusCode {}
+
static boolean sIsInitialized = false;
static HashMap<Context, CardEmulation> sCardEmus = new HashMap<Context, CardEmulation>();
static INfcCardEmulation sService;
@@ -883,22 +942,24 @@
}
/**
- * Allows to set or unset preferred service (category other) to avoid AID Collision.
+ * Allows to set or unset preferred service (category other) to avoid AID Collision. The user
+ * should use corresponding context using {@link Context#createContextAsUser(UserHandle, int)}
*
* @param service The ComponentName of the service
* @param status true to enable, false to disable
- * @param userId the user handle of the user whose information is being requested.
- * @return set service for the category and true if service is already set return false.
+ * @return true if preferred service is successfully set or unset, otherwise return false.
*
* @hide
*/
- public boolean setServiceEnabledForCategoryOther(ComponentName service, boolean status,
- int userId) {
- if (service == null) {
- throw new NullPointerException("activity or service or category is null");
- }
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_NFC_SET_SERVICE_ENABLED_FOR_CATEGORY_OTHER)
+ @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
+ @SetServiceEnabledStatusCode
+ public int setServiceEnabledForCategoryOther(@NonNull ComponentName service,
+ boolean status) {
return callServiceReturn(() ->
- sService.setServiceEnabledForCategoryOther(userId, service, status), false);
+ sService.setServiceEnabledForCategoryOther(mContext.getUser().getIdentifier(),
+ service, status), SET_SERVICE_ENABLED_STATUS_FAILURE_UNKNOWN_ERROR);
}
/** @hide */
diff --git a/nfc/java/android/nfc/flags.aconfig b/nfc/java/android/nfc/flags.aconfig
index 34f0200..ee287ab 100644
--- a/nfc/java/android/nfc/flags.aconfig
+++ b/nfc/java/android/nfc/flags.aconfig
@@ -173,3 +173,19 @@
description: "Share wallet role routing priority with associated services"
bug: "366243361"
}
+
+flag {
+ name: "nfc_set_service_enabled_for_category_other"
+ is_exported: true
+ namespace: "nfc"
+ description: "Enable set service enabled for category other"
+ bug: "338157113"
+}
+
+flag {
+ name: "nfc_check_tag_intent_preference"
+ is_exported: true
+ namespace: "nfc"
+ description: "App can check its tag intent preference status"
+ bug: "335916336"
+}
diff --git a/packages/CrashRecovery/services/module/java/com/android/server/PackageWatchdog.java b/packages/CrashRecovery/services/module/java/com/android/server/PackageWatchdog.java
index fbf51fd..8c15b09 100644
--- a/packages/CrashRecovery/services/module/java/com/android/server/PackageWatchdog.java
+++ b/packages/CrashRecovery/services/module/java/com/android/server/PackageWatchdog.java
@@ -218,7 +218,7 @@
@GuardedBy("sPackageWatchdogLock")
private static PackageWatchdog sPackageWatchdog;
- private final Object mLock = new Object();
+ private static final Object sLock = new Object();
// System server context
private final Context mContext;
// Handler to run short running tasks
@@ -228,7 +228,7 @@
// Contains (observer-name -> observer-handle) that have ever been registered from
// previous boots. Observers with all packages expired are periodically pruned.
// It is saved to disk on system shutdown and repouplated on startup so it survives reboots.
- @GuardedBy("mLock")
+ @GuardedBy("sLock")
private final ArrayMap<String, ObserverInternal> mAllObservers = new ArrayMap<>();
// File containing the XML data of monitored packages /data/system/package-watchdog.xml
private final AtomicFile mPolicyFile;
@@ -244,26 +244,26 @@
private final Set<String> mPackagesExemptFromImpactLevelThreshold = new ArraySet<>();
// The set of packages that have been synced with the ExplicitHealthCheckController
- @GuardedBy("mLock")
+ @GuardedBy("sLock")
private Set<String> mRequestedHealthCheckPackages = new ArraySet<>();
- @GuardedBy("mLock")
+ @GuardedBy("sLock")
private boolean mIsPackagesReady;
// Flag to control whether explicit health checks are supported or not
- @GuardedBy("mLock")
+ @GuardedBy("sLock")
private boolean mIsHealthCheckEnabled = DEFAULT_EXPLICIT_HEALTH_CHECK_ENABLED;
- @GuardedBy("mLock")
+ @GuardedBy("sLock")
private int mTriggerFailureDurationMs = DEFAULT_TRIGGER_FAILURE_DURATION_MS;
- @GuardedBy("mLock")
+ @GuardedBy("sLock")
private int mTriggerFailureCount = DEFAULT_TRIGGER_FAILURE_COUNT;
// SystemClock#uptimeMillis when we last executed #syncState
// 0 if no prune is scheduled.
- @GuardedBy("mLock")
+ @GuardedBy("sLock")
private long mUptimeAtLastStateSync;
// If true, sync explicit health check packages with the ExplicitHealthCheckController.
- @GuardedBy("mLock")
+ @GuardedBy("sLock")
private boolean mSyncRequired = false;
- @GuardedBy("mLock")
+ @GuardedBy("sLock")
private long mLastMitigation = -1000000;
@FunctionalInterface
@@ -303,7 +303,11 @@
sPackageWatchdog = this;
}
- /** Creates or gets singleton instance of PackageWatchdog. */
+ /**
+ * Creates or gets singleton instance of PackageWatchdog.
+ *
+ * @param context The system server context.
+ */
public static @NonNull PackageWatchdog getInstance(@NonNull Context context) {
synchronized (sPackageWatchdogLock) {
if (sPackageWatchdog == null) {
@@ -319,7 +323,7 @@
* @hide
*/
public void onPackagesReady() {
- synchronized (mLock) {
+ synchronized (sLock) {
mIsPackagesReady = true;
mHealthCheckController.setCallbacks(packageName -> onHealthCheckPassed(packageName),
packages -> onSupportedPackages(packages),
@@ -338,7 +342,7 @@
* @hide
*/
public void registerHealthObserver(PackageHealthObserver observer) {
- synchronized (mLock) {
+ synchronized (sLock) {
ObserverInternal internalObserver = mAllObservers.get(observer.getUniqueIdentifier());
if (internalObserver != null) {
internalObserver.registeredObserver = observer;
@@ -405,7 +409,7 @@
mLongTaskHandler.post(() -> {
syncState("observing new packages");
- synchronized (mLock) {
+ synchronized (sLock) {
ObserverInternal oldObserver = mAllObservers.get(observer.getUniqueIdentifier());
if (oldObserver == null) {
Slog.d(TAG, observer.getUniqueIdentifier() + " started monitoring health "
@@ -437,7 +441,7 @@
*/
public void unregisterHealthObserver(PackageHealthObserver observer) {
mLongTaskHandler.post(() -> {
- synchronized (mLock) {
+ synchronized (sLock) {
mAllObservers.remove(observer.getUniqueIdentifier());
}
syncState("unregistering observer: " + observer.getUniqueIdentifier());
@@ -458,7 +462,7 @@
Slog.w(TAG, "Could not resolve a list of failing packages");
return;
}
- synchronized (mLock) {
+ synchronized (sLock) {
final long now = mSystemClock.uptimeMillis();
if (Flags.recoverabilityDetection()) {
if (now >= mLastMitigation
@@ -469,7 +473,7 @@
}
}
mLongTaskHandler.post(() -> {
- synchronized (mLock) {
+ synchronized (sLock) {
if (mAllObservers.isEmpty()) {
return;
}
@@ -569,7 +573,7 @@
int currentObserverImpact,
int mitigationCount) {
if (allowMitigations(currentObserverImpact, versionedPackage)) {
- synchronized (mLock) {
+ synchronized (sLock) {
mLastMitigation = mSystemClock.uptimeMillis();
}
currentObserverToNotify.onExecuteHealthCheckMitigation(versionedPackage, failureReason,
@@ -599,7 +603,7 @@
*/
@SuppressWarnings("GuardedBy")
public void noteBoot() {
- synchronized (mLock) {
+ synchronized (sLock) {
// if boot count has reached threshold, start mitigation.
// We wait until threshold number of restarts only for the first time. Perform
// mitigations for every restart after that.
@@ -652,7 +656,7 @@
// This currently adds about 7ms extra to shutdown thread
/** @hide Writes the package information to file during shutdown. */
public void writeNow() {
- synchronized (mLock) {
+ synchronized (sLock) {
// Must only run synchronous tasks as this runs on the ShutdownThread and no other
// thread is guaranteed to run during shutdown.
if (!mAllObservers.isEmpty()) {
@@ -671,7 +675,7 @@
* passed and the health check service is stopped.
*/
private void setExplicitHealthCheckEnabled(boolean enabled) {
- synchronized (mLock) {
+ synchronized (sLock) {
mIsHealthCheckEnabled = enabled;
mHealthCheckController.setEnabled(enabled);
mSyncRequired = true;
@@ -841,7 +845,10 @@
/**
* Returns {@code true} if this observer wishes to observe the given package, {@code false}
- * otherwise
+ * otherwise.
+ * Any failing package can be passed on to the observer. Currently the packages that have
+ * ANRs and perform {@link android.service.watchdog.ExplicitHealthCheckService} are being
+ * passed to observers in these API.
*
* <p> A persistent observer may choose to start observing certain failing packages, even if
* it has not explicitly asked to watch the package with {@link #startObservingHealth}.
@@ -853,14 +860,14 @@
@VisibleForTesting
long getTriggerFailureCount() {
- synchronized (mLock) {
+ synchronized (sLock) {
return mTriggerFailureCount;
}
}
@VisibleForTesting
long getTriggerFailureDurationMs() {
- synchronized (mLock) {
+ synchronized (sLock) {
return mTriggerFailureDurationMs;
}
}
@@ -881,7 +888,7 @@
*/
private void syncRequests() {
boolean syncRequired = false;
- synchronized (mLock) {
+ synchronized (sLock) {
if (mIsPackagesReady) {
Set<String> packages = getPackagesPendingHealthChecksLocked();
if (mSyncRequired || !packages.equals(mRequestedHealthCheckPackages)
@@ -918,7 +925,7 @@
Slog.i(TAG, "Health check passed for package: " + packageName);
boolean isStateChanged = false;
- synchronized (mLock) {
+ synchronized (sLock) {
for (int observerIdx = 0; observerIdx < mAllObservers.size(); observerIdx++) {
ObserverInternal observer = mAllObservers.valueAt(observerIdx);
MonitoredPackage monitoredPackage = observer.getMonitoredPackage(packageName);
@@ -946,7 +953,7 @@
supportedPackageTimeouts.put(info.getPackageName(), info.getHealthCheckTimeoutMillis());
}
- synchronized (mLock) {
+ synchronized (sLock) {
Slog.d(TAG, "Received supported packages " + supportedPackages);
Iterator<ObserverInternal> oit = mAllObservers.values().iterator();
while (oit.hasNext()) {
@@ -977,13 +984,13 @@
}
private void onSyncRequestNotified() {
- synchronized (mLock) {
+ synchronized (sLock) {
mSyncRequired = true;
syncRequestsAsync();
}
}
- @GuardedBy("mLock")
+ @GuardedBy("sLock")
private Set<String> getPackagesPendingHealthChecksLocked() {
Set<String> packages = new ArraySet<>();
Iterator<ObserverInternal> oit = mAllObservers.values().iterator();
@@ -1009,7 +1016,7 @@
* health check service and schedules the next state sync.
*/
private void syncState(String reason) {
- synchronized (mLock) {
+ synchronized (sLock) {
Slog.i(TAG, "Syncing state, reason: " + reason);
pruneObserversLocked();
@@ -1025,7 +1032,7 @@
syncState("scheduled");
}
- @GuardedBy("mLock")
+ @GuardedBy("sLock")
private void scheduleNextSyncStateLocked() {
long durationMs = getNextStateSyncMillisLocked();
mShortTaskHandler.removeCallbacks(mSyncStateWithScheduledReason);
@@ -1043,7 +1050,7 @@
*
* @returns Long#MAX_VALUE if there are no observed packages.
*/
- @GuardedBy("mLock")
+ @GuardedBy("sLock")
private long getNextStateSyncMillisLocked() {
long shortestDurationMs = Long.MAX_VALUE;
for (int oIndex = 0; oIndex < mAllObservers.size(); oIndex++) {
@@ -1064,7 +1071,7 @@
* Removes {@code elapsedMs} milliseconds from all durations on monitored packages
* and updates other internal state.
*/
- @GuardedBy("mLock")
+ @GuardedBy("sLock")
private void pruneObserversLocked() {
long elapsedMs = mUptimeAtLastStateSync == 0
? 0 : mSystemClock.uptimeMillis() - mUptimeAtLastStateSync;
@@ -1092,7 +1099,7 @@
private void onHealthCheckFailed(ObserverInternal observer,
Set<MonitoredPackage> failedPackages) {
mLongTaskHandler.post(() -> {
- synchronized (mLock) {
+ synchronized (sLock) {
PackageHealthObserver registeredObserver = observer.registeredObserver;
if (registeredObserver != null) {
Iterator<MonitoredPackage> it = failedPackages.iterator();
@@ -1201,7 +1208,7 @@
*/
@VisibleForTesting
void updateConfigs() {
- synchronized (mLock) {
+ synchronized (sLock) {
mTriggerFailureCount = DeviceConfig.getInt(
DeviceConfig.NAMESPACE_ROLLBACK,
PROPERTY_WATCHDOG_TRIGGER_FAILURE_COUNT,
@@ -1230,7 +1237,7 @@
*/
private boolean saveToFile() {
Slog.i(TAG, "Saving observer state to file");
- synchronized (mLock) {
+ synchronized (sLock) {
FileOutputStream stream;
try {
stream = mPolicyFile.startWrite();
@@ -1297,20 +1304,38 @@
/** Dump status of every observer in mAllObservers. */
public void dump(@NonNull PrintWriter pw) {
- if (Flags.synchronousRebootInRescueParty() && RescueParty.isRecoveryTriggeredReboot()) {
+ if (Flags.synchronousRebootInRescueParty() && isRecoveryTriggeredReboot()) {
dumpInternal(pw);
} else {
- synchronized (mLock) {
+ synchronized (sLock) {
dumpInternal(pw);
}
}
}
+ /**
+ * Check if we're currently attempting to reboot during mitigation. This method must return
+ * true if triggered reboot early during a boot loop, since the device will not be fully booted
+ * at this time.
+ * @hide
+ */
+ public static boolean isRecoveryTriggeredReboot() {
+ return isFactoryResetPropertySet() || isRebootPropertySet();
+ }
+
+ private static boolean isFactoryResetPropertySet() {
+ return CrashRecoveryProperties.attemptingFactoryReset().orElse(false);
+ }
+
+ private static boolean isRebootPropertySet() {
+ return CrashRecoveryProperties.attemptingReboot().orElse(false);
+ }
+
private void dumpInternal(@NonNull PrintWriter pw) {
IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
ipw.println("Package Watchdog status");
ipw.increaseIndent();
- synchronized (mLock) {
+ synchronized (sLock) {
for (String observerName : mAllObservers.keySet()) {
ipw.println("Observer name: " + observerName);
ipw.increaseIndent();
@@ -1324,7 +1349,7 @@
}
@VisibleForTesting
- @GuardedBy("mLock")
+ @GuardedBy("sLock")
void registerObserverInternal(ObserverInternal observerInternal) {
mAllObservers.put(observerInternal.name, observerInternal);
}
@@ -1333,15 +1358,15 @@
* Represents an observer monitoring a set of packages along with the failure thresholds for
* each package.
*
- * <p> Note, the PackageWatchdog#mLock must always be held when reading or writing
+ * <p> Note, the PackageWatchdog#sLock must always be held when reading or writing
* instances of this class.
*/
static class ObserverInternal {
public final String name;
- @GuardedBy("mLock")
+ @GuardedBy("sLock")
private final ArrayMap<String, MonitoredPackage> mPackages = new ArrayMap<>();
@Nullable
- @GuardedBy("mLock")
+ @GuardedBy("sLock")
public PackageHealthObserver registeredObserver;
private int mMitigationCount;
@@ -1359,7 +1384,7 @@
* Writes important {@link MonitoredPackage} details for this observer to file.
* Does not persist any package failure thresholds.
*/
- @GuardedBy("mLock")
+ @GuardedBy("sLock")
public boolean writeLocked(XmlSerializer out) {
try {
out.startTag(null, TAG_OBSERVER);
@@ -1387,7 +1412,7 @@
mMitigationCount = mitigationCount;
}
- @GuardedBy("mLock")
+ @GuardedBy("sLock")
public void updatePackagesLocked(List<MonitoredPackage> packages) {
for (int pIndex = 0; pIndex < packages.size(); pIndex++) {
MonitoredPackage p = packages.get(pIndex);
@@ -1410,7 +1435,7 @@
* health check passing, or an empty list if no package expired for which an explicit health
* check was still pending
*/
- @GuardedBy("mLock")
+ @GuardedBy("sLock")
private Set<MonitoredPackage> prunePackagesLocked(long elapsedMs) {
Set<MonitoredPackage> failedPackages = new ArraySet<>();
Iterator<MonitoredPackage> it = mPackages.values().iterator();
@@ -1435,7 +1460,7 @@
* @returns {@code true} if failure threshold is exceeded, {@code false} otherwise
* @hide
*/
- @GuardedBy("mLock")
+ @GuardedBy("sLock")
public boolean onPackageFailureLocked(String packageName) {
if (getMonitoredPackage(packageName) == null && registeredObserver.isPersistent()
&& registeredObserver.mayObservePackage(packageName)) {
@@ -1454,7 +1479,7 @@
*
* @return a mapping of package names to {@link MonitoredPackage} objects.
*/
- @GuardedBy("mLock")
+ @GuardedBy("sLock")
public ArrayMap<String, MonitoredPackage> getMonitoredPackages() {
return mPackages;
}
@@ -1467,7 +1492,7 @@
* @return the {@link MonitoredPackage} object associated with the package name if one
* exists, {@code null} otherwise.
*/
- @GuardedBy("mLock")
+ @GuardedBy("sLock")
@Nullable
public MonitoredPackage getMonitoredPackage(String packageName) {
return mPackages.get(packageName);
@@ -1478,7 +1503,7 @@
*
* @param p: the {@link MonitoredPackage} to store.
*/
- @GuardedBy("mLock")
+ @GuardedBy("sLock")
public void putMonitoredPackage(MonitoredPackage p) {
mPackages.put(p.getName(), p);
}
@@ -1601,17 +1626,17 @@
* Represents a package and its health check state along with the time
* it should be monitored for.
*
- * <p> Note, the PackageWatchdog#mLock must always be held when reading or writing
+ * <p> Note, the PackageWatchdog#sLock must always be held when reading or writing
* instances of this class.
*/
class MonitoredPackage {
private final String mPackageName;
// Times when package failures happen sorted in ascending order
- @GuardedBy("mLock")
+ @GuardedBy("sLock")
private final LongArrayQueue mFailureHistory = new LongArrayQueue();
// Times when an observer was called to mitigate this package's failure. Sorted in
// ascending order.
- @GuardedBy("mLock")
+ @GuardedBy("sLock")
private final LongArrayQueue mMitigationCalls;
// One of STATE_[ACTIVE|INACTIVE|PASSED|FAILED]. Updated on construction and after
// methods that could change the health check state: handleElapsedTimeLocked and
@@ -1620,17 +1645,17 @@
// Whether an explicit health check has passed.
// This value in addition with mHealthCheckDurationMs determines the health check state
// of the package, see #getHealthCheckStateLocked
- @GuardedBy("mLock")
+ @GuardedBy("sLock")
private boolean mHasPassedHealthCheck;
// System uptime duration to monitor package.
- @GuardedBy("mLock")
+ @GuardedBy("sLock")
private long mDurationMs;
// System uptime duration to check the result of an explicit health check
// Initially, MAX_VALUE until we get a value from the health check service
// and request health checks.
// This value in addition with mHasPassedHealthCheck determines the health check state
// of the package, see #getHealthCheckStateLocked
- @GuardedBy("mLock")
+ @GuardedBy("sLock")
private long mHealthCheckDurationMs = Long.MAX_VALUE;
MonitoredPackage(String packageName, long durationMs,
@@ -1647,7 +1672,7 @@
/** Writes the salient fields to disk using {@code out}.
* @hide
*/
- @GuardedBy("mLock")
+ @GuardedBy("sLock")
public void writeLocked(XmlSerializer out) throws IOException {
out.startTag(null, TAG_PACKAGE);
out.attribute(null, ATTR_NAME, getName());
@@ -1665,7 +1690,7 @@
*
* @return {@code true} if failure count exceeds a threshold, {@code false} otherwise
*/
- @GuardedBy("mLock")
+ @GuardedBy("sLock")
public boolean onFailureLocked() {
// Sliding window algorithm: find out if there exists a window containing failures >=
// mTriggerFailureCount.
@@ -1685,7 +1710,7 @@
/**
* Notes the timestamp of a mitigation call into the observer.
*/
- @GuardedBy("mLock")
+ @GuardedBy("sLock")
public void noteMitigationCallLocked() {
mMitigationCalls.addLast(mSystemClock.uptimeMillis());
}
@@ -1696,7 +1721,7 @@
*
* @return the number of mitigation calls made in the de-escalation window.
*/
- @GuardedBy("mLock")
+ @GuardedBy("sLock")
public int getMitigationCountLocked() {
try {
final long now = mSystemClock.uptimeMillis();
@@ -1716,7 +1741,7 @@
*
* @return a LongArrayQueue of the mitigation calls relative to the current system uptime.
*/
- @GuardedBy("mLock")
+ @GuardedBy("sLock")
public LongArrayQueue normalizeMitigationCalls() {
LongArrayQueue normalized = new LongArrayQueue();
final long now = mSystemClock.uptimeMillis();
@@ -1731,7 +1756,7 @@
*
* @return the new health check state
*/
- @GuardedBy("mLock")
+ @GuardedBy("sLock")
public int setHealthCheckActiveLocked(long initialHealthCheckDurationMs) {
if (initialHealthCheckDurationMs <= 0) {
Slog.wtf(TAG, "Cannot set non-positive health check duration "
@@ -1751,7 +1776,7 @@
*
* @return the new health check state
*/
- @GuardedBy("mLock")
+ @GuardedBy("sLock")
public int handleElapsedTimeLocked(long elapsedMs) {
if (elapsedMs <= 0) {
Slog.w(TAG, "Cannot handle non-positive elapsed time for package " + getName());
@@ -1769,7 +1794,7 @@
}
/** Explicitly update the monitoring duration of the package. */
- @GuardedBy("mLock")
+ @GuardedBy("sLock")
public void updateHealthCheckDuration(long newDurationMs) {
mDurationMs = newDurationMs;
}
@@ -1780,7 +1805,7 @@
*
* @return the new {@link HealthCheckState health check state}
*/
- @GuardedBy("mLock")
+ @GuardedBy("sLock")
@HealthCheckState
public int tryPassHealthCheckLocked() {
if (mHealthCheckState != HealthCheckState.FAILED) {
@@ -1799,7 +1824,7 @@
/**
* Returns the current {@link HealthCheckState health check state}.
*/
- @GuardedBy("mLock")
+ @GuardedBy("sLock")
@HealthCheckState
public int getHealthCheckStateLocked() {
return mHealthCheckState;
@@ -1810,7 +1835,7 @@
*
* @return the duration or {@link Long#MAX_VALUE} if the package should not be scheduled
*/
- @GuardedBy("mLock")
+ @GuardedBy("sLock")
public long getShortestScheduleDurationMsLocked() {
// Consider health check duration only if #isPendingHealthChecksLocked is true
return Math.min(toPositive(mDurationMs),
@@ -1822,7 +1847,7 @@
* Returns {@code true} if the total duration left to monitor the package is less than or
* equal to 0 {@code false} otherwise.
*/
- @GuardedBy("mLock")
+ @GuardedBy("sLock")
public boolean isExpiredLocked() {
return mDurationMs <= 0;
}
@@ -1831,7 +1856,7 @@
* Returns {@code true} if the package, {@link #getName} is expecting health check results
* {@code false} otherwise.
*/
- @GuardedBy("mLock")
+ @GuardedBy("sLock")
public boolean isPendingHealthChecksLocked() {
return mHealthCheckState == HealthCheckState.ACTIVE
|| mHealthCheckState == HealthCheckState.INACTIVE;
@@ -1843,7 +1868,7 @@
*
* @return the new {@link HealthCheckState health check state}
*/
- @GuardedBy("mLock")
+ @GuardedBy("sLock")
@HealthCheckState
private int updateHealthCheckStateLocked() {
int oldState = mHealthCheckState;
@@ -1898,7 +1923,7 @@
}
}
- @GuardedBy("mLock")
+ @GuardedBy("sLock")
@SuppressWarnings("GuardedBy")
void saveAllObserversBootMitigationCountToMetadata(String filePath) {
HashMap<String, Integer> bootMitigationCounts = new HashMap<>();
@@ -2001,7 +2026,7 @@
/** Increments the boot counter, and returns whether the device is bootlooping. */
- @GuardedBy("mLock")
+ @GuardedBy("sLock")
public boolean incrementAndTest() {
if (Flags.recoverabilityDetection()) {
readAllObserversBootMitigationCountIfNecessary(METADATA_FILE);
@@ -2042,7 +2067,7 @@
}
}
- @GuardedBy("mLock")
+ @GuardedBy("sLock")
private boolean performedMitigationsDuringWindow() {
for (ObserverInternal observerInternal: mAllObservers.values()) {
if (observerInternal.getBootMitigationCount() > 0) {
@@ -2052,7 +2077,7 @@
return false;
}
- @GuardedBy("mLock")
+ @GuardedBy("sLock")
private void resetAllObserversBootMitigationCount() {
for (int i = 0; i < mAllObservers.size(); i++) {
final ObserverInternal observer = mAllObservers.valueAt(i);
@@ -2061,7 +2086,7 @@
saveAllObserversBootMitigationCountToMetadata(METADATA_FILE);
}
- @GuardedBy("mLock")
+ @GuardedBy("sLock")
@SuppressWarnings("GuardedBy")
void readAllObserversBootMitigationCountIfNecessary(String filePath) {
File metadataFile = new File(filePath);
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-night-v35/themes.xml b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-night-v35/themes.xml
index fadcf7b..e68253e 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-night-v35/themes.xml
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-night-v35/themes.xml
@@ -18,7 +18,7 @@
<style name="Theme.CollapsingToolbar.Settings" parent="@style/Theme.MaterialComponents.DayNight">
<item name="elevationOverlayEnabled">true</item>
<item name="elevationOverlayColor">?attr/colorPrimary</item>
- <item name="colorPrimary">@color/settingslib_materialColorOnSurfaceInverse</item>
+ <item name="colorPrimary">@color/settingslib_materialColorInverseOnSurface</item>
<item name="colorAccent">@color/settingslib_materialColorPrimaryFixed</item>
</style>
</resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v35/themes.xml b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v35/themes.xml
index 7c9d1a4..f7c9aac 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v35/themes.xml
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v35/themes.xml
@@ -18,7 +18,7 @@
<style name="Theme.CollapsingToolbar.Settings" parent="@style/Theme.MaterialComponents.DayNight">
<item name="elevationOverlayEnabled">true</item>
<item name="elevationOverlayColor">?attr/colorPrimary</item>
- <item name="colorPrimary">@color/settingslib_materialColorOnSurfaceInverse</item>
+ <item name="colorPrimary">@color/settingslib_materialColorInverseOnSurface</item>
<item name="colorAccent">@color/settingslib_materialColorPrimary</item>
</style>
</resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SharedPreferencesObservable.kt b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SharedPreferencesObservable.kt
deleted file mode 100644
index e70ec5b..0000000
--- a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SharedPreferencesObservable.kt
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settingslib.datastore
-
-import android.content.SharedPreferences
-
-/** [SharedPreferences] based [KeyedDataObservable]. */
-class SharedPreferencesObservable(private val sharedPreferences: SharedPreferences) :
- KeyedDataObservable<String>(), AutoCloseable {
-
- private val listener = createSharedPreferenceListener()
-
- init {
- sharedPreferences.registerOnSharedPreferenceChangeListener(listener)
- }
-
- override fun close() {
- sharedPreferences.unregisterOnSharedPreferenceChangeListener(listener)
- }
-}
-
-/** Creates [SharedPreferences.OnSharedPreferenceChangeListener] for [KeyedObservable]. */
-internal fun KeyedObservable<String>.createSharedPreferenceListener() =
- SharedPreferences.OnSharedPreferenceChangeListener { _, key ->
- if (key != null) {
- notifyChange(key, DataChangeReason.UPDATE)
- } else {
- // On Android >= R, SharedPreferences.Editor.clear() will trigger this case
- notifyChange(DataChangeReason.DELETE)
- }
- }
diff --git a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/GetPreferenceGraphApiHandler.kt b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/GetPreferenceGraphApiHandler.kt
index 5ceee6d..7aece51 100644
--- a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/GetPreferenceGraphApiHandler.kt
+++ b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/GetPreferenceGraphApiHandler.kt
@@ -22,11 +22,13 @@
import com.android.settingslib.ipc.ApiHandler
import com.android.settingslib.ipc.MessageCodec
import com.android.settingslib.metadata.PreferenceScreenRegistry
+import com.android.settingslib.preference.PreferenceScreenProvider
import java.util.Locale
/** API to get preference graph. */
-abstract class GetPreferenceGraphApiHandler :
- ApiHandler<GetPreferenceGraphRequest, PreferenceGraphProto> {
+abstract class GetPreferenceGraphApiHandler(
+ private val preferenceScreenProviders: Set<Class<out PreferenceScreenProvider>>
+) : ApiHandler<GetPreferenceGraphRequest, PreferenceGraphProto> {
override val requestCodec: MessageCodec<GetPreferenceGraphRequest>
get() = GetPreferenceGraphRequestCodec
@@ -40,14 +42,16 @@
callingUid: Int,
request: GetPreferenceGraphRequest,
): PreferenceGraphProto {
- val builderRequest =
- if (request.screenKeys.isEmpty()) {
- val keys = PreferenceScreenRegistry.preferenceScreens.keys
- GetPreferenceGraphRequest(keys, request.visitedScreens, request.locale)
- } else {
- request
+ val builder = PreferenceGraphBuilder.of(application, myUid, callingUid, request)
+ if (request.screenKeys.isEmpty()) {
+ for (key in PreferenceScreenRegistry.preferenceScreens.keys) {
+ builder.addPreferenceScreenFromRegistry(key)
}
- return PreferenceGraphBuilder.of(application, builderRequest).build()
+ for (provider in preferenceScreenProviders) {
+ builder.addPreferenceScreenProvider(provider)
+ }
+ }
+ return builder.build()
}
}
@@ -64,16 +68,18 @@
val screenKeys: Set<String> = setOf(),
val visitedScreens: Set<String> = setOf(),
val locale: Locale? = null,
- val includeValue: Boolean = true,
+ val flags: Int = PreferenceGetterFlags.ALL,
+ val includeValue: Boolean = true, // TODO: clean up
val includeValueDescriptor: Boolean = true,
)
object GetPreferenceGraphRequestCodec : MessageCodec<GetPreferenceGraphRequest> {
override fun encode(data: GetPreferenceGraphRequest): Bundle =
- Bundle(3).apply {
+ Bundle(4).apply {
putStringArray(KEY_SCREEN_KEYS, data.screenKeys.toTypedArray())
putStringArray(KEY_VISITED_KEYS, data.visitedScreens.toTypedArray())
putString(KEY_LOCALE, data.locale?.toLanguageTag())
+ putInt(KEY_FLAGS, data.flags)
}
override fun decode(data: Bundle): GetPreferenceGraphRequest {
@@ -84,12 +90,14 @@
screenKeys.toSet(),
visitedScreens.toSet(),
data.getString(KEY_LOCALE).toLocale(),
+ data.getInt(KEY_FLAGS),
)
}
private const val KEY_SCREEN_KEYS = "k"
private const val KEY_VISITED_KEYS = "v"
private const val KEY_LOCALE = "l"
+ private const val KEY_FLAGS = "f"
}
object PreferenceGraphProtoCodec : MessageCodec<PreferenceGraphProto> {
diff --git a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceCoordinate.kt b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceCoordinate.kt
new file mode 100644
index 0000000..68aa2d2
--- /dev/null
+++ b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceCoordinate.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.graph
+
+import android.os.Parcel
+import android.os.Parcelable
+
+/**
+ * Coordinate to locate a preference.
+ *
+ * Within an app, the preference screen key (unique among screens) plus preference key (unique on
+ * the screen) is used to locate a preference.
+ */
+data class PreferenceCoordinate(val screenKey: String, val key: String) : Parcelable {
+
+ constructor(parcel: Parcel) : this(parcel.readString()!!, parcel.readString()!!)
+
+ override fun writeToParcel(parcel: Parcel, flags: Int) {
+ parcel.writeString(screenKey)
+ parcel.writeString(key)
+ }
+
+ override fun describeContents() = 0
+
+ companion object CREATOR : Parcelable.Creator<PreferenceCoordinate> {
+
+ override fun createFromParcel(parcel: Parcel) = PreferenceCoordinate(parcel)
+
+ override fun newArray(size: Int) = arrayOfNulls<PreferenceCoordinate>(size)
+ }
+}
diff --git a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGetterApi.kt b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGetterApi.kt
new file mode 100644
index 0000000..c8453ef
--- /dev/null
+++ b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGetterApi.kt
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.graph
+
+import android.app.Application
+import androidx.annotation.IntDef
+import com.android.settingslib.graph.proto.PreferenceProto
+import com.android.settingslib.ipc.ApiDescriptor
+import com.android.settingslib.ipc.ApiHandler
+import com.android.settingslib.ipc.ApiPermissionChecker
+import com.android.settingslib.metadata.PreferenceHierarchyNode
+import com.android.settingslib.metadata.PreferenceScreenRegistry
+
+/**
+ * Request to get preference information.
+ *
+ * @param preferences coordinate of preferences
+ * @param flags a combination of constants in [PreferenceGetterFlags]
+ */
+class PreferenceGetterRequest(val preferences: Array<PreferenceCoordinate>, val flags: Int)
+
+/** Error code of preference getter request. */
+@Target(AnnotationTarget.TYPE)
+@IntDef(
+ PreferenceGetterErrorCode.NOT_FOUND,
+ PreferenceGetterErrorCode.DISALLOW,
+ PreferenceGetterErrorCode.INTERNAL_ERROR,
+)
+@Retention(AnnotationRetention.SOURCE)
+annotation class PreferenceGetterErrorCode {
+ companion object {
+ /** Preference is not found. */
+ const val NOT_FOUND = 1
+ /** Disallow to get preference value (e.g. uid not allowed). */
+ const val DISALLOW = 2
+ /** Internal error happened when get preference information. */
+ const val INTERNAL_ERROR = 3
+
+ fun getMessage(code: Int) =
+ when (code) {
+ NOT_FOUND -> "Preference not found"
+ DISALLOW -> "Disallow to get preference value"
+ INTERNAL_ERROR -> "Internal error"
+ else -> "Unknown error"
+ }
+ }
+}
+
+/** Response of the getter API. */
+class PreferenceGetterResponse(
+ val errors: Map<PreferenceCoordinate, @PreferenceGetterErrorCode Int>,
+ val preferences: Map<PreferenceCoordinate, PreferenceProto>,
+)
+
+/** Preference getter API descriptor. */
+class PreferenceGetterApiDescriptor(override val id: Int) :
+ ApiDescriptor<PreferenceGetterRequest, PreferenceGetterResponse> {
+
+ override val requestCodec = PreferenceGetterRequestCodec()
+
+ override val responseCodec = PreferenceGetterResponseCodec()
+}
+
+/** Preference getter API implementation. */
+class PreferenceGetterApiHandler(
+ override val id: Int,
+ private val permissionChecker: ApiPermissionChecker<PreferenceGetterRequest>,
+) : ApiHandler<PreferenceGetterRequest, PreferenceGetterResponse> {
+
+ override fun hasPermission(
+ application: Application,
+ myUid: Int,
+ callingUid: Int,
+ request: PreferenceGetterRequest,
+ ) = permissionChecker.hasPermission(application, myUid, callingUid, request)
+
+ override suspend fun invoke(
+ application: Application,
+ myUid: Int,
+ callingUid: Int,
+ request: PreferenceGetterRequest,
+ ): PreferenceGetterResponse {
+ val errors = mutableMapOf<PreferenceCoordinate, Int>()
+ val preferences = mutableMapOf<PreferenceCoordinate, PreferenceProto>()
+ val flags = request.flags
+ for ((screenKey, coordinates) in request.preferences.groupBy { it.screenKey }) {
+ val screenMetadata = PreferenceScreenRegistry[screenKey]
+ if (screenMetadata == null) {
+ for (coordinate in coordinates) {
+ errors[coordinate] = PreferenceGetterErrorCode.NOT_FOUND
+ }
+ continue
+ }
+ val nodes = mutableMapOf<String, PreferenceHierarchyNode?>()
+ for (coordinate in coordinates) nodes[coordinate.key] = null
+ screenMetadata.getPreferenceHierarchy(application).forEachRecursively {
+ val metadata = it.metadata
+ val key = metadata.key
+ if (nodes.containsKey(key)) nodes[key] = it
+ }
+ for (coordinate in coordinates) {
+ val node = nodes[coordinate.key]
+ if (node == null) {
+ errors[coordinate] = PreferenceGetterErrorCode.NOT_FOUND
+ continue
+ }
+ val metadata = node.metadata
+ try {
+ val preferenceProto =
+ metadata.toProto(
+ application,
+ myUid,
+ callingUid,
+ screenMetadata,
+ metadata.key == screenMetadata.key,
+ flags,
+ )
+ if (flags == PreferenceGetterFlags.VALUE && !preferenceProto.hasValue()) {
+ errors[coordinate] = PreferenceGetterErrorCode.DISALLOW
+ } else {
+ preferences[coordinate] = preferenceProto
+ }
+ } catch (e: Exception) {
+ errors[coordinate] = PreferenceGetterErrorCode.INTERNAL_ERROR
+ }
+ }
+ }
+ return PreferenceGetterResponse(errors, preferences)
+ }
+
+ override val requestCodec = PreferenceGetterRequestCodec()
+
+ override val responseCodec = PreferenceGetterResponseCodec()
+}
diff --git a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGetterCodecs.kt b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGetterCodecs.kt
new file mode 100644
index 0000000..ff14eb5
--- /dev/null
+++ b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGetterCodecs.kt
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.graph
+
+import android.os.Bundle
+import android.os.Parcel
+import com.android.settingslib.graph.proto.PreferenceProto
+import com.android.settingslib.ipc.MessageCodec
+import java.util.Arrays
+
+/** Message codec for [PreferenceGetterRequest]. */
+class PreferenceGetterRequestCodec : MessageCodec<PreferenceGetterRequest> {
+ override fun encode(data: PreferenceGetterRequest) =
+ Bundle(2).apply {
+ putParcelableArray(null, data.preferences)
+ putInt(FLAGS, data.flags)
+ }
+
+ @Suppress("DEPRECATION")
+ override fun decode(data: Bundle): PreferenceGetterRequest {
+ data.classLoader = PreferenceCoordinate::class.java.classLoader
+ val array = data.getParcelableArray(null)!!
+
+ return PreferenceGetterRequest(
+ Arrays.copyOf(array, array.size, Array<PreferenceCoordinate>::class.java),
+ data.getInt(FLAGS),
+ )
+ }
+
+ companion object {
+ private const val FLAGS = "f"
+ }
+}
+
+/** Message codec for [PreferenceGetterResponse]. */
+class PreferenceGetterResponseCodec : MessageCodec<PreferenceGetterResponse> {
+ override fun encode(data: PreferenceGetterResponse) =
+ Bundle(2).apply {
+ data.errors.toErrorsByteArray()?.let { putByteArray(ERRORS, it) }
+ data.preferences.toPreferencesByteArray()?.let { putByteArray(null, it) }
+ }
+
+ private fun Map<PreferenceCoordinate, Int>.toErrorsByteArray(): ByteArray? {
+ if (isEmpty()) return null
+ val parcel = Parcel.obtain()
+ parcel.writeInt(size)
+ for ((coordinate, code) in this) {
+ coordinate.writeToParcel(parcel, 0)
+ parcel.writeInt(code)
+ }
+ val bytes = parcel.marshall()
+ parcel.recycle()
+ return bytes
+ }
+
+ private fun Map<PreferenceCoordinate, PreferenceProto>.toPreferencesByteArray(): ByteArray? {
+ if (isEmpty()) return null
+ val parcel = Parcel.obtain()
+ parcel.writeInt(size)
+ for ((coordinate, preferenceProto) in this) {
+ coordinate.writeToParcel(parcel, 0)
+ val data = preferenceProto.toByteArray()
+ parcel.writeInt(data.size)
+ parcel.writeByteArray(data)
+ }
+ val bytes = parcel.marshall()
+ parcel.recycle()
+ return bytes
+ }
+
+ override fun decode(data: Bundle) =
+ PreferenceGetterResponse(
+ data.getByteArray(ERRORS).toErrors(),
+ data.getByteArray(null).toPreferences(),
+ )
+
+ private fun ByteArray?.toErrors(): Map<PreferenceCoordinate, Int> {
+ if (this == null) return emptyMap()
+ val parcel = Parcel.obtain()
+ parcel.unmarshall(this, 0, size)
+ parcel.setDataPosition(0)
+ val count = parcel.readInt()
+ val errors = mutableMapOf<PreferenceCoordinate, Int>()
+ repeat(count) {
+ val coordinate = PreferenceCoordinate(parcel)
+ errors[coordinate] = parcel.readInt()
+ }
+ parcel.recycle()
+ return errors
+ }
+
+ private fun ByteArray?.toPreferences(): Map<PreferenceCoordinate, PreferenceProto> {
+ if (this == null) return emptyMap()
+ val parcel = Parcel.obtain()
+ parcel.unmarshall(this, 0, size)
+ parcel.setDataPosition(0)
+ val count = parcel.readInt()
+ val preferences = mutableMapOf<PreferenceCoordinate, PreferenceProto>()
+ repeat(count) {
+ val coordinate = PreferenceCoordinate(parcel)
+ val bytes = parcel.readInt()
+ val array = ByteArray(bytes).also { parcel.readByteArray(it) }
+ preferences[coordinate] = PreferenceProto.parseFrom(array)
+ }
+ parcel.recycle()
+ return preferences
+ }
+
+ companion object {
+ private const val ERRORS = "e"
+ }
+}
diff --git a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGetterFlags.kt b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGetterFlags.kt
new file mode 100644
index 0000000..632f154
--- /dev/null
+++ b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGetterFlags.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.graph
+
+/** Flags for preference getter operation. */
+object PreferenceGetterFlags {
+ const val VALUE = 1 shl 0
+ const val VALUE_DESCRIPTOR = 1 shl 1
+ const val METADATA = 1 shl 2
+ const val ALL = (1 shl 3) - 1
+
+ fun Int.includeValue() = (this and VALUE) != 0
+
+ fun Int.includeValueDescriptor() = (this and VALUE_DESCRIPTOR) != 0
+
+ fun Int.includeMetadata() = (this and METADATA) != 0
+}
diff --git a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGraphBuilder.kt b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGraphBuilder.kt
index 2256bb3..a65d24c 100644
--- a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGraphBuilder.kt
+++ b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGraphBuilder.kt
@@ -31,6 +31,9 @@
import androidx.preference.PreferenceGroup
import androidx.preference.PreferenceScreen
import androidx.preference.TwoStatePreference
+import com.android.settingslib.graph.PreferenceGetterFlags.includeMetadata
+import com.android.settingslib.graph.PreferenceGetterFlags.includeValue
+import com.android.settingslib.graph.PreferenceGetterFlags.includeValueDescriptor
import com.android.settingslib.graph.proto.PreferenceGraphProto
import com.android.settingslib.graph.proto.PreferenceGroupProto
import com.android.settingslib.graph.proto.PreferenceProto
@@ -41,7 +44,6 @@
import com.android.settingslib.metadata.PersistentPreference
import com.android.settingslib.metadata.PreferenceAvailabilityProvider
import com.android.settingslib.metadata.PreferenceHierarchy
-import com.android.settingslib.metadata.PreferenceHierarchyNode
import com.android.settingslib.metadata.PreferenceMetadata
import com.android.settingslib.metadata.PreferenceRestrictionProvider
import com.android.settingslib.metadata.PreferenceScreenBindingKeyProvider
@@ -50,6 +52,7 @@
import com.android.settingslib.metadata.PreferenceSummaryProvider
import com.android.settingslib.metadata.PreferenceTitleProvider
import com.android.settingslib.metadata.RangeValue
+import com.android.settingslib.metadata.ReadWritePermit
import com.android.settingslib.preference.PreferenceScreenFactory
import com.android.settingslib.preference.PreferenceScreenProvider
import java.util.Locale
@@ -60,14 +63,17 @@
/** Builder of preference graph. */
class PreferenceGraphBuilder
-private constructor(private val context: Context, private val request: GetPreferenceGraphRequest) {
+private constructor(
+ private val context: Context,
+ private val myUid: Int,
+ private val callingUid: Int,
+ private val request: GetPreferenceGraphRequest,
+) {
private val preferenceScreenFactory by lazy {
PreferenceScreenFactory(context.ofLocale(request.locale))
}
private val builder by lazy { PreferenceGraphProto.newBuilder() }
private val visitedScreens = mutableSetOf<String>().apply { addAll(request.visitedScreens) }
- private val includeValue = request.includeValue
- private val includeValueDescriptor = request.includeValueDescriptor
private suspend fun init() {
for (key in request.screenKeys) {
@@ -133,7 +139,7 @@
null
}
- private suspend fun addPreferenceScreenFromRegistry(key: String): Boolean {
+ suspend fun addPreferenceScreenFromRegistry(key: String): Boolean {
val metadata = PreferenceScreenRegistry[key] ?: return false
return addPreferenceScreenMetadata(metadata)
}
@@ -146,7 +152,7 @@
}
}
- private suspend fun addPreferenceScreenProvider(activityClass: Class<*>) {
+ suspend fun addPreferenceScreenProvider(activityClass: Class<*>) {
Log.d(TAG, "add $activityClass")
createPreferenceScreen { activityClass.newInstance() }
?.let {
@@ -229,7 +235,7 @@
enabled = isEnabled
available = isVisible
persistent = isPersistent
- if (includeValue && isPersistent && this@toProto is TwoStatePreference) {
+ if (request.flags.includeValue() && isPersistent && this@toProto is TwoStatePreference) {
value = preferenceValueProto { booleanValue = this@toProto.isChecked }
}
this@toProto.fragment.toActionTarget(preferenceExtras)?.let {
@@ -243,14 +249,14 @@
screenMetadata: PreferenceScreenMetadata,
isRoot: Boolean,
): PreferenceGroupProto = preferenceGroupProto {
- preference = toProto(screenMetadata, this@toProto, isRoot)
+ preference = toProto(screenMetadata, this@toProto.metadata, isRoot)
forEachAsync {
addPreferences(
preferenceOrGroupProto {
if (it is PreferenceHierarchy) {
group = it.toProto(screenMetadata, false)
} else {
- preference = toProto(screenMetadata, it, false)
+ preference = toProto(screenMetadata, it.metadata, false)
}
}
)
@@ -259,93 +265,19 @@
private suspend fun toProto(
screenMetadata: PreferenceScreenMetadata,
- node: PreferenceHierarchyNode,
+ metadata: PreferenceMetadata,
isRoot: Boolean,
- ) = preferenceProto {
- val metadata = node.metadata
- key = metadata.key
- metadata.getTitleTextProto(isRoot)?.let { title = it }
- if (metadata.summary != 0) {
- summary = textProto { resourceId = metadata.summary }
- } else {
- (metadata as? PreferenceSummaryProvider)?.getSummary(context)?.let {
- summary = textProto { string = it.toString() }
+ ) =
+ metadata.toProto(context, myUid, callingUid, screenMetadata, isRoot, request.flags).also {
+ if (metadata is PreferenceScreenMetadata) {
+ @Suppress("CheckReturnValue") addPreferenceScreenMetadata(metadata)
}
- }
- val metadataIcon = metadata.getPreferenceIcon(context)
- if (metadataIcon != 0) icon = metadataIcon
- if (metadata.keywords != 0) keywords = metadata.keywords
- val preferenceExtras = metadata.extras(context)
- preferenceExtras?.let { extras = it.toProto() }
- indexable = metadata.isIndexable(context)
- enabled = metadata.isEnabled(context)
- if (metadata is PreferenceAvailabilityProvider) {
- available = metadata.isAvailable(context)
- }
- if (metadata is PreferenceRestrictionProvider) {
- restricted = metadata.isRestricted(context)
- }
- persistent = metadata.isPersistent(context)
- if (persistent) {
- if (includeValue && metadata is PersistentPreference<*>) {
- value = preferenceValueProto {
- when (metadata) {
- is BooleanValue ->
- metadata
- .storage(context)
- .getValue(metadata.key, Boolean::class.javaObjectType)
- ?.let { booleanValue = it }
- is RangeValue -> {
- metadata
- .storage(context)
- .getValue(metadata.key, Int::class.javaObjectType)
- ?.let { intValue = it }
- }
- else -> {}
- }
- }
- }
- if (includeValueDescriptor) {
- valueDescriptor = preferenceValueDescriptorProto {
- when (metadata) {
- is BooleanValue -> booleanType = true
- is RangeValue -> rangeValue = rangeValueProto {
- min = metadata.getMinValue(context)
- max = metadata.getMaxValue(context)
- step = metadata.getIncrementStep(context)
- }
- else -> {}
- }
+ metadata.intent(context)?.resolveActivity(context.packageManager)?.let {
+ if (it.packageName == context.packageName) {
+ add(it.className)
}
}
}
- if (metadata is PreferenceScreenMetadata) {
- @Suppress("CheckReturnValue") addPreferenceScreenMetadata(metadata)
- }
- metadata.intent(context)?.let { actionTarget = it.toActionTarget() }
- screenMetadata.getLaunchIntent(context, metadata)?.let { launchIntent = it.toProto() }
- }
-
- private fun PreferenceMetadata.getTitleTextProto(isRoot: Boolean): TextProto? {
- if (isRoot && this is PreferenceScreenMetadata) {
- val titleRes = screenTitle
- if (titleRes != 0) {
- return textProto { resourceId = titleRes }
- } else {
- getScreenTitle(context)?.let {
- return textProto { string = it.toString() }
- }
- }
- } else {
- val titleRes = title
- if (titleRes != 0) {
- return textProto { resourceId = titleRes }
- }
- }
- return (this as? PreferenceTitleProvider)?.getTitle(context)?.let {
- textProto { string = it.toString() }
- }
- }
private suspend fun String?.toActionTarget(extras: Bundle?): ActionTarget? {
if (this.isNullOrEmpty()) return null
@@ -399,24 +331,131 @@
return null
}
- private suspend fun Intent.toActionTarget(): ActionTarget {
- if (component?.packageName == "") {
- setClassName(context, component!!.className)
- }
- resolveActivity(context.packageManager)?.let {
- if (it.packageName == context.packageName) {
- add(it.className)
+ private suspend fun Intent.toActionTarget() =
+ toActionTarget(context).also {
+ resolveActivity(context.packageManager)?.let {
+ if (it.packageName == context.packageName) {
+ add(it.className)
+ }
}
}
- return actionTargetProto { intent = toProto() }
- }
companion object {
- suspend fun of(context: Context, request: GetPreferenceGraphRequest) =
- PreferenceGraphBuilder(context, request).also { it.init() }
+ suspend fun of(
+ context: Context,
+ myUid: Int,
+ callingUid: Int,
+ request: GetPreferenceGraphRequest,
+ ) = PreferenceGraphBuilder(context, myUid, callingUid, request).also { it.init() }
}
}
+fun PreferenceMetadata.toProto(
+ context: Context,
+ myUid: Int,
+ callingUid: Int,
+ screenMetadata: PreferenceScreenMetadata,
+ isRoot: Boolean,
+ flags: Int,
+) = preferenceProto {
+ val metadata = this@toProto
+ key = metadata.key
+ if (flags.includeMetadata()) {
+ metadata.getTitleTextProto(context, isRoot)?.let { title = it }
+ if (metadata.summary != 0) {
+ summary = textProto { resourceId = metadata.summary }
+ } else {
+ (metadata as? PreferenceSummaryProvider)?.getSummary(context)?.let {
+ summary = textProto { string = it.toString() }
+ }
+ }
+ val metadataIcon = metadata.getPreferenceIcon(context)
+ if (metadataIcon != 0) icon = metadataIcon
+ if (metadata.keywords != 0) keywords = metadata.keywords
+ val preferenceExtras = metadata.extras(context)
+ preferenceExtras?.let { extras = it.toProto() }
+ indexable = metadata.isIndexable(context)
+ enabled = metadata.isEnabled(context)
+ if (metadata is PreferenceAvailabilityProvider) {
+ available = metadata.isAvailable(context)
+ }
+ if (metadata is PreferenceRestrictionProvider) {
+ restricted = metadata.isRestricted(context)
+ }
+ metadata.intent(context)?.let { actionTarget = it.toActionTarget(context) }
+ screenMetadata.getLaunchIntent(context, metadata)?.let { launchIntent = it.toProto() }
+ }
+ persistent = metadata.isPersistent(context)
+ if (persistent) {
+ if (
+ flags.includeValue() &&
+ enabled &&
+ (!hasAvailable() || available) &&
+ (!hasRestricted() || !restricted) &&
+ metadata is PersistentPreference<*> &&
+ metadata.getReadPermit(context, myUid, callingUid) == ReadWritePermit.ALLOW
+ ) {
+ value = preferenceValueProto {
+ when (metadata) {
+ is BooleanValue ->
+ metadata
+ .storage(context)
+ .getValue(metadata.key, Boolean::class.javaObjectType)
+ ?.let { booleanValue = it }
+ is RangeValue -> {
+ metadata
+ .storage(context)
+ .getValue(metadata.key, Int::class.javaObjectType)
+ ?.let { intValue = it }
+ }
+ else -> {}
+ }
+ }
+ }
+ if (flags.includeValueDescriptor()) {
+ valueDescriptor = preferenceValueDescriptorProto {
+ when (metadata) {
+ is BooleanValue -> booleanType = true
+ is RangeValue -> rangeValue = rangeValueProto {
+ min = metadata.getMinValue(context)
+ max = metadata.getMaxValue(context)
+ step = metadata.getIncrementStep(context)
+ }
+ else -> {}
+ }
+ }
+ }
+ }
+}
+
+private fun PreferenceMetadata.getTitleTextProto(context: Context, isRoot: Boolean): TextProto? {
+ if (isRoot && this is PreferenceScreenMetadata) {
+ val titleRes = screenTitle
+ if (titleRes != 0) {
+ return textProto { resourceId = titleRes }
+ } else {
+ getScreenTitle(context)?.let {
+ return textProto { string = it.toString() }
+ }
+ }
+ } else {
+ val titleRes = title
+ if (titleRes != 0) {
+ return textProto { resourceId = titleRes }
+ }
+ }
+ return (this as? PreferenceTitleProvider)?.getTitle(context)?.let {
+ textProto { string = it.toString() }
+ }
+}
+
+private fun Intent.toActionTarget(context: Context): ActionTarget {
+ if (component?.packageName == "") {
+ setClassName(context, component!!.className)
+ }
+ return actionTargetProto { intent = toProto() }
+}
+
@SuppressLint("AppBundleLocaleChanges")
internal fun Context.ofLocale(locale: Locale?): Context {
if (locale == null) return this
diff --git a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceSetterApi.kt b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceSetterApi.kt
index 6e4db1d..7cfce0d 100644
--- a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceSetterApi.kt
+++ b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceSetterApi.kt
@@ -22,6 +22,7 @@
import com.android.settingslib.graph.proto.PreferenceValueProto
import com.android.settingslib.ipc.ApiDescriptor
import com.android.settingslib.ipc.ApiHandler
+import com.android.settingslib.ipc.ApiPermissionChecker
import com.android.settingslib.ipc.IntMessageCodec
import com.android.settingslib.ipc.MessageCodec
import com.android.settingslib.metadata.BooleanValue
@@ -45,7 +46,11 @@
PreferenceSetterResult.OK,
PreferenceSetterResult.UNSUPPORTED,
PreferenceSetterResult.DISABLED,
+ PreferenceSetterResult.RESTRICTED,
PreferenceSetterResult.UNAVAILABLE,
+ PreferenceSetterResult.REQUIRE_APP_PERMISSION,
+ PreferenceSetterResult.REQUIRE_USER_AGREEMENT,
+ PreferenceSetterResult.DISALLOW,
PreferenceSetterResult.INVALID_REQUEST,
PreferenceSetterResult.INTERNAL_ERROR,
)
@@ -87,14 +92,17 @@
}
/** Preference setter API implementation. */
-class PreferenceSetterApiHandler(override val id: Int) : ApiHandler<PreferenceSetterRequest, Int> {
+class PreferenceSetterApiHandler(
+ override val id: Int,
+ private val permissionChecker: ApiPermissionChecker<PreferenceSetterRequest>,
+) : ApiHandler<PreferenceSetterRequest, Int> {
override fun hasPermission(
application: Application,
myUid: Int,
callingUid: Int,
request: PreferenceSetterRequest,
- ): Boolean = true
+ ) = permissionChecker.hasPermission(application, myUid, callingUid, request)
override suspend fun invoke(
application: Application,
diff --git a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/ProtoConverters.kt b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/ProtoConverters.kt
index d9b9590..1bda277 100644
--- a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/ProtoConverters.kt
+++ b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/ProtoConverters.kt
@@ -16,8 +16,10 @@
package com.android.settingslib.graph
+import android.content.ComponentName
import android.content.Context
import android.content.Intent
+import android.net.Uri
import android.os.Bundle
import com.android.settingslib.graph.proto.BundleProto
import com.android.settingslib.graph.proto.BundleProto.BundleValue
@@ -42,6 +44,20 @@
this@toProto.type?.let { mimeType = it }
}
+fun IntentProto.toIntent(): Intent? {
+ if (!hasComponent()) return null
+ val componentName = ComponentName.unflattenFromString(component) ?: return null
+ val intent = Intent()
+ intent.component = componentName
+ if (hasAction()) intent.action = action
+ if (hasData()) intent.data = Uri.parse(data)
+ if (hasPkg()) intent.`package` = pkg
+ if (hasFlags()) intent.flags = flags
+ if (hasExtras()) intent.putExtras(extras.toBundle())
+ if (hasMimeType()) intent.setType(mimeType)
+ return intent
+}
+
fun Bundle.toProto(): BundleProto = bundleProto {
fun toProto(value: Any): BundleValue = bundleValueProto {
when (value) {
@@ -61,14 +77,18 @@
}
}
-fun BundleValue.stringify(): String =
- when {
- hasBooleanValue() -> "$valueCase"
- hasBytesValue() -> "$bytesValue"
- hasIntValue() -> "$intValue"
- hasLongValue() -> "$longValue"
- hasStringValue() -> stringValue
- hasDoubleValue() -> "$doubleValue"
- hasBundleValue() -> "$bundleValue"
- else -> "Unknown"
+fun BundleProto.toBundle(): Bundle =
+ Bundle().apply {
+ for ((key, value) in valuesMap) {
+ when {
+ value.hasBooleanValue() -> putBoolean(key, value.booleanValue)
+ value.hasBytesValue() -> putByteArray(key, value.bytesValue.toByteArray())
+ value.hasIntValue() -> putInt(key, value.intValue)
+ value.hasLongValue() -> putLong(key, value.longValue)
+ value.hasStringValue() -> putString(key, value.stringValue)
+ value.hasDoubleValue() -> putDouble(key, value.doubleValue)
+ value.hasBundleValue() -> putBundle(key, value.bundleValue.toBundle())
+ else -> throw IllegalArgumentException("Unknown type: ${value.javaClass} $value")
+ }
+ }
}
diff --git a/packages/SettingsLib/Ipc/src/com/android/settingslib/ipc/ApiHandler.kt b/packages/SettingsLib/Ipc/src/com/android/settingslib/ipc/ApiHandler.kt
index 802141d..4febd89 100644
--- a/packages/SettingsLib/Ipc/src/com/android/settingslib/ipc/ApiHandler.kt
+++ b/packages/SettingsLib/Ipc/src/com/android/settingslib/ipc/ApiHandler.kt
@@ -56,6 +56,27 @@
val responseCodec: MessageCodec<Response>
}
+/** Permission checker for api. */
+fun interface ApiPermissionChecker<R> {
+ /**
+ * Returns if the request is permitted.
+ *
+ * @param application application context
+ * @param myUid uid of current process
+ * @param callingUid uid of peer process
+ * @param request API request
+ * @return `false` if permission is denied, otherwise `true`
+ */
+ fun hasPermission(application: Application, myUid: Int, callingUid: Int, request: R): Boolean
+
+ companion object {
+ private val ALWAYS_ALLOW = ApiPermissionChecker<Any> { _, _, _, _ -> true }
+
+ @Suppress("UNCHECKED_CAST")
+ fun <T> alwaysAllow(): ApiPermissionChecker<T> = ALWAYS_ALLOW as ApiPermissionChecker<T>
+ }
+}
+
/**
* Handler of API.
*
@@ -64,18 +85,8 @@
*
* The implementation must be threadsafe.
*/
-interface ApiHandler<Request, Response> : ApiDescriptor<Request, Response> {
- /**
- * Returns if the request is permitted.
- *
- * @return `false` if permission is denied, otherwise `true`
- */
- fun hasPermission(
- application: Application,
- myUid: Int,
- callingUid: Int,
- request: Request,
- ): Boolean
+interface ApiHandler<Request, Response> :
+ ApiDescriptor<Request, Response>, ApiPermissionChecker<Request> {
/**
* Invokes the API.
diff --git a/packages/SettingsLib/Ipc/src/com/android/settingslib/ipc/MessengerServiceClient.kt b/packages/SettingsLib/Ipc/src/com/android/settingslib/ipc/MessengerServiceClient.kt
index 7ffefed..ef907e1 100644
--- a/packages/SettingsLib/Ipc/src/com/android/settingslib/ipc/MessengerServiceClient.kt
+++ b/packages/SettingsLib/Ipc/src/com/android/settingslib/ipc/MessengerServiceClient.kt
@@ -320,6 +320,11 @@
}
}
+ override fun onNullBinding(name: ComponentName) {
+ Log.i(TAG, "onNullBinding $name")
+ close(ClientBindServiceException(null))
+ }
+
internal open fun drainPendingRequests() {
disposableHandle = null
if (pendingRequests.isEmpty()) {
diff --git a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceHierarchy.kt b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceHierarchy.kt
index a2b826a..9179f8f 100644
--- a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceHierarchy.kt
+++ b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceHierarchy.kt
@@ -58,6 +58,9 @@
/** Specifies preference order in the hierarchy. */
infix fun PreferenceHierarchyNode.order(order: Int) = apply { this.order = order }
+ /** Specifies preference order in the hierarchy for group. */
+ infix fun PreferenceHierarchy.order(order: Int) = apply { this.order = order }
+
/** Adds a preference to the hierarchy. */
@JvmOverloads
fun add(metadata: PreferenceMetadata, order: Int? = null) {
diff --git a/packages/SettingsLib/Service/src/com/android/settingslib/service/PreferenceGraphApi.kt b/packages/SettingsLib/Service/src/com/android/settingslib/service/PreferenceGraphApi.kt
index 6e38df1..1823ba6 100644
--- a/packages/SettingsLib/Service/src/com/android/settingslib/service/PreferenceGraphApi.kt
+++ b/packages/SettingsLib/Service/src/com/android/settingslib/service/PreferenceGraphApi.kt
@@ -19,9 +19,14 @@
import android.app.Application
import com.android.settingslib.graph.GetPreferenceGraphApiHandler
import com.android.settingslib.graph.GetPreferenceGraphRequest
+import com.android.settingslib.ipc.ApiPermissionChecker
+import com.android.settingslib.preference.PreferenceScreenProvider
/** Api to get preference graph. */
-internal class PreferenceGraphApi : GetPreferenceGraphApiHandler() {
+internal class PreferenceGraphApi(
+ preferenceScreenProviders: Set<Class<out PreferenceScreenProvider>>,
+ private val permissionChecker: ApiPermissionChecker<GetPreferenceGraphRequest>,
+) : GetPreferenceGraphApiHandler(preferenceScreenProviders) {
override val id: Int
get() = API_GET_PREFERENCE_GRAPH
@@ -31,5 +36,5 @@
myUid: Int,
callingUid: Int,
request: GetPreferenceGraphRequest,
- ) = true
+ ) = permissionChecker.hasPermission(application, myUid, callingUid, request)
}
diff --git a/packages/SettingsLib/Service/src/com/android/settingslib/service/PreferenceService.kt b/packages/SettingsLib/Service/src/com/android/settingslib/service/PreferenceService.kt
index 8ebb145..7cb36db 100644
--- a/packages/SettingsLib/Service/src/com/android/settingslib/service/PreferenceService.kt
+++ b/packages/SettingsLib/Service/src/com/android/settingslib/service/PreferenceService.kt
@@ -16,10 +16,16 @@
package com.android.settingslib.service
+import com.android.settingslib.graph.GetPreferenceGraphRequest
+import com.android.settingslib.graph.PreferenceGetterApiHandler
+import com.android.settingslib.graph.PreferenceGetterRequest
import com.android.settingslib.graph.PreferenceSetterApiHandler
+import com.android.settingslib.graph.PreferenceSetterRequest
import com.android.settingslib.ipc.ApiHandler
+import com.android.settingslib.ipc.ApiPermissionChecker
import com.android.settingslib.ipc.MessengerService
import com.android.settingslib.ipc.PermissionChecker
+import com.android.settingslib.preference.PreferenceScreenProvider
/**
* Preference service providing a bunch of APIs.
@@ -28,14 +34,25 @@
* [PREFERENCE_SERVICE_ACTION].
*/
open class PreferenceService(
- permissionChecker: PermissionChecker,
name: String = "PreferenceService",
+ permissionChecker: PermissionChecker = PermissionChecker { _, _, _ -> true },
+ preferenceScreenProviders: Set<Class<out PreferenceScreenProvider>> = setOf(),
+ graphPermissionChecker: ApiPermissionChecker<GetPreferenceGraphRequest>? = null,
+ setterPermissionChecker: ApiPermissionChecker<PreferenceSetterRequest>? = null,
+ getterPermissionChecker: ApiPermissionChecker<PreferenceGetterRequest>? = null,
+ vararg apiHandlers: ApiHandler<*, *>,
) :
MessengerService(
- listOf<ApiHandler<*, *>>(
- PreferenceGraphApi(),
- PreferenceSetterApiHandler(API_PREFERENCE_SETTER),
- ),
+ mutableListOf<ApiHandler<*, *>>().apply {
+ graphPermissionChecker?.let { add(PreferenceGraphApi(preferenceScreenProviders, it)) }
+ setterPermissionChecker?.let {
+ add(PreferenceSetterApiHandler(API_PREFERENCE_SETTER, it))
+ }
+ getterPermissionChecker?.let {
+ add(PreferenceGetterApiHandler(API_PREFERENCE_GETTER, it))
+ }
+ addAll(apiHandlers)
+ },
permissionChecker,
name,
)
diff --git a/packages/SettingsLib/Service/src/com/android/settingslib/service/ServiceApiConstants.kt b/packages/SettingsLib/Service/src/com/android/settingslib/service/ServiceApiConstants.kt
index 1f38a66..d71405e 100644
--- a/packages/SettingsLib/Service/src/com/android/settingslib/service/ServiceApiConstants.kt
+++ b/packages/SettingsLib/Service/src/com/android/settingslib/service/ServiceApiConstants.kt
@@ -18,5 +18,17 @@
const val PREFERENCE_SERVICE_ACTION = "com.android.settingslib.PREFERENCE_SERVICE"
+/** API id for retrieving preference graph. */
internal const val API_GET_PREFERENCE_GRAPH = 1
+
+/** API id for preference value setter. */
internal const val API_PREFERENCE_SETTER = 2
+
+/** API id for preference getter. */
+internal const val API_PREFERENCE_GETTER = 3
+
+/**
+ * The max API id reserved for internal preference service usages. Custom API id should start with
+ * **1000** to avoid conflict.
+ */
+internal const val API_MAX_RESERVED = 999
diff --git a/packages/SettingsLib/SettingsTheme/res/color-v31/settingslib_surface_light.xml b/packages/SettingsLib/SettingsTheme/res/color-v31/settingslib_surface_light.xml
index b46181e..8b574aa 100644
--- a/packages/SettingsLib/SettingsTheme/res/color-v31/settingslib_surface_light.xml
+++ b/packages/SettingsLib/SettingsTheme/res/color-v31/settingslib_surface_light.xml
@@ -13,7 +13,7 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<!--Deprecated. After sdk 35, don't use it, using materialColorOnSurfaceInverse in light theme -->
+<!--Deprecated. After sdk 35, don't use it, using materialColorInverseOnSurface in light theme -->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="@android:color/system_neutral1_500" android:lStar="98" />
</selector>
\ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/values-night-v31/colors.xml b/packages/SettingsLib/SettingsTheme/res/values-night-v31/colors.xml
index 313748d..46ec62e 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-night-v31/colors.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-night-v31/colors.xml
@@ -51,7 +51,7 @@
<color name="settingslib_text_color_preference_category_title">@android:color/system_accent1_100</color>
- <!--Deprecated. After sdk 35, don't use it, using materialColorOnSurfaceInverse in dark theme -->
+ <!--Deprecated. After sdk 35, don't use it, using materialColorInverseOnSurface in dark theme -->
<color name="settingslib_surface_dark">@android:color/system_neutral1_800</color>
<!--Deprecated. After sdk 35, don't use it-->
diff --git a/packages/SettingsLib/SettingsTheme/res/values-night-v35/colors.xml b/packages/SettingsLib/SettingsTheme/res/values-night-v35/colors.xml
index 94ff02d..84a3ed6 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-night-v35/colors.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-night-v35/colors.xml
@@ -53,11 +53,11 @@
<color name="settingslib_materialColorSurfaceContainerLow">@android:color/system_surface_container_low_dark</color>
<color name="settingslib_materialColorOnPrimaryContainer">@android:color/system_on_primary_container_dark</color>
<color name="settingslib_materialColorOnErrorContainer">@android:color/system_on_error_container_dark</color>
- <color name="settingslib_materialColorOnSurfaceInverse">@android:color/system_on_surface_light</color>
+ <color name="settingslib_materialColorInverseOnSurface">@android:color/system_on_surface_light</color>
<color name="settingslib_materialColorSecondaryContainer">@android:color/system_secondary_container_dark</color>
<color name="settingslib_materialColorErrorContainer">@android:color/system_error_container_dark</color>
- <color name="settingslib_materialColorPrimaryInverse">@android:color/system_primary_light</color>
- <color name="settingslib_materialColorSurfaceInverse">@android:color/system_surface_light</color>
+ <color name="settingslib_materialColorInversePrimary">@android:color/system_primary_light</color>
+ <color name="settingslib_materialColorInverseSurface">@android:color/system_surface_light</color>
<color name="settingslib_materialColorSurfaceVariant">@android:color/system_surface_variant_dark</color>
<color name="settingslib_materialColorTertiaryContainer">@android:color/system_tertiary_container_dark</color>
<color name="settingslib_materialColorPrimaryContainer">@android:color/system_primary_container_dark</color>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-v31/colors.xml b/packages/SettingsLib/SettingsTheme/res/values-v31/colors.xml
index b99ee51..fef92b7 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-v31/colors.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-v31/colors.xml
@@ -67,9 +67,9 @@
<color name="settingslib_accent_primary_variant">@android:color/system_accent1_600</color>
<!--Deprecated. After sdk 35 don't use it.-->
<color name="settingslib_accent_secondary_device_default">@android:color/system_accent2_100</color>
- <!--Deprecated. After sdk 35 don't use it.using materialColorOnSurfaceInverse in dark theme-->
+ <!--Deprecated. After sdk 35 don't use it.using materialColorInverseOnSurface in dark theme-->
<color name="settingslib_background_device_default_dark">@android:color/system_neutral1_900</color>
- <!--Deprecated. After sdk 35 don't use it. using materialColorOnSurfaceInverse in light theme-->
+ <!--Deprecated. After sdk 35 don't use it. using materialColorInverseOnSurface in light theme-->
<color name="settingslib_background_device_default_light">@android:color/system_neutral1_50</color>
<!--Deprecated. After sdk 35 don't use it. using materialColorOnSurface-->
<color name="settingslib_text_color_primary_device_default">@android:color/system_neutral1_900</color>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-v35/colors.xml b/packages/SettingsLib/SettingsTheme/res/values-v35/colors.xml
index 8b95016..90c19e1 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-v35/colors.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-v35/colors.xml
@@ -66,16 +66,16 @@
<color name="settingslib_materialColorSecondaryFixedDim">@android:color/system_secondary_fixed_dim</color>
<color name="settingslib_materialColorOnErrorContainer">@android:color/system_on_error_container_light</color>
<color name="settingslib_materialColorOnSecondaryFixed">@android:color/system_on_secondary_fixed</color>
- <color name="settingslib_materialColorOnSurfaceInverse">@android:color/system_on_surface_dark</color>
+ <color name="settingslib_materialColorInverseOnSurface">@android:color/system_on_surface_dark</color>
<color name="settingslib_materialColorTertiaryFixedDim">@android:color/system_tertiary_fixed_dim</color>
<color name="settingslib_materialColorOnTertiaryFixed">@android:color/system_on_tertiary_fixed</color>
<color name="settingslib_materialColorPrimaryFixedDim">@android:color/system_primary_fixed_dim</color>
<color name="settingslib_materialColorSecondaryContainer">@android:color/system_secondary_container_light</color>
<color name="settingslib_materialColorErrorContainer">@android:color/system_error_container_light</color>
<color name="settingslib_materialColorOnPrimaryFixed">@android:color/system_on_primary_fixed</color>
- <color name="settingslib_materialColorPrimaryInverse">@android:color/system_primary_dark</color>
+ <color name="settingslib_materialColorInversePrimary">@android:color/system_primary_dark</color>
<color name="settingslib_materialColorSecondaryFixed">@android:color/system_secondary_fixed</color>
- <color name="settingslib_materialColorSurfaceInverse">@android:color/system_surface_dark</color>
+ <color name="settingslib_materialColorInverseSurface">@android:color/system_surface_dark</color>
<color name="settingslib_materialColorSurfaceVariant">@android:color/system_surface_variant_light</color>
<color name="settingslib_materialColorTertiaryContainer">@android:color/system_tertiary_container_light</color>
<color name="settingslib_materialColorTertiaryFixed">@android:color/system_tertiary_fixed</color>
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/ModifierExt.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/ModifierExt.kt
index e883a4a..25bb46c 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/ModifierExt.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/ModifierExt.kt
@@ -25,3 +25,36 @@
if (contentDescription != null) this.semantics {
this.contentDescription = contentDescription
} else this
+
+/**
+ * Concatenates this modifier with another if `condition` is true.
+ *
+ * This method allows inline conditional addition of modifiers to a modifier chain. Instead of
+ * writing
+ *
+ * ```
+ * val aModifier = Modifier.a()
+ * val bModifier = if(condition) aModifier.b() else aModifier
+ * Composable(modifier = bModifier)
+ * ```
+ *
+ * You can instead write
+ *
+ * ```
+ * Composable(modifier = Modifier.a().thenIf(condition){
+ * Modifier.b()
+ * }
+ * ```
+ *
+ * This makes the modifier chain easier to read.
+ *
+ * Note that unlike the non-factory version, the conditional modifier is recreated each time, and
+ * may never be created at all.
+ *
+ * @param condition Whether or not to apply the modifiers.
+ * @param factory Creates the modifier to concatenate with the current one.
+ * @return a Modifier representing this modifier followed by other in sequence.
+ * @see Modifier.then
+ */
+inline fun Modifier.thenIf(condition: Boolean, crossinline factory: () -> Modifier): Modifier =
+ if (condition) this.then(factory()) else this
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/BaseLayout.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/BaseLayout.kt
index acb96be..b1bb79d 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/BaseLayout.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/BaseLayout.kt
@@ -36,12 +36,13 @@
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
-import androidx.compose.ui.unit.min
+import com.android.settingslib.spa.framework.compose.thenIf
import com.android.settingslib.spa.framework.theme.SettingsDimension
import com.android.settingslib.spa.framework.theme.SettingsOpacity.alphaForEnabled
import com.android.settingslib.spa.framework.theme.SettingsShape
import com.android.settingslib.spa.framework.theme.SettingsTheme
import com.android.settingslib.spa.framework.theme.isSpaExpressiveEnabled
+import com.android.settingslib.spa.widget.ui.LocalIsInCategory
import com.android.settingslib.spa.widget.ui.SettingsTitle
@Composable
@@ -57,18 +58,18 @@
paddingVertical: Dp = SettingsDimension.itemPaddingVertical,
widget: @Composable () -> Unit = {},
) {
+ val surfaceBright = MaterialTheme.colorScheme.surfaceBright
Row(
modifier =
modifier
.fillMaxWidth()
.semantics(mergeDescendants = true) {}
- .then(
- if (isSpaExpressiveEnabled)
- Modifier.heightIn(min = SettingsDimension.preferenceMinHeight)
- .clip(SettingsShape.CornerExtraSmall)
- .background(MaterialTheme.colorScheme.surfaceBright)
- else Modifier
- )
+ .thenIf(isSpaExpressiveEnabled) {
+ Modifier.heightIn(min = SettingsDimension.preferenceMinHeight)
+ }
+ .thenIf(isSpaExpressiveEnabled && LocalIsInCategory.current) {
+ Modifier.clip(SettingsShape.CornerExtraSmall).background(surfaceBright)
+ }
.padding(end = paddingEnd),
verticalAlignment = Alignment.CenterVertically,
) {
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Category.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Category.kt
index 28b2b4a..96d2abb 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Category.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Category.kt
@@ -31,6 +31,8 @@
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.runtime.compositionLocalOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@@ -99,7 +101,7 @@
verticalArrangement =
if (isSpaExpressiveEnabled) Arrangement.spacedBy(SettingsDimension.paddingTiny)
else Arrangement.Top,
- content = content,
+ content = { CompositionLocalProvider(LocalIsInCategory provides true) { content() } },
)
}
}
@@ -109,15 +111,14 @@
*
* @param list The list of items to display.
* @param entry The entry for each list item according to its index in list.
- * @param key Optional. The key for each item in list to provide unique item identifiers, making
- * the list more efficient.
- * @param title Optional. Category title for each item or each group of items in the list. It
- * should be decided by the index.
+ * @param key Optional. The key for each item in list to provide unique item identifiers, making the
+ * list more efficient.
+ * @param title Optional. Category title for each item or each group of items in the list. It should
+ * be decided by the index.
* @param bottomPadding Optional. Bottom outside padding of the category.
* @param state Optional. State of LazyList.
* @param content Optional. Content to be shown at the top of the category.
*/
-
@Composable
fun LazyCategory(
list: List<Any>,
@@ -144,17 +145,19 @@
verticalArrangement = Arrangement.spacedBy(SettingsDimension.paddingTiny),
state = state,
) {
- item { content() }
+ item { CompositionLocalProvider(LocalIsInCategory provides true) { content() } }
items(count = list.size, key = key) {
title?.invoke(it)?.let { title -> CategoryTitle(title) }
- val entryPreference = entry(it)
- entryPreference()
+ CompositionLocalProvider(LocalIsInCategory provides true) { entry(it)() }
}
}
}
}
+/** LocalIsInCategory containing the if the current composable is in a category. */
+internal val LocalIsInCategory = compositionLocalOf { false }
+
@Preview
@Composable
private fun CategoryPreview() {
diff --git a/packages/SettingsLib/aconfig/settingslib.aconfig b/packages/SettingsLib/aconfig/settingslib.aconfig
index bf419cc..076f82a 100644
--- a/packages/SettingsLib/aconfig/settingslib.aconfig
+++ b/packages/SettingsLib/aconfig/settingslib.aconfig
@@ -171,3 +171,10 @@
description: "Enable the user consent prompt before writing sensitive preferences via service"
bug: "378552675"
}
+
+flag {
+ name: "hearing_devices_input_routing_control"
+ namespace: "accessibility"
+ description: "Enable the input routing control in device details and hearing devices dialog."
+ bug: "349255906"
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index d0827b3..4eb0567 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -133,8 +133,9 @@
* If an ACTION_UUID intent comes in within
* MAX_UUID_DELAY_FOR_AUTO_CONNECT milliseconds, we will try auto-connect
* again with the new UUIDs
+ * The value is reset if a manual disconnection happens.
*/
- private long mConnectAttempted;
+ private long mConnectAttempted = -1;
// Active device state
private boolean mIsActiveDeviceA2dp = false;
@@ -369,6 +370,7 @@
}
public void disconnect() {
+ mConnectAttempted = -1;
synchronized (mProfileLock) {
if (getGroupId() != BluetoothCsipSetCoordinator.GROUP_ID_INVALID) {
for (CachedBluetoothDevice member : getMemberDevice()) {
@@ -983,15 +985,19 @@
}
if (BluetoothUtils.D) {
- Log.d(TAG, "onUuidChanged: Time since last connect="
- + (SystemClock.elapsedRealtime() - mConnectAttempted));
+ long lastConnectAttempted = mConnectAttempted == -1 ? 0 : mConnectAttempted;
+ Log.d(
+ TAG,
+ "onUuidChanged: Time since last connect/manual disconnect="
+ + (SystemClock.elapsedRealtime() - lastConnectAttempted));
}
/*
* If a connect was attempted earlier without any UUID, we will do the connect now.
* Otherwise, allow the connect on UUID change.
*/
- if ((mConnectAttempted + timeout) > SystemClock.elapsedRealtime()) {
+ if (mConnectAttempted != -1
+ && (mConnectAttempted + timeout) > SystemClock.elapsedRealtime()) {
Log.d(TAG, "onUuidChanged: triggering connectDevice");
connectDevice();
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InputMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/InputMediaDevice.java
index 83ee975..80e5e59 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InputMediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InputMediaDevice.java
@@ -15,6 +15,7 @@
*/
package com.android.settingslib.media;
+import static android.media.AudioDeviceInfo.TYPE_BLE_HEADSET;
import static android.media.AudioDeviceInfo.TYPE_BLUETOOTH_SCO;
import static android.media.AudioDeviceInfo.TYPE_BUILTIN_MIC;
import static android.media.AudioDeviceInfo.TYPE_USB_ACCESSORY;
@@ -103,7 +104,8 @@
TYPE_USB_DEVICE,
TYPE_USB_HEADSET,
TYPE_USB_ACCESSORY,
- TYPE_BLUETOOTH_SCO ->
+ TYPE_BLUETOOTH_SCO,
+ TYPE_BLE_HEADSET ->
true;
default -> false;
};
@@ -124,7 +126,7 @@
mProductName != null
? mProductName
: mContext.getString(R.string.media_transfer_usb_device_mic_name);
- case TYPE_BLUETOOTH_SCO ->
+ case TYPE_BLUETOOTH_SCO, TYPE_BLE_HEADSET ->
mProductName != null
? mProductName
: mContext.getString(R.string.media_transfer_bt_device_mic_name);
diff --git a/packages/SettingsLib/src/com/android/settingslib/notification/modes/TestModeBuilder.java b/packages/SettingsLib/src/com/android/settingslib/notification/modes/TestModeBuilder.java
index 5eeb49a..6842d0a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/notification/modes/TestModeBuilder.java
+++ b/packages/SettingsLib/src/com/android/settingslib/notification/modes/TestModeBuilder.java
@@ -204,6 +204,13 @@
return this;
}
+ public TestModeBuilder implicitForPackage(String pkg) {
+ setPackage(pkg);
+ setId(ZenModeConfig.implicitRuleId(pkg));
+ setName("Do Not Disturb (" + pkg + ")");
+ return this;
+ }
+
public TestModeBuilder setActive(boolean active) {
if (active) {
mConfigZenRule.enabled = true;
diff --git a/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenMode.java b/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenMode.java
index 34d3bd9..d5cfe55 100644
--- a/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenMode.java
+++ b/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenMode.java
@@ -208,6 +208,11 @@
}
@NonNull
+ public Kind getKind() {
+ return mKind;
+ }
+
+ @NonNull
public Status getStatus() {
return mStatus;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/AudioSharingRepository.kt b/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/AudioSharingRepository.kt
index b41e970..4c4ce2a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/AudioSharingRepository.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/AudioSharingRepository.kt
@@ -221,6 +221,7 @@
override suspend fun audioSharingAvailable(): Boolean {
return withContext(backgroundCoroutineContext) {
BluetoothUtils.isAudioSharingEnabled()
+ || BluetoothUtils.isAudioSharingPreviewEnabled(contentResolver)
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/volume/shared/AudioSharingLogger.kt b/packages/SettingsLib/src/com/android/settingslib/volume/shared/AudioSharingLogger.kt
index 18a4c6d..791d866 100644
--- a/packages/SettingsLib/src/com/android/settingslib/volume/shared/AudioSharingLogger.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/volume/shared/AudioSharingLogger.kt
@@ -26,4 +26,6 @@
fun onVolumeMapChanged(map: Map<Int, Int>)
fun onSetDeviceVolumeRequested(volume: Int)
+
+ fun onAudioSharingAvailabilityRequestedError(requestFrom: String, e: String)
}
\ No newline at end of file
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/FakeAudioSharingRepositoryLogger.kt b/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/FakeAudioSharingRepositoryLogger.kt
index cc4cc8d..731b3a1 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/FakeAudioSharingRepositoryLogger.kt
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/FakeAudioSharingRepositoryLogger.kt
@@ -43,4 +43,8 @@
override fun onSetDeviceVolumeRequested(volume: Int) {
mutableLogs.add("onSetVolumeRequested volume=$volume")
}
+
+ override fun onAudioSharingAvailabilityRequestedError(requestFrom: String, e: String) {
+ mutableLogs.add("onAudioSharingAvailabilityRequestedError, requestFrom=$requestFrom")
+ }
}
\ No newline at end of file
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InputMediaDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InputMediaDeviceTest.java
index 7775b91..8624c4d 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InputMediaDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InputMediaDeviceTest.java
@@ -38,6 +38,7 @@
private final int WIRED_HEADSET_ID = 2;
private final int USB_HEADSET_ID = 3;
private final int BT_HEADSET_ID = 4;
+ private final int BLE_HEADSET_ID = 5;
private final int MAX_VOLUME = 1;
private final int CURRENT_VOLUME = 0;
private final boolean IS_VOLUME_FIXED = true;
@@ -45,6 +46,7 @@
private static final String PRODUCT_NAME_WIRED_HEADSET = "My Wired Headset";
private static final String PRODUCT_NAME_USB_HEADSET = "My USB Headset";
private static final String PRODUCT_NAME_BT_HEADSET = "My Bluetooth Headset";
+ private static final String PRODUCT_NAME_BLE_HEADSET = "My BLE Headset";
@Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@@ -163,4 +165,35 @@
assertThat(btMediaDevice.getName())
.isEqualTo(mContext.getString(R.string.media_transfer_bt_device_mic_name));
}
+
+ @Test
+ public void getName_returnCorrectName_bleHeadset() {
+ InputMediaDevice bleMediaDevice =
+ InputMediaDevice.create(
+ mContext,
+ String.valueOf(BLE_HEADSET_ID),
+ AudioDeviceInfo.TYPE_BLE_HEADSET,
+ MAX_VOLUME,
+ CURRENT_VOLUME,
+ IS_VOLUME_FIXED,
+ PRODUCT_NAME_BLE_HEADSET);
+ assertThat(bleMediaDevice).isNotNull();
+ assertThat(bleMediaDevice.getName()).isEqualTo(PRODUCT_NAME_BLE_HEADSET);
+ }
+
+ @Test
+ public void getName_returnCorrectName_bleHeadset_nullProductName() {
+ InputMediaDevice bleMediaDevice =
+ InputMediaDevice.create(
+ mContext,
+ String.valueOf(BLE_HEADSET_ID),
+ AudioDeviceInfo.TYPE_BLE_HEADSET,
+ MAX_VOLUME,
+ CURRENT_VOLUME,
+ IS_VOLUME_FIXED,
+ null);
+ assertThat(bleMediaDevice).isNotNull();
+ assertThat(bleMediaDevice.getName())
+ .isEqualTo(mContext.getString(R.string.media_transfer_bt_device_mic_name));
+ }
}
diff --git a/packages/SettingsProvider/res/xml/bookmarks.xml b/packages/SettingsProvider/res/xml/bookmarks.xml
index 22d0226..645b275 100644
--- a/packages/SettingsProvider/res/xml/bookmarks.xml
+++ b/packages/SettingsProvider/res/xml/bookmarks.xml
@@ -32,6 +32,9 @@
'y': YouTube
-->
<bookmarks>
+ <!-- TODO(b/358569822): Remove this from Settings DB
+ This is legacy implementation to store bookmarks in Settings DB, which is deprecated and
+ no longer used -->
<bookmark
role="android.app.role.BROWSER"
shortcut="b" />
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
index 8f58e8c..32d4580 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
@@ -462,5 +462,16 @@
Global.Wearable.PHONE_SWITCHING_REQUEST_SOURCE_NONE,
Global.Wearable.PHONE_SWITCHING_REQUEST_SOURCE_COMPANION
));
+ VALIDATORS.put(Global.HEARING_DEVICE_LOCAL_AMBIENT_VOLUME, ANY_STRING_VALIDATOR);
+ VALIDATORS.put(Global.HEARING_DEVICE_LOCAL_NOTIFICATION, ANY_STRING_VALIDATOR);
+ VALIDATORS.put(
+ Global.Wearable.WEAR_SYSTEM_STATUS_TRAY_CONFIGURATION,
+ new DiscreteValueValidator(
+ new String[] {
+ String.valueOf(
+ Global.Wearable.STATUS_TRAY_CONFIGURATION_DEFAULT),
+ String.valueOf(
+ Global.Wearable.STATUS_TRAY_CONFIGURATION_SYSTEM_HIDDEN)
+ }));
}
}
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
index 558ccaf..509b88b 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
@@ -263,6 +263,5 @@
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);
- VALIDATORS.put(System.HEARING_DEVICE_LOCAL_AMBIENT_VOLUME, ANY_STRING_VALIDATOR);
}
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 603a911..03fea37 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -2434,28 +2434,40 @@
context.checkCallingOrSelfPermission(
Manifest.permission.WRITE_DEVICE_CONFIG)
== PackageManager.PERMISSION_GRANTED;
- boolean isRoot = Binder.getCallingUid() == Process.ROOT_UID;
+ // Only the shell user and tests request the allowlist permission; this is used to force
+ // the WRITE_ALLOWLISTED_DEVICE_CONFIG path to log any flags that need to be allowlisted.
+ boolean isRestrictedShell = android.security.Flags.protectDeviceConfigFlags()
+ && hasAllowlistPermission;
- if (isRoot) {
- return;
- }
-
- if (hasWritePermission) {
+ if (!isRestrictedShell && hasWritePermission) {
assertCallingUserDenyList(flags);
} else if (hasAllowlistPermission) {
for (String flag : flags) {
boolean namespaceAllowed = false;
- for (String allowlistedPrefix : WritableNamespacePrefixes.ALLOWLIST) {
- if (flag.startsWith(allowlistedPrefix)) {
+ if (isRestrictedShell) {
+ int delimiterIndex = flag.indexOf("/");
+ String flagNamespace;
+ if (delimiterIndex != -1) {
+ flagNamespace = flag.substring(0, delimiterIndex);
+ } else {
+ flagNamespace = flag;
+ }
+ if (WritableNamespaces.ALLOWLIST.contains(flagNamespace)) {
namespaceAllowed = true;
- break;
+ }
+ } else {
+ for (String allowlistedPrefix : WritableNamespacePrefixes.ALLOWLIST) {
+ if (flag.startsWith(allowlistedPrefix)) {
+ namespaceAllowed = true;
+ break;
+ }
}
}
if (!namespaceAllowed && !DeviceConfig.getAdbWritableFlags().contains(flag)) {
- throw new SecurityException("Permission denial for flag '"
- + flag
- + "'; allowlist permission granted, but must add flag to the allowlist.");
+ Slog.wtf(LOG_TAG, "Permission denial for flag '" + flag
+ + "'; allowlist permission granted, but must add flag to the "
+ + "allowlist");
}
}
assertCallingUserDenyList(flags);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/WritableNamespaces.java b/packages/SettingsProvider/src/com/android/providers/settings/WritableNamespaces.java
new file mode 100644
index 0000000..d835c5f
--- /dev/null
+++ b/packages/SettingsProvider/src/com/android/providers/settings/WritableNamespaces.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.providers.settings;
+
+import android.util.ArraySet;
+
+import java.util.Arrays;
+import java.util.Set;
+
+/**
+ * Contains the list of namespaces in which any flag can be written by adb without root
+ * permissions.
+ * <p>
+ * A security review is required for any namespace that's added to this list. To add to
+ * the list, create a change and tag the OWNER. In the commit message, include a
+ * description of the flag's functionality, and a justification for why it needs to be
+ * allowlisted.
+ */
+final class WritableNamespaces {
+ public static final Set<String> ALLOWLIST =
+ new ArraySet<String>(Arrays.asList(
+ "exo"
+ ));
+}
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index c2beaa8..a62b7fd 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -564,6 +564,8 @@
Settings.Global.MANAGED_PROVISIONING_DEFER_PROVISIONING_TO_ROLE_HOLDER,
Settings.Global.REVIEW_PERMISSIONS_NOTIFICATION_STATE,
Settings.Global.ENABLE_BACK_ANIMATION, // Temporary for T, dev option only
+ Settings.Global.HEARING_DEVICE_LOCAL_AMBIENT_VOLUME, // cache per hearing device
+ Settings.Global.HEARING_DEVICE_LOCAL_NOTIFICATION, // cache per hearing device
Settings.Global.Wearable.COMBINED_LOCATION_ENABLE,
Settings.Global.Wearable.HAS_PAY_TOKENS,
Settings.Global.Wearable.GMS_CHECKIN_TIMEOUT_MIN,
@@ -633,7 +635,8 @@
Settings.Global.Wearable.WEAR_MEDIA_SESSIONS_PACKAGE,
Settings.Global.Wearable.WEAR_POWER_ANOMALY_SERVICE_ENABLED,
Settings.Global.Wearable.CONNECTIVITY_KEEP_DATA_ON,
- Settings.Global.Wearable.PHONE_SWITCHING_REQUEST_SOURCE);
+ Settings.Global.Wearable.PHONE_SWITCHING_REQUEST_SOURCE,
+ Settings.Global.Wearable.WEAR_SYSTEM_STATUS_TRAY_CONFIGURATION);
private static final Set<String> BACKUP_DENY_LIST_SECURE_SETTINGS =
newHashSet(
@@ -944,8 +947,7 @@
Settings.System.WEAR_ACCESSIBILITY_GESTURE_ENABLED_DURING_OOBE,
Settings.System.WEAR_TTS_PREWARM_ENABLED,
Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ,
- Settings.System.MULTI_AUDIO_FOCUS_ENABLED, // form-factor/OEM specific
- Settings.System.HEARING_DEVICE_LOCAL_AMBIENT_VOLUME // internal cache
+ Settings.System.MULTI_AUDIO_FOCUS_ENABLED // form-factor/OEM specific
);
if (!Flags.backUpSmoothDisplayAndForcePeakRefreshRate()) {
settings.add(Settings.System.MIN_REFRESH_RATE);
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 7b6321d..fef0f8c 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -242,6 +242,8 @@
<uses-permission android:name="android.permission.READ_LOWPAN_CREDENTIAL" />
<uses-permission android:name="android.permission.BLUETOOTH_STACK" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
+ <uses-permission android:name="android.permission.COPY_ACCOUNTS" />
+ <uses-permission android:name="android.permission.REMOVE_ACCOUNTS" />
<uses-permission android:name="android.permission.RETRIEVE_WINDOW_TOKEN" />
<uses-permission android:name="android.permission.FRAME_STATS" />
<uses-permission android:name="android.permission.BIND_APPWIDGET" />
@@ -369,6 +371,9 @@
<!-- Permission needed for CTS test - UnsupportedErrorDialogTests -->
<uses-permission android:name="android.permission.RESET_APP_ERRORS" />
+ <!-- Permission needed tests -->
+ <uses-permission android:name="android.permission.OBSERVE_PICTURE_PROFILES" />
+
<!-- Permission needed for CTS test - CtsSystemUiTestCases:PipNotificationTests -->
<uses-permission android:name="android.permission.GET_INTENT_SENDER_INTENT" />
@@ -743,6 +748,9 @@
<!-- Permission required for ATS test - CarDevicePolicyManagerTest -->
<uses-permission android:name="android.permission.LOCK_DEVICE" />
+ <!-- Permission required for AuthenticationPolicyManagerTest -->
+ <uses-permission android:name="android.permission.MANAGE_SECURE_LOCK_DEVICE" />
+
<!-- Permissions required for CTS test - CtsSafetyCenterTestCases -->
<uses-permission android:name="android.permission.SEND_SAFETY_CENTER_UPDATE" />
<uses-permission android:name="android.permission.READ_SAFETY_CENTER_STATUS" />
@@ -953,6 +961,13 @@
<uses-permission android:name="android.permission.QUERY_ADVANCED_PROTECTION_MODE"
android:featureFlag="android.security.aapm_api"/>
+ <!-- Permission required for CTS test - ForensicManagerTest -->
+ <uses-permission android:name="android.permission.READ_FORENSIC_STATE"
+ android:featureFlag="android.security.afl_api"/>
+ <uses-permission android:name="android.permission.MANAGE_FORENSIC_STATE"
+ android:featureFlag="android.security.afl_api"/>
+
+
<!-- Permission required for CTS test - CtsAppTestCases -->
<uses-permission android:name="android.permission.KILL_UID" />
diff --git a/packages/Shell/OWNERS b/packages/Shell/OWNERS
index 8feefa5..d4b5b862 100644
--- a/packages/Shell/OWNERS
+++ b/packages/Shell/OWNERS
@@ -12,3 +12,4 @@
cbrubaker@google.com
omakoto@google.com
michaelwr@google.com
+ronish@google.com
diff --git a/packages/Shell/aconfig/Android.bp b/packages/Shell/aconfig/Android.bp
new file mode 100644
index 0000000..1d797b2
--- /dev/null
+++ b/packages/Shell/aconfig/Android.bp
@@ -0,0 +1,13 @@
+aconfig_declarations {
+ name: "wear_aconfig_declarations",
+ package: "com.android.shell.flags",
+ container: "system",
+ srcs: [
+ "wear.aconfig",
+ ],
+}
+
+java_aconfig_library {
+ name: "wear_aconfig_declarations_flags_java_lib",
+ aconfig_declarations: "wear_aconfig_declarations",
+}
diff --git a/packages/Shell/aconfig/wear.aconfig b/packages/Shell/aconfig/wear.aconfig
new file mode 100644
index 0000000..e07bd96
--- /dev/null
+++ b/packages/Shell/aconfig/wear.aconfig
@@ -0,0 +1,9 @@
+package: "com.android.shell.flags"
+container: "system"
+
+flag {
+ name: "handle_bugreports_for_wear"
+ namespace: "wear_services"
+ description: "This flag enables Shell to propagate bugreport results to WearServices."
+ bug: "378060870"
+}
\ No newline at end of file
diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java
index 7c478ac..7f25b51 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -153,6 +153,10 @@
static final String INTENT_BUGREPORT_FINISHED =
"com.android.internal.intent.action.BUGREPORT_FINISHED";
+ // Intent sent to notify external apps that bugreport aborted due to error.
+ static final String INTENT_BUGREPORT_ABORTED =
+ "com.android.internal.intent.action.BUGREPORT_ABORTED";
+
// Internal intents used on notification actions.
static final String INTENT_BUGREPORT_CANCEL = "android.intent.action.BUGREPORT_CANCEL";
static final String INTENT_BUGREPORT_SHARE = "android.intent.action.BUGREPORT_SHARE";
@@ -174,6 +178,8 @@
static final String EXTRA_INFO = "android.intent.extra.INFO";
static final String EXTRA_EXTRA_ATTACHMENT_URIS =
"android.intent.extra.EXTRA_ATTACHMENT_URIS";
+ static final String EXTRA_ABORTED_ERROR_CODE =
+ "android.intent.extra.EXTRA_ABORTED_ERROR_CODE";
private static final ThreadFactory sBugreportManagerCallbackThreadFactory =
new ThreadFactory() {
@@ -404,6 +410,7 @@
@Override
public void onError(@BugreportErrorCode int errorCode) {
synchronized (mLock) {
+ sendBugreportAbortedBroadcastLocked(errorCode);
stopProgressLocked(mInfo.id);
mInfo.deleteEmptyFiles();
}
@@ -460,6 +467,13 @@
onBugreportFinished(mInfo);
}
}
+
+ @GuardedBy("mLock")
+ private void sendBugreportAbortedBroadcastLocked(@BugreportErrorCode int errorCode) {
+ final Intent intent = new Intent(INTENT_BUGREPORT_ABORTED);
+ intent.putExtra(EXTRA_ABORTED_ERROR_CODE, errorCode);
+ mContext.sendBroadcast(intent, android.Manifest.permission.DUMP);
+ }
}
private void sendRemoteBugreportFinishedBroadcast(Context context,
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index 0948931..d1a22e8 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -90,8 +90,6 @@
"tests/src/**/systemui/globalactions/GlobalActionsDialogLiteTest.java",
"tests/src/**/systemui/globalactions/GlobalActionsImeTest.java",
"tests/src/**/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt",
- "tests/src/**/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImplTest.kt",
- "tests/src/**/systemui/media/controls/domain/pipeline/MediaDataProcessorTest.kt",
"tests/src/**/systemui/media/dialog/MediaOutputAdapterTest.java",
"tests/src/**/systemui/media/dialog/MediaOutputBaseDialogTest.java",
"tests/src/**/systemui/media/dialog/MediaOutputBroadcastDialogTest.java",
@@ -134,7 +132,6 @@
"tests/src/**/systemui/accessibility/floatingmenu/MenuViewLayerTest.java",
"tests/src/**/systemui/accessibility/floatingmenu/MenuViewTest.java",
"tests/src/**/systemui/classifier/PointerCountClassifierTest.java",
- "tests/src/**/systemui/qs/tileimpl/QSIconViewImplTest_311121830.kt",
"tests/src/**/systemui/accessibility/floatingmenu/RadiiAnimatorTest.java",
"tests/src/**/systemui/screenrecord/RecordingControllerTest.java",
"tests/src/**/systemui/screenshot/RequestProcessorTest.kt",
@@ -211,13 +208,13 @@
"tests/src/**/systemui/qs/tiles/DreamTileTest.java",
"tests/src/**/systemui/qs/FgsManagerControllerTest.java",
"tests/src/**/systemui/qs/QSPanelTest.kt",
+ "tests/src/**/systemui/reardisplay/RearDisplayCoreStartableTest.kt",
"tests/src/**/systemui/reardisplay/RearDisplayDialogControllerTest.java",
+ "tests/src/**/systemui/reardisplay/RearDisplayInnerDialogDelegateTest.kt",
"tests/src/**/systemui/statusbar/KeyboardShortcutListSearchTest.java",
"tests/src/**/systemui/statusbar/KeyboardShortcutsTest.java",
"tests/src/**/systemui/statusbar/KeyguardIndicationControllerWithCoroutinesTest.kt",
"tests/src/**/systemui/statusbar/notification/AssistantFeedbackControllerTest.java",
- "tests/src/**/systemui/statusbar/notification/PropertyAnimatorTest.java",
- "tests/src/**/systemui/statusbar/notification/collection/NotifCollectionTest.java",
"tests/src/**/systemui/statusbar/notification/collection/NotificationEntryTest.java",
"tests/src/**/systemui/statusbar/notification/collection/render/GroupExpansionManagerTest.kt",
"tests/src/**/systemui/statusbar/notification/collection/ShadeListBuilderTest.java",
@@ -233,9 +230,7 @@
"tests/src/**/systemui/statusbar/notification/row/NotificationConversationInfoTest.java",
"tests/src/**/systemui/statusbar/notification/row/NotificationGutsManagerTest.java",
"tests/src/**/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt",
- "tests/src/**/systemui/statusbar/notification/row/NotifLayoutInflaterFactoryTest.kt",
"tests/src/**/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapperTest.kt",
- "tests/src/**/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java",
"tests/src/**/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java",
"tests/src/**/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt",
"tests/src/**/systemui/statusbar/phone/AutoTileManagerTest.java",
@@ -250,7 +245,6 @@
"tests/src/**/systemui/statusbar/pipeline/airplane/ui/viewmodel/AirplaneModeViewModelImplTest.kt",
"tests/src/**/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt",
"tests/src/**/systemui/statusbar/pipeline/mobile/ui/view/ModernStatusBarMobileViewTest.kt",
- "tests/src/**/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt",
"tests/src/**/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt",
"tests/src/**/systemui/statusbar/policy/CallbackControllerTest.java",
"tests/src/**/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java",
@@ -263,11 +257,9 @@
"tests/src/**/systemui/theme/ThemeOverlayApplierTest.java",
"tests/src/**/systemui/touch/TouchInsetManagerTest.java",
"tests/src/**/systemui/util/LifecycleFragmentTest.java",
- "tests/src/**/systemui/util/TestableAlertDialogTest.kt",
"tests/src/**/systemui/util/kotlin/PairwiseFlowTest",
"tests/src/**/systemui/util/sensors/AsyncManagerTest.java",
"tests/src/**/systemui/util/sensors/ThresholdSensorImplTest.java",
- "tests/src/**/systemui/util/wakelock/KeepAwakeAnimationListenerTest.java",
"tests/src/**/systemui/volume/VolumeDialogImplTest.java",
"tests/src/**/systemui/wallet/controller/QuickAccessWalletControllerTest.java",
"tests/src/**/systemui/wallet/ui/WalletScreenControllerTest.java",
@@ -288,9 +280,6 @@
"tests/src/**/systemui/qs/QSImplTest.java",
"tests/src/**/systemui/qs/panels/ui/compose/DragAndDropTest.kt",
"tests/src/**/systemui/qs/panels/ui/compose/ResizingTest.kt",
- "tests/src/**/systemui/screenshot/ActionExecutorTest.kt",
- "tests/src/**/systemui/screenshot/ScreenshotDetectionControllerTest.kt",
- "tests/src/**/systemui/screenshot/TakeScreenshotServiceTest.kt",
"tests/src/**/systemui/accessibility/floatingmenu/MenuAnimationControllerTest.java",
"tests/src/**/systemui/accessibility/floatingmenu/PositionTest.java",
"tests/src/**/systemui/animation/TransitionAnimatorTest.kt",
@@ -303,10 +292,45 @@
"tests/src/**/systemui/statusbar/notification/row/BigPictureIconManagerTest.kt",
"tests/src/**/systemui/statusbar/policy/RotationLockControllerImplTest.java",
"tests/src/**/systemui/statusbar/phone/ScrimControllerTest.java",
- "tests/src/**/systemui/stylus/StylusUsiPowerStartableTest.kt",
"tests/src/**/systemui/toast/ToastUITest.java",
"tests/src/**/systemui/statusbar/policy/FlashlightControllerImplTest.kt",
"tests/src/**/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImplTest.kt",
+ "tests/src/**/systemui/stylus/StylusUsiPowerUiTest.kt",
+ ],
+}
+
+// Files which use ExtendedMockito on the device.
+filegroup {
+ name: "SystemUI-tests-broken-robofiles-mockito-extended",
+ srcs: [
+ "tests/src/**/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfigTest.kt",
+ "tests/src/**/systemui/notetask/shortcut/LaunchNoteTaskActivityTest.kt",
+ "tests/src/**/systemui/notetask/LaunchNotesRoleSettingsTrampolineActivityTest.kt",
+ "tests/src/**/systemui/bluetooth/qsdialog/AudioSharingDeviceItemActionInteractorTest.kt",
+ "tests/src/**/systemui/bluetooth/qsdialog/AudioSharingButtonViewModelTest.kt",
+ "tests/src/**/systemui/bluetooth/qsdialog/DeviceItemFactoryTest.kt",
+ "tests/src/**/systemui/stylus/StylusManagerTest.kt",
+ "tests/src/**/systemui/recents/OverviewProxyServiceTest.kt",
+ "tests/src/**/systemui/DisplayCutoutBaseViewTest.kt",
+ "tests/src/**/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigRepositoryTest.kt",
+ "tests/src/**/systemui/statusbar/policy/BatteryControllerTest.java",
+ "tests/src/**/systemui/statusbar/policy/SensitiveNotificationProtectionControllerTest.kt",
+ "tests/src/**/systemui/statusbar/KeyboardShortcutsReceiverTest.java",
+ "tests/src/**/systemui/media/controls/domain/pipeline/MediaDataProcessorTest.kt",
+ "tests/src/**/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImplTest.kt",
+ "tests/src/**/systemui/temporarydisplay/chipbar/SwipeChipbarAwayGestureHandlerTest.kt",
+ "tests/src/**/systemui/qs/tiles/HotspotTileTest.java",
+ "tests/src/**/systemui/qs/tiles/dialog/InternetDialogDelegateTest.java",
+ "tests/src/**/systemui/navigationbar/NavigationBarControllerImplTest.java",
+ "tests/src/**/systemui/wmshell/BubblesTest.java",
+ "tests/src/**/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java",
+ "tests/src/**/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java",
+ "tests/src/**/systemui/shared/system/RemoteTransitionTest.java",
+ "tests/src/**/systemui/qs/tiles/dialog/InternetDialogDelegateControllerTest.java",
+ "tests/src/**/systemui/qs/external/TileLifecycleManagerTest.java",
+ "tests/src/**/systemui/ScreenDecorationsTest.java",
+ "tests/src/**/keyguard/CarrierTextManagerTest.java",
+ "tests/src/**/keyguard/KeyguardUpdateMonitorTest.java",
],
}
@@ -314,29 +338,23 @@
filegroup {
name: "SystemUI-tests-broken-robofiles-compile",
srcs: [
- "tests/src/**/keyguard/KeyguardUpdateMonitorTest.java",
"tests/src/**/systemui/statusbar/notification/icon/IconManagerTest.kt",
"tests/src/**/systemui/statusbar/KeyguardIndicationControllerTest.java",
"tests/src/**/systemui/doze/DozeScreenStateTest.java",
- "tests/src/**/keyguard/CarrierTextManagerTest.java",
"tests/src/**/systemui/notetask/NoteTaskInitializerTest.kt",
"tests/src/**/systemui/media/controls/ui/controller/MediaHierarchyManagerTest.kt",
"tests/src/**/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt",
"tests/src/**/systemui/controls/management/ControlsFavoritingActivityTest.kt",
"tests/src/**/systemui/controls/management/ControlsProviderSelectorActivityTest.kt",
"tests/src/**/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt",
- "tests/src/**/systemui/media/controls/domain/pipeline/MediaTimeoutListenerTest.kt",
"tests/src/**/systemui/media/taptotransfer/receiver/FakeMediaTttChipControllerReceiver.kt",
"tests/src/**/systemui/qs/tileimpl/QSTileViewImplTest.kt",
- "tests/src/**/systemui/statusbar/policy/BatteryStateNotifierTest.kt",
"tests/src/**/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt",
"tests/src/**/keyguard/ClockEventControllerTest.kt",
"tests/src/**/systemui/bluetooth/qsdialog/BluetoothAutoOnRepositoryTest.kt",
- "tests/src/**/systemui/bluetooth/qsdialog/BluetoothStateInteractorTest.kt",
"tests/src/**/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegateTest.kt",
"tests/src/**/systemui/bluetooth/qsdialog/BluetoothTileDialogRepositoryTest.kt",
"tests/src/**/systemui/bluetooth/qsdialog/BluetoothTileDialogViewModelTest.kt",
- "tests/src/**/systemui/bluetooth/qsdialog/DeviceItemFactoryTest.kt",
"tests/src/**/systemui/bluetooth/qsdialog/DeviceItemInteractorTest.kt",
"tests/src/**/systemui/broadcast/UserBroadcastDispatcherTest.kt",
"tests/src/**/systemui/charging/WiredChargingRippleControllerTest.kt",
@@ -351,7 +369,6 @@
"tests/src/**/systemui/controls/ui/SelectionItemTest.kt",
"tests/src/**/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySectionTest.kt",
"tests/src/**/systemui/media/controls/domain/pipeline/LegacyMediaDataFilterImplTest.kt",
- "tests/src/**/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImplTest.kt",
"tests/src/**/systemui/media/controls/domain/pipeline/MediaDataFilterImplTest.kt",
"tests/src/**/systemui/media/controls/ui/animation/AnimationBindHandlerTest.kt",
"tests/src/**/systemui/media/controls/ui/animation/ColorSchemeTransitionTest.kt",
@@ -417,40 +434,9 @@
"tests/src/**/systemui/statusbar/policy/VariableDateViewControllerTest.kt",
"tests/src/**/systemui/statusbar/policy/WalletControllerImplTest.kt",
"tests/src/**/systemui/statusbar/SplitShadeLockScreenOverScrollerTest.kt",
- "tests/src/**/systemui/stylus/StylusUsiPowerUiTest.kt",
"tests/src/**/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt",
- "tests/src/**/systemui/ScreenDecorationsTest.java",
- "tests/src/**/systemui/temporarydisplay/chipbar/SwipeChipbarAwayGestureHandlerTest.kt",
- "tests/src/**/systemui/media/controls/domain/pipeline/MediaDataProcessorTest.kt",
- "tests/src/**/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImplTest.kt",
- "tests/src/**/systemui/shared/system/RemoteTransitionTest.java",
- "tests/src/**/systemui/navigationbar/NavigationBarControllerImplTest.java",
- "tests/src/**/systemui/bluetooth/qsdialog/AudioSharingButtonViewModelTest.kt",
- "tests/src/**/systemui/bluetooth/qsdialog/AudioSharingDeviceItemActionInteractorTest.kt",
- "tests/src/**/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfigTest.kt",
- "tests/src/**/systemui/notetask/LaunchNotesRoleSettingsTrampolineActivityTest.kt",
- "tests/src/**/systemui/notetask/shortcut/LaunchNoteTaskActivityTest.kt",
- "tests/src/**/systemui/DisplayCutoutBaseViewTest.kt",
- "tests/src/**/systemui/qs/tiles/dialog/InternetDialogDelegateTest.java",
- "tests/src/**/systemui/qs/tiles/dialog/InternetDialogDelegateControllerTest.java",
- "tests/src/**/systemui/qs/tiles/HotspotTileTest.java",
- "tests/src/**/systemui/qs/external/TileLifecycleManagerTest.java",
- "tests/src/**/systemui/recents/OverviewProxyServiceTest.kt",
- "tests/src/**/systemui/stylus/StylusManagerTest.kt",
- "tests/src/**/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java",
- "tests/src/**/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java",
"tests/src/**/systemui/statusbar/policy/BatteryControllerStartableTest.java",
- "tests/src/**/systemui/statusbar/policy/BatteryControllerTest.java",
- "tests/src/**/systemui/statusbar/policy/SensitiveNotificationProtectionControllerTest.kt",
- "tests/src/**/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigRepositoryTest.kt",
- "tests/src/**/systemui/statusbar/KeyboardShortcutsReceiverTest.java",
- "tests/src/**/systemui/wmshell/BubblesTest.java",
- "tests/src/**/systemui/power/PowerUITest.java",
- "tests/src/**/systemui/qs/QSSecurityFooterTest.java",
- "tests/src/**/systemui/qs/tileimpl/QSTileImplTest.java",
"tests/src/**/systemui/shared/plugins/PluginActionManagerTest.java",
- "tests/src/**/systemui/statusbar/CommandQueueTest.java",
- "tests/src/**/systemui/statusbar/connectivity/CallbackHandlerTest.java",
"tests/src/**/systemui/statusbar/policy/SecurityControllerTest.java",
"tests/src/**/systemui/shared/clocks/view/SimpleDigitalClockTextViewTest.kt",
],
@@ -916,6 +902,7 @@
":SystemUI-tests-robofiles",
],
exclude_srcs: [
+ ":SystemUI-tests-broken-robofiles-mockito-extended",
":SystemUI-tests-broken-robofiles-compile",
":SystemUI-tests-broken-robofiles-run",
":SystemUI-tests-broken-robofiles-sysui-run",
diff --git a/packages/SystemUI/TEST_MAPPING b/packages/SystemUI/TEST_MAPPING
index 380344a..e47704eb 100644
--- a/packages/SystemUI/TEST_MAPPING
+++ b/packages/SystemUI/TEST_MAPPING
@@ -153,5 +153,11 @@
{
"path": "cts/tests/tests/multiuser"
}
+ ],
+
+ "sysui-e2e-presubmit": [
+ {
+ "name": "PlatformScenarioTests_SysUI_Presubmit"
+ }
]
}
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index a4b8821..c82c63c7 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -632,9 +632,9 @@
flag {
name: "status_bar_connected_displays"
- namespace: "systemui"
+ namespace: "lse_desktop_experience"
description: "Shows the status bar on connected displays"
- bug: "362720336"
+ bug: "379264862"
}
flag {
@@ -1748,6 +1748,13 @@
}
flag {
+ name: "notification_shade_blur"
+ namespace: "systemui"
+ description: "Enables the new blur effect on the Notification Shade."
+ bug: "370555223"
+}
+
+flag {
name: "ensure_enr_views_visibility"
namespace: "systemui"
description: "Ensures public and private visibilities"
@@ -1790,4 +1797,18 @@
metadata {
purpose: PURPOSE_BUGFIX
}
-}
\ No newline at end of file
+}
+
+flag {
+ name: "gsf_bouncer"
+ namespace: "systemui"
+ description: "Applies GSF font styles to Bouncer surfaces."
+ bug: "379364381"
+}
+
+flag {
+ name: "gsf_quick_settings"
+ namespace: "systemui"
+ description: "Applies GSF font styles to Quick Settings surfaces."
+ bug: "379364381"
+}
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt
index 18f40c9..eee0caf 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt
@@ -24,18 +24,22 @@
import android.content.ComponentName
import android.graphics.Color
import android.graphics.Matrix
+import android.graphics.PointF
import android.graphics.Rect
import android.graphics.RectF
import android.os.Binder
import android.os.Build
import android.os.Handler
+import android.os.IBinder
import android.os.Looper
import android.os.RemoteException
+import android.util.ArrayMap
import android.util.Log
import android.view.IRemoteAnimationFinishedCallback
import android.view.IRemoteAnimationRunner
import android.view.RemoteAnimationAdapter
import android.view.RemoteAnimationTarget
+import android.view.SurfaceControl
import android.view.SyncRtSurfaceTransactionApplier
import android.view.View
import android.view.ViewGroup
@@ -45,8 +49,12 @@
import android.view.WindowManager.TRANSIT_TO_BACK
import android.view.WindowManager.TRANSIT_TO_FRONT
import android.view.animation.PathInterpolator
+import android.window.IRemoteTransition
+import android.window.IRemoteTransitionFinishedCallback
import android.window.RemoteTransition
import android.window.TransitionFilter
+import android.window.TransitionInfo
+import android.window.WindowAnimationState
import androidx.annotation.AnyThread
import androidx.annotation.BinderThread
import androidx.annotation.UiThread
@@ -55,11 +63,14 @@
import com.android.internal.policy.ScreenDecorationsUtils
import com.android.systemui.Flags.activityTransitionUseLargestWindow
import com.android.systemui.Flags.translucentOccludingActivityFix
+import com.android.systemui.animation.TransitionAnimator.Companion.assertLongLivedReturnAnimations
+import com.android.systemui.animation.TransitionAnimator.Companion.assertReturnAnimations
+import com.android.systemui.animation.TransitionAnimator.Companion.longLivedReturnAnimationsEnabled
+import com.android.systemui.animation.TransitionAnimator.Companion.returnAnimationsEnabled
import com.android.systemui.animation.TransitionAnimator.Companion.toTransitionState
-import com.android.systemui.shared.Flags.returnAnimationFrameworkLibrary
-import com.android.systemui.shared.Flags.returnAnimationFrameworkLongLived
import com.android.wm.shell.shared.IShellTransitions
import com.android.wm.shell.shared.ShellTransitions
+import com.android.wm.shell.shared.TransitionUtil
import java.util.concurrent.Executor
import kotlin.math.roundToInt
@@ -194,7 +205,13 @@
private const val LONG_TRANSITION_TIMEOUT = 5_000L
private fun defaultTransitionAnimator(mainExecutor: Executor): TransitionAnimator {
- return TransitionAnimator(mainExecutor, TIMINGS, INTERPOLATORS)
+ return TransitionAnimator(
+ mainExecutor,
+ TIMINGS,
+ INTERPOLATORS,
+ SPRING_TIMINGS,
+ SPRING_INTERPOLATORS,
+ )
}
private fun defaultDialogToAppAnimator(mainExecutor: Executor): TransitionAnimator {
@@ -275,7 +292,7 @@
"ActivityTransitionAnimator.callback must be set before using this animator"
)
val runner = createRunner(controller)
- val runnerDelegate = runner.delegate!!
+ val runnerDelegate = runner.delegate
val hideKeyguardWithAnimation = callback.isOnKeyguard() && !showOverLockscreen
// Pass the RemoteAnimationAdapter to the intent starter only if we are not hiding the
@@ -330,7 +347,11 @@
// If we expect an animation, post a timeout to cancel it in case the remote animation is
// never started.
if (willAnimate) {
- runnerDelegate.postTimeouts()
+ if (longLivedReturnAnimationsEnabled()) {
+ runner.postTimeouts()
+ } else {
+ runnerDelegate!!.postTimeouts()
+ }
// Hide the keyguard using the launch animation instead of the default unlock animation.
if (hideKeyguardWithAnimation) {
@@ -390,7 +411,7 @@
launchController: Controller,
transitionRegister: TransitionRegister?,
) {
- if (!returnAnimationFrameworkLibrary()) return
+ if (!returnAnimationsEnabled()) return
var cleanUpRunnable: Runnable? = null
val returnRunner =
@@ -413,7 +434,8 @@
private fun cleanUp() {
cleanUpRunnable?.run()
}
- }
+ },
+ initializeLazily = longLivedReturnAnimationsEnabled(),
)
// mTypeSet and mModes match back signals only, and not home. This is on purpose, because
@@ -435,7 +457,11 @@
"${launchController.transitionCookie}_returnTransition",
)
- transitionRegister?.register(filter, transition)
+ transitionRegister?.register(
+ filter,
+ transition,
+ includeTakeover = longLivedReturnAnimationsEnabled(),
+ )
cleanUpRunnable = Runnable { transitionRegister?.unregister(transition) }
}
@@ -451,7 +477,10 @@
/** Create a new animation [Runner] controlled by [controller]. */
@VisibleForTesting
- fun createRunner(controller: Controller): Runner {
+ @JvmOverloads
+ fun createRunner(controller: Controller, initializeLazily: Boolean = false): Runner {
+ if (initializeLazily) assertLongLivedReturnAnimations()
+
// Make sure we use the modified timings when animating a dialog into an app.
val transitionAnimator =
if (controller.isDialogLaunch) {
@@ -460,7 +489,13 @@
transitionAnimator
}
- return Runner(controller, callback!!, transitionAnimator, lifecycleListener)
+ return Runner(
+ controller,
+ callback!!,
+ transitionAnimator,
+ lifecycleListener,
+ initializeLazily,
+ )
}
interface PendingIntentStarter {
@@ -628,10 +663,7 @@
* this registration.
*/
fun register(controller: Controller) {
- check(returnAnimationFrameworkLongLived()) {
- "Long-lived registrations cannot be used when the returnAnimationFrameworkLongLived " +
- "flag is disabled"
- }
+ assertLongLivedReturnAnimations()
if (transitionRegister == null) {
throw IllegalStateException(
@@ -667,10 +699,10 @@
}
val launchRemoteTransition =
RemoteTransition(
- RemoteAnimationRunnerCompat.wrap(createRunner(controller)),
+ OriginTransition(createRunner(controller, initializeLazily = true)),
"${cookie}_launchTransition",
)
- transitionRegister.register(launchFilter, launchRemoteTransition)
+ transitionRegister.register(launchFilter, launchRemoteTransition, includeTakeover = true)
val returnController =
object : Controller by controller {
@@ -689,10 +721,10 @@
}
val returnRemoteTransition =
RemoteTransition(
- RemoteAnimationRunnerCompat.wrap(createRunner(returnController)),
+ OriginTransition(createRunner(returnController, initializeLazily = true)),
"${cookie}_returnTransition",
)
- transitionRegister.register(returnFilter, returnRemoteTransition)
+ transitionRegister.register(returnFilter, returnRemoteTransition, includeTakeover = true)
longLivedTransitions[cookie] = Pair(launchRemoteTransition, returnRemoteTransition)
}
@@ -738,14 +770,154 @@
}
}
+ /** [Runner] wrapper that supports animation takeovers. */
+ private inner class OriginTransition(private val runner: Runner) : IRemoteTransition {
+ private val delegate = RemoteAnimationRunnerCompat.wrap(runner)
+
+ init {
+ assertLongLivedReturnAnimations()
+ }
+
+ override fun startAnimation(
+ token: IBinder?,
+ info: TransitionInfo?,
+ t: SurfaceControl.Transaction?,
+ finishCallback: IRemoteTransitionFinishedCallback?,
+ ) {
+ delegate.startAnimation(token, info, t, finishCallback)
+ }
+
+ override fun mergeAnimation(
+ transition: IBinder?,
+ info: TransitionInfo?,
+ t: SurfaceControl.Transaction?,
+ mergeTarget: IBinder?,
+ finishCallback: IRemoteTransitionFinishedCallback?,
+ ) {
+ delegate.mergeAnimation(transition, info, t, mergeTarget, finishCallback)
+ }
+
+ override fun onTransitionConsumed(transition: IBinder?, aborted: Boolean) {
+ delegate.onTransitionConsumed(transition, aborted)
+ }
+
+ override fun takeOverAnimation(
+ token: IBinder?,
+ info: TransitionInfo?,
+ t: SurfaceControl.Transaction?,
+ finishCallback: IRemoteTransitionFinishedCallback?,
+ states: Array<WindowAnimationState>,
+ ) {
+ if (info == null || t == null) {
+ Log.e(
+ TAG,
+ "Skipping the animation takeover because the required data is missing: " +
+ "info=$info, transaction=$t",
+ )
+ return
+ }
+
+ // The following code converts the contents of the given TransitionInfo into
+ // RemoteAnimationTargets. This is necessary because we must currently support both the
+ // new (Shell, remote transitions) and old (remote animations) framework to maintain
+ // functionality for all users of the library.
+ val apps = ArrayList<RemoteAnimationTarget>()
+ val filteredStates = ArrayList<WindowAnimationState>()
+ val leashMap = ArrayMap<SurfaceControl, SurfaceControl>()
+ val leafTaskFilter = TransitionUtil.LeafTaskFilter()
+
+ // About layering: we divide up the "layer space" into 2 regions (each the size of the
+ // change count). This lets us categorize things into above and below while
+ // maintaining their relative ordering.
+ val belowLayers = info.changes.size
+ val aboveLayers = info.changes.size * 2
+ for (i in info.changes.indices) {
+ val change = info.changes[i]
+ if (change == null || change.taskInfo == null) {
+ continue
+ }
+
+ val taskInfo = change.taskInfo
+
+ if (TransitionUtil.isWallpaper(change)) {
+ val target =
+ TransitionUtil.newTarget(
+ change,
+ belowLayers - i, // wallpapers go into the "below" layer space
+ info,
+ t,
+ leashMap,
+ )
+
+ // Make all the wallpapers opaque.
+ t.setAlpha(target.leash, 1f)
+ } else if (leafTaskFilter.test(change)) {
+ // Start by putting everything into the "below" layer space.
+ val target =
+ TransitionUtil.newTarget(change, belowLayers - i, info, t, leashMap)
+ apps.add(target)
+ filteredStates.add(states[i])
+
+ // Make all the apps opaque.
+ t.setAlpha(target.leash, 1f)
+
+ if (
+ TransitionUtil.isClosingType(change.mode) &&
+ taskInfo?.topActivityType != WindowConfiguration.ACTIVITY_TYPE_HOME
+ ) {
+ // Raise closing task to "above" layer so it isn't covered.
+ t.setLayer(target.leash, aboveLayers - i)
+ }
+ } else if (TransitionInfo.isIndependent(change, info)) {
+ // Root tasks
+ if (TransitionUtil.isClosingType(change.mode)) {
+ // Raise closing task to "above" layer so it isn't covered.
+ t.setLayer(change.leash, aboveLayers - i)
+ } else if (TransitionUtil.isOpeningType(change.mode)) {
+ // Put into the "below" layer space.
+ t.setLayer(change.leash, belowLayers - i)
+ }
+ } else if (TransitionUtil.isDividerBar(change)) {
+ val target =
+ TransitionUtil.newTarget(change, belowLayers - i, info, t, leashMap)
+ apps.add(target)
+ filteredStates.add(states[i])
+ }
+ }
+
+ val wrappedCallback: IRemoteAnimationFinishedCallback =
+ object : IRemoteAnimationFinishedCallback.Stub() {
+ override fun onAnimationFinished() {
+ leashMap.clear()
+ val finishTransaction = SurfaceControl.Transaction()
+ finishCallback?.onTransitionFinished(null, finishTransaction)
+ finishTransaction.close()
+ }
+ }
+
+ runner.takeOverAnimation(
+ apps.toTypedArray(),
+ filteredStates.toTypedArray(),
+ t,
+ wrappedCallback,
+ )
+ }
+
+ override fun asBinder(): IBinder {
+ return delegate.asBinder()
+ }
+ }
+
@VisibleForTesting
inner class Runner(
- controller: Controller,
- callback: Callback,
+ private val controller: Controller,
+ private val callback: Callback,
/** The animator to use to animate the window transition. */
- transitionAnimator: TransitionAnimator,
+ private val transitionAnimator: TransitionAnimator,
/** Listener for animation lifecycle events. */
- listener: Listener? = null,
+ private val listener: Listener? = null,
+ /** Whether the internal [delegate] should be initialized lazily. */
+ private val initializeLazily: Boolean = false,
) : IRemoteAnimationRunner.Stub() {
// This is being passed across IPC boundaries and cycles (through PendingIntentRecords,
// etc.) are possible. So we need to make sure we drop any references that might
@@ -753,15 +925,14 @@
@VisibleForTesting var delegate: AnimationDelegate?
init {
- delegate =
- AnimationDelegate(
- mainExecutor,
- controller,
- callback,
- DelegatingAnimationCompletionListener(listener, this::dispose),
- transitionAnimator,
- disableWmTimeout,
- )
+ delegate = null
+ if (!initializeLazily) {
+ // Ephemeral launches bundle the runner with the launch request (instead of being
+ // registered ahead of time for later use). This means that there could be a timeout
+ // between creation and invocation, so the delegate needs to exist from the
+ // beginning in order to handle such timeout.
+ createDelegate()
+ }
}
@BinderThread
@@ -772,6 +943,36 @@
nonApps: Array<out RemoteAnimationTarget>?,
finishedCallback: IRemoteAnimationFinishedCallback?,
) {
+ initAndRun(finishedCallback) { delegate ->
+ delegate.onAnimationStart(transit, apps, wallpapers, nonApps, finishedCallback)
+ }
+ }
+
+ @VisibleForTesting
+ @BinderThread
+ fun takeOverAnimation(
+ apps: Array<RemoteAnimationTarget>?,
+ windowAnimationStates: Array<WindowAnimationState>,
+ startTransaction: SurfaceControl.Transaction,
+ finishedCallback: IRemoteAnimationFinishedCallback?,
+ ) {
+ assertLongLivedReturnAnimations()
+ initAndRun(finishedCallback) { delegate ->
+ delegate.takeOverAnimation(
+ apps,
+ windowAnimationStates,
+ startTransaction,
+ finishedCallback,
+ )
+ }
+ }
+
+ @BinderThread
+ private fun initAndRun(
+ finishedCallback: IRemoteAnimationFinishedCallback?,
+ performAnimation: (AnimationDelegate) -> Unit,
+ ) {
+ maybeSetUp()
val delegate = delegate
mainExecutor.execute {
if (delegate == null) {
@@ -780,7 +981,7 @@
// signal back that we're done with it.
finishedCallback?.onAnimationFinished()
} else {
- delegate.onAnimationStart(transit, apps, wallpapers, nonApps, finishedCallback)
+ performAnimation(delegate)
}
}
}
@@ -794,6 +995,32 @@
}
}
+ @VisibleForTesting
+ @UiThread
+ fun postTimeouts() {
+ maybeSetUp()
+ delegate?.postTimeouts()
+ }
+
+ @AnyThread
+ private fun maybeSetUp() {
+ if (!initializeLazily || delegate != null) return
+ createDelegate()
+ }
+
+ @AnyThread
+ private fun createDelegate() {
+ delegate =
+ AnimationDelegate(
+ mainExecutor,
+ controller,
+ callback,
+ DelegatingAnimationCompletionListener(listener, this::dispose),
+ transitionAnimator,
+ disableWmTimeout,
+ )
+ }
+
@AnyThread
fun dispose() {
// Drop references to animation controller once we're done with the animation
@@ -867,7 +1094,7 @@
init {
// We do this check here to cover all entry points, including Launcher which doesn't
// call startIntentWithAnimation()
- if (!controller.isLaunching) TransitionAnimator.checkReturnAnimationFrameworkFlag()
+ if (!controller.isLaunching) assertReturnAnimations()
}
@UiThread
@@ -893,19 +1120,73 @@
nonApps: Array<out RemoteAnimationTarget>?,
callback: IRemoteAnimationFinishedCallback?,
) {
+ val window = setUpAnimation(apps, callback) ?: return
+
+ if (controller.windowAnimatorState == null || !longLivedReturnAnimationsEnabled()) {
+ val navigationBar =
+ nonApps?.firstOrNull {
+ it.windowType == WindowManager.LayoutParams.TYPE_NAVIGATION_BAR
+ }
+
+ startAnimation(window, navigationBar, iCallback = callback)
+ } else {
+ // If a [controller.windowAnimatorState] exists, treat this like a takeover.
+ takeOverAnimationInternal(
+ window,
+ startWindowStates = null,
+ startTransaction = null,
+ callback,
+ )
+ }
+ }
+
+ @UiThread
+ internal fun takeOverAnimation(
+ apps: Array<out RemoteAnimationTarget>?,
+ startWindowStates: Array<WindowAnimationState>,
+ startTransaction: SurfaceControl.Transaction,
+ callback: IRemoteAnimationFinishedCallback?,
+ ) {
+ val window = setUpAnimation(apps, callback) ?: return
+ takeOverAnimationInternal(window, startWindowStates, startTransaction, callback)
+ }
+
+ private fun takeOverAnimationInternal(
+ window: RemoteAnimationTarget,
+ startWindowStates: Array<WindowAnimationState>?,
+ startTransaction: SurfaceControl.Transaction?,
+ callback: IRemoteAnimationFinishedCallback?,
+ ) {
+ val useSpring =
+ !controller.isLaunching && startWindowStates != null && startTransaction != null
+ startAnimation(
+ window,
+ navigationBar = null,
+ useSpring,
+ startWindowStates,
+ startTransaction,
+ callback,
+ )
+ }
+
+ @UiThread
+ private fun setUpAnimation(
+ apps: Array<out RemoteAnimationTarget>?,
+ callback: IRemoteAnimationFinishedCallback?,
+ ): RemoteAnimationTarget? {
removeTimeouts()
// The animation was started too late and we already notified the controller that it
// timed out.
if (timedOut) {
callback?.invoke()
- return
+ return null
}
// This should not happen, but let's make sure we don't start the animation if it was
// cancelled before and we already notified the controller.
if (cancelled) {
- return
+ return null
}
val window = findTargetWindowIfPossible(apps)
@@ -921,15 +1202,10 @@
}
controller.onTransitionAnimationCancelled()
listener?.onTransitionAnimationCancelled()
- return
+ return null
}
- val navigationBar =
- nonApps?.firstOrNull {
- it.windowType == WindowManager.LayoutParams.TYPE_NAVIGATION_BAR
- }
-
- startAnimation(window, navigationBar, callback)
+ return window
}
private fun findTargetWindowIfPossible(
@@ -950,7 +1226,7 @@
for (it in apps) {
if (it.mode == targetMode) {
if (activityTransitionUseLargestWindow()) {
- if (returnAnimationFrameworkLibrary()) {
+ if (returnAnimationsEnabled()) {
// If the controller contains a cookie, _only_ match if either the
// candidate contains the matching cookie, or a component is also
// defined and is a match.
@@ -995,8 +1271,11 @@
private fun startAnimation(
window: RemoteAnimationTarget,
- navigationBar: RemoteAnimationTarget?,
- iCallback: IRemoteAnimationFinishedCallback?,
+ navigationBar: RemoteAnimationTarget? = null,
+ useSpring: Boolean = false,
+ startingWindowStates: Array<WindowAnimationState>? = null,
+ startTransaction: SurfaceControl.Transaction? = null,
+ iCallback: IRemoteAnimationFinishedCallback? = null,
) {
if (TransitionAnimator.DEBUG) {
Log.d(TAG, "Remote animation started")
@@ -1046,18 +1325,66 @@
val controller =
object : Controller by delegate {
override fun createAnimatorState(): TransitionAnimator.State {
- if (isLaunching) return delegate.createAnimatorState()
- return delegate.windowAnimatorState?.toTransitionState()
- ?: getWindowRadius(isExpandingFullyAbove).let {
- TransitionAnimator.State(
- top = windowBounds.top,
- bottom = windowBounds.bottom,
- left = windowBounds.left,
- right = windowBounds.right,
- topCornerRadius = it,
- bottomCornerRadius = it,
- )
+ if (isLaunching) {
+ return delegate.createAnimatorState()
+ } else if (!longLivedReturnAnimationsEnabled()) {
+ return delegate.windowAnimatorState?.toTransitionState()
+ ?: getWindowRadius(isExpandingFullyAbove).let {
+ TransitionAnimator.State(
+ top = windowBounds.top,
+ bottom = windowBounds.bottom,
+ left = windowBounds.left,
+ right = windowBounds.right,
+ topCornerRadius = it,
+ bottomCornerRadius = it,
+ )
+ }
+ }
+
+ // The states are sorted matching the changes inside the transition info.
+ // Using this info, the RemoteAnimationTargets are created, with their
+ // prefixOrderIndex fields in reverse order to that of changes. To extract
+ // the right state, we need to invert again.
+ val windowState =
+ if (startingWindowStates != null) {
+ startingWindowStates[
+ startingWindowStates.size - window.prefixOrderIndex]
+ } else {
+ controller.windowAnimatorState
}
+
+ // TODO(b/323863002): use the timestamp and velocity to update the initial
+ // position.
+ val bounds = windowState?.bounds
+ val left: Int = bounds?.left?.toInt() ?: windowBounds.left
+ val top: Int = bounds?.top?.toInt() ?: windowBounds.top
+ val right: Int = bounds?.right?.toInt() ?: windowBounds.right
+ val bottom: Int = bounds?.bottom?.toInt() ?: windowBounds.bottom
+
+ val width = windowBounds.right - windowBounds.left
+ val height = windowBounds.bottom - windowBounds.top
+ // Scale the window. We use the max of (widthRatio, heightRatio) so that
+ // there is no blank space on any side.
+ val widthRatio = (right - left).toFloat() / width
+ val heightRatio = (bottom - top).toFloat() / height
+ val startScale = maxOf(widthRatio, heightRatio)
+
+ val maybeRadius = windowState?.topLeftRadius
+ val windowRadius =
+ if (maybeRadius != null) {
+ maybeRadius * startScale
+ } else {
+ getWindowRadius(isExpandingFullyAbove)
+ }
+
+ return TransitionAnimator.State(
+ top = top,
+ bottom = bottom,
+ left = left,
+ right = right,
+ topCornerRadius = windowRadius,
+ bottomCornerRadius = windowRadius,
+ )
}
override fun onTransitionAnimationStart(isExpandingFullyAbove: Boolean) {
@@ -1071,6 +1398,19 @@
"[controller=$delegate]",
)
}
+
+ if (startTransaction != null) {
+ // Calling applyStateToWindow() here avoids skipping a frame when taking
+ // over an animation.
+ applyStateToWindow(
+ window,
+ createAnimatorState(),
+ linearProgress = 0f,
+ useSpring,
+ startTransaction,
+ )
+ }
+
delegate.onTransitionAnimationStart(isExpandingFullyAbove)
}
@@ -1094,14 +1434,29 @@
progress: Float,
linearProgress: Float,
) {
- applyStateToWindow(window, state, linearProgress)
+ applyStateToWindow(window, state, linearProgress, useSpring)
navigationBar?.let { applyStateToNavigationBar(it, state, linearProgress) }
listener?.onTransitionAnimationProgress(linearProgress)
delegate.onTransitionAnimationProgress(state, progress, linearProgress)
}
}
-
+ val windowState =
+ if (startingWindowStates != null) {
+ startingWindowStates[startingWindowStates.size - window.prefixOrderIndex]
+ } else {
+ controller.windowAnimatorState
+ }
+ val velocityPxPerS =
+ if (longLivedReturnAnimationsEnabled() && windowState?.velocityPxPerMs != null) {
+ val xVelocityPxPerS = windowState.velocityPxPerMs.x * 1000
+ val yVelocityPxPerS = windowState.velocityPxPerMs.y * 1000
+ PointF(xVelocityPxPerS, yVelocityPxPerS)
+ } else if (useSpring) {
+ PointF(0f, 0f)
+ } else {
+ null
+ }
animation =
transitionAnimator.startAnimation(
controller,
@@ -1109,6 +1464,7 @@
windowBackgroundColor,
fadeWindowBackgroundLayer = !controller.isBelowAnimatingWindow,
drawHole = !controller.isBelowAnimatingWindow,
+ startVelocity = velocityPxPerS,
)
}
@@ -1128,6 +1484,8 @@
window: RemoteAnimationTarget,
state: TransitionAnimator.State,
linearProgress: Float,
+ useSpring: Boolean,
+ transaction: SurfaceControl.Transaction? = null,
) {
if (transactionApplierView.viewRootImpl == null || !window.leash.isValid) {
// Don't apply any transaction if the view root we synchronize with was detached or
@@ -1171,25 +1529,47 @@
windowCropF.bottom.roundToInt(),
)
- val windowAnimationDelay =
+ val interpolators: TransitionAnimator.Interpolators
+ val windowProgress: Float
+
+ if (useSpring) {
+ val windowAnimationDelay: Float
+ val windowAnimationDuration: Float
if (controller.isLaunching) {
- TIMINGS.contentAfterFadeInDelay
+ windowAnimationDelay = SPRING_TIMINGS.contentAfterFadeInDelay
+ windowAnimationDuration = SPRING_TIMINGS.contentAfterFadeInDuration
} else {
- TIMINGS.contentBeforeFadeOutDelay
+ windowAnimationDelay = SPRING_TIMINGS.contentBeforeFadeOutDelay
+ windowAnimationDuration = SPRING_TIMINGS.contentBeforeFadeOutDuration
}
- val windowAnimationDuration =
+
+ interpolators = SPRING_INTERPOLATORS
+ windowProgress =
+ TransitionAnimator.getProgress(
+ linearProgress,
+ windowAnimationDelay,
+ windowAnimationDuration,
+ )
+ } else {
+ val windowAnimationDelay: Long
+ val windowAnimationDuration: Long
if (controller.isLaunching) {
- TIMINGS.contentAfterFadeInDuration
+ windowAnimationDelay = TIMINGS.contentAfterFadeInDelay
+ windowAnimationDuration = TIMINGS.contentAfterFadeInDuration
} else {
- TIMINGS.contentBeforeFadeOutDuration
+ windowAnimationDelay = TIMINGS.contentBeforeFadeOutDelay
+ windowAnimationDuration = TIMINGS.contentBeforeFadeOutDuration
}
- val windowProgress =
- TransitionAnimator.getProgress(
- TIMINGS,
- linearProgress,
- windowAnimationDelay,
- windowAnimationDuration,
- )
+
+ interpolators = INTERPOLATORS
+ windowProgress =
+ TransitionAnimator.getProgress(
+ TIMINGS,
+ linearProgress,
+ windowAnimationDelay,
+ windowAnimationDuration,
+ )
+ }
// The alpha of the opening window. If it opens above the expandable, then it should
// fade in progressively. Otherwise, it should be fully opaque and will be progressively
@@ -1197,12 +1577,12 @@
val alpha =
if (controller.isBelowAnimatingWindow) {
if (controller.isLaunching) {
- INTERPOLATORS.contentAfterFadeInInterpolator.getInterpolation(
+ interpolators.contentAfterFadeInInterpolator.getInterpolation(
windowProgress
)
} else {
1 -
- INTERPOLATORS.contentBeforeFadeOutInterpolator.getInterpolation(
+ interpolators.contentBeforeFadeOutInterpolator.getInterpolation(
windowProgress
)
}
@@ -1216,6 +1596,7 @@
// especially important for lock screen animations, where the window is not clipped by
// the shade.
val cornerRadius = maxOf(state.topCornerRadius, state.bottomCornerRadius) / scale
+
val params =
SyncRtSurfaceTransactionApplier.SurfaceParams.Builder(window.leash)
.withAlpha(alpha)
@@ -1223,11 +1604,15 @@
.withWindowCrop(windowCrop)
.withCornerRadius(cornerRadius)
.withVisibility(true)
- .build()
+ if (transaction != null) params.withMergeTransaction(transaction)
- transactionApplier.scheduleApply(params)
+ transactionApplier.scheduleApply(params.build())
}
+ // TODO(b/377643129): remote transitions have no way of identifying the navbar when
+ // converting to RemoteAnimationTargets (and in my testing it was never included in the
+ // transition at all). So this method is not used anymore. Remove or adapt once we fully
+ // convert to remote transitions.
private fun applyStateToNavigationBar(
navigationBar: RemoteAnimationTarget,
state: TransitionAnimator.State,
@@ -1362,9 +1747,17 @@
}
/** Register [remoteTransition] with WM Shell using the given [filter]. */
- internal fun register(filter: TransitionFilter, remoteTransition: RemoteTransition) {
+ internal fun register(
+ filter: TransitionFilter,
+ remoteTransition: RemoteTransition,
+ includeTakeover: Boolean,
+ ) {
shellTransitions?.registerRemote(filter, remoteTransition)
iShellTransitions?.registerRemote(filter, remoteTransition)
+ if (includeTakeover) {
+ shellTransitions?.registerRemoteForTakeover(filter, remoteTransition)
+ iShellTransitions?.registerRemoteForTakeover(filter, remoteTransition)
+ }
}
/** Unregister [remoteTransition] from WM Shell. */
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/TransitionAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/TransitionAnimator.kt
index fdb4871..de4bdbc 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/TransitionAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/TransitionAnimator.kt
@@ -38,6 +38,7 @@
import com.android.internal.dynamicanimation.animation.SpringAnimation
import com.android.internal.dynamicanimation.animation.SpringForce
import com.android.systemui.shared.Flags.returnAnimationFrameworkLibrary
+import com.android.systemui.shared.Flags.returnAnimationFrameworkLongLived
import java.util.concurrent.Executor
import kotlin.math.abs
import kotlin.math.max
@@ -113,13 +114,26 @@
)
}
- internal fun checkReturnAnimationFrameworkFlag() {
- check(returnAnimationFrameworkLibrary()) {
- "isLaunching cannot be false when the returnAnimationFrameworkLibrary flag is " +
- "disabled"
+ internal fun assertReturnAnimations() {
+ check(returnAnimationsEnabled()) {
+ "isLaunching cannot be false when the returnAnimationFrameworkLibrary flag " +
+ "is disabled"
}
}
+ internal fun returnAnimationsEnabled() = returnAnimationFrameworkLibrary()
+
+ internal fun assertLongLivedReturnAnimations() {
+ check(longLivedReturnAnimationsEnabled()) {
+ "Long-lived registrations cannot be used when the " +
+ "returnAnimationFrameworkLibrary or the " +
+ "returnAnimationFrameworkLongLived flag are disabled"
+ }
+ }
+
+ internal fun longLivedReturnAnimationsEnabled() =
+ returnAnimationFrameworkLibrary() && returnAnimationFrameworkLongLived()
+
internal fun WindowAnimationState.toTransitionState() =
State().also {
bounds?.let { b ->
@@ -467,7 +481,8 @@
drawHole: Boolean = false,
startVelocity: PointF? = null,
): Animation {
- if (!controller.isLaunching || startVelocity != null) checkReturnAnimationFrameworkFlag()
+ if (!controller.isLaunching) assertReturnAnimations()
+ if (startVelocity != null) assertLongLivedReturnAnimations()
// We add an extra layer with the same color as the dialog/app splash screen background
// color, which is usually the same color of the app background. We first fade in this layer
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/theme/PlatformTheme.kt b/packages/SystemUI/compose/core/src/com/android/compose/theme/PlatformTheme.kt
index d31d7aa..71ec63c 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/theme/PlatformTheme.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/theme/PlatformTheme.kt
@@ -63,6 +63,9 @@
return if (isDarkTheme) {
dynamicDarkColorScheme(context)
.copy(
+ inverseSurface = color(context, R.color.system_inverse_surface_dark),
+ inverseOnSurface = color(context, R.color.system_inverse_on_surface_dark),
+ inversePrimary = color(context, R.color.system_inverse_primary_dark),
error = color(context, R.color.system_error_dark),
onError = color(context, R.color.system_on_error_dark),
errorContainer = color(context, R.color.system_error_container_dark),
@@ -71,6 +74,9 @@
} else {
dynamicLightColorScheme(context)
.copy(
+ inverseSurface = color(context, R.color.system_inverse_surface_light),
+ inverseOnSurface = color(context, R.color.system_inverse_on_surface_light),
+ inversePrimary = color(context, R.color.system_inverse_primary_light),
error = color(context, R.color.system_error_light),
onError = color(context, R.color.system_on_error_light),
errorContainer = color(context, R.color.system_error_container_light),
diff --git a/packages/SystemUI/compose/core/tests/src/com/android/compose/theme/PlatformThemeTest.kt b/packages/SystemUI/compose/core/tests/src/com/android/compose/theme/PlatformThemeTest.kt
index de021a0..737853b 100644
--- a/packages/SystemUI/compose/core/tests/src/com/android/compose/theme/PlatformThemeTest.kt
+++ b/packages/SystemUI/compose/core/tests/src/com/android/compose/theme/PlatformThemeTest.kt
@@ -89,7 +89,7 @@
addValue(
"inversePrimary",
colorScheme.inversePrimary,
- R.attr.materialColorPrimaryInverse,
+ R.attr.materialColorInversePrimary,
)
addValue("secondary", colorScheme.secondary, R.attr.materialColorSecondary)
addValue("onSecondary", colorScheme.onSecondary, R.attr.materialColorOnSecondary)
@@ -131,12 +131,12 @@
addValue(
"inverseSurface",
colorScheme.inverseSurface,
- R.attr.materialColorSurfaceInverse,
+ R.attr.materialColorInverseSurface,
)
addValue(
"inverseOnSurface",
colorScheme.inverseOnSurface,
- R.attr.materialColorOnSurfaceInverse,
+ R.attr.materialColorInverseOnSurface,
)
addValue("error", colorScheme.error, R.attr.materialColorError)
addValue("onError", colorScheme.onError, R.attr.materialColorOnError)
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt
index 96e99b1..778d7e7 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt
@@ -67,6 +67,7 @@
dialogFactory = dialogFactory,
widgetSection = widgetSection,
modifier = Modifier.element(Communal.Elements.Grid),
+ sceneScope = this@Content,
)
}
with(lockSection) {
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
index df1185c..5b1203f 100644
--- 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
@@ -163,6 +163,7 @@
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.window.layout.WindowMetricsCalculator
import com.android.compose.animation.Easings.Emphasized
+import com.android.compose.animation.scene.SceneScope
import com.android.compose.modifiers.thenIf
import com.android.compose.ui.graphics.painter.rememberDrawablePainter
import com.android.internal.R.dimen.system_app_widget_background_radius
@@ -186,7 +187,9 @@
import com.android.systemui.communal.widgets.SmartspaceAppWidgetHostView
import com.android.systemui.communal.widgets.WidgetConfigurator
import com.android.systemui.lifecycle.rememberViewModel
+import com.android.systemui.media.controls.ui.composable.MediaCarousel
import com.android.systemui.res.R
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.statusbar.phone.SystemUIDialogFactory
import kotlin.math.max
import kotlin.math.min
@@ -205,6 +208,7 @@
widgetConfigurator: WidgetConfigurator? = null,
onOpenWidgetPicker: (() -> Unit)? = null,
onEditDone: (() -> Unit)? = null,
+ sceneScope: SceneScope? = null,
) {
val communalContent by
viewModel.communalContent.collectAsStateWithLifecycle(initialValue = emptyList())
@@ -414,6 +418,7 @@
widgetConfigurator = widgetConfigurator,
interactionHandler = interactionHandler,
widgetSection = widgetSection,
+ sceneScope = sceneScope,
)
}
}
@@ -735,6 +740,7 @@
widgetConfigurator: WidgetConfigurator?,
interactionHandler: RemoteViews.InteractionHandler?,
widgetSection: CommunalAppWidgetSection,
+ sceneScope: SceneScope?,
) {
var gridModifier =
Modifier.align(Alignment.TopStart).onGloballyPositioned { setGridCoordinates(it) }
@@ -871,6 +877,7 @@
interactionHandler = interactionHandler,
widgetSection = widgetSection,
resizeableItemFrameViewModel = resizeableItemFrameViewModel,
+ sceneScope = sceneScope,
)
}
}
@@ -1095,6 +1102,7 @@
interactionHandler: RemoteViews.InteractionHandler?,
widgetSection: CommunalAppWidgetSection,
resizeableItemFrameViewModel: ResizeableItemFrameViewModel,
+ sceneScope: SceneScope? = null,
) {
when (model) {
is CommunalContentModel.WidgetContent.Widget ->
@@ -1118,7 +1126,7 @@
is CommunalContentModel.CtaTileInViewMode -> CtaTileInViewModeContent(viewModel, modifier)
is CommunalContentModel.Smartspace -> SmartspaceContent(interactionHandler, model, modifier)
is CommunalContentModel.Tutorial -> TutorialContent(modifier)
- is CommunalContentModel.Umo -> Umo(viewModel, modifier)
+ is CommunalContentModel.Umo -> Umo(viewModel, sceneScope, modifier)
}
}
@@ -1529,7 +1537,25 @@
}
@Composable
-private fun Umo(viewModel: BaseCommunalViewModel, modifier: Modifier = Modifier) {
+private fun Umo(
+ viewModel: BaseCommunalViewModel,
+ sceneScope: SceneScope?,
+ modifier: Modifier = Modifier,
+) {
+ if (SceneContainerFlag.isEnabled && sceneScope != null) {
+ sceneScope.MediaCarousel(
+ modifier = modifier.fillMaxSize(),
+ isVisible = true,
+ mediaHost = viewModel.mediaHost,
+ carouselController = viewModel.mediaCarouselController,
+ )
+ } else {
+ UmoLegacy(viewModel, modifier)
+ }
+}
+
+@Composable
+private fun UmoLegacy(viewModel: BaseCommunalViewModel, modifier: Modifier = Modifier) {
AndroidView(
modifier =
modifier.pointerInput(Unit) {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt
index 1551ca9..ef5e90b 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt
@@ -179,11 +179,18 @@
val targetItem =
if (communalWidgetResizing()) {
state.layoutInfo.visibleItemsInfo.findLast { item ->
+ val lastVisibleItemIndex = state.layoutInfo.visibleItemsInfo.last().index
val itemBoundingBox = IntRect(item.offset, item.size)
draggingItemKey != item.key &&
contentListState.isItemEditable(item.index) &&
(draggingBoundingBox.contains(itemBoundingBox.center) ||
- itemBoundingBox.contains(draggingBoundingBox.center))
+ itemBoundingBox.contains(draggingBoundingBox.center)) &&
+ // If we swap with the last visible item, and that item doesn't fit
+ // in the gap created by moving the current item, then the current item
+ // will get placed after the last visible item. In this case, it gets
+ // placed outside of the viewport. We avoid this here, so the user
+ // has to scroll first before the swap can happen.
+ (item.index != lastVisibleItemIndex || item.span <= draggingItem.span)
}
} else {
state.layoutInfo.visibleItemsInfo
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/LockSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/LockSection.kt
index 9390664..597cbf2 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/LockSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/LockSection.kt
@@ -31,6 +31,7 @@
import com.android.compose.animation.scene.ElementKey
import com.android.compose.animation.scene.SceneScope
import com.android.systemui.biometrics.AuthController
+import com.android.systemui.customization.R as customR
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.flags.FeatureFlagsClassic
import com.android.systemui.flags.Flags
@@ -147,7 +148,7 @@
} else {
val scaleFactor = authController.scaleFactor
val bottomPaddingPx =
- context.resources.getDimensionPixelSize(R.dimen.lock_icon_margin_bottom)
+ context.resources.getDimensionPixelSize(customR.dimen.lock_icon_margin_bottom)
val heightPx = windowViewBounds.bottom.toFloat()
Pair(
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt
index 20c8c6a3..2d32fd7 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt
@@ -36,6 +36,7 @@
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.foundation.shape.CornerSize
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Icon
import androidx.compose.material3.LocalContentColor
@@ -84,6 +85,7 @@
import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel
import com.android.systemui.qs.ui.composable.QuickSettings
import com.android.systemui.qs.ui.composable.QuickSettingsTheme
+import com.android.systemui.qs.ui.compose.borderOnFocus
import com.android.systemui.res.R
import kotlinx.coroutines.launch
@@ -264,7 +266,11 @@
color = colorAttr(model.backgroundColor),
shape = CircleShape,
onClick = model.onClick,
- modifier = modifier,
+ modifier =
+ modifier.borderOnFocus(
+ color = MaterialTheme.colorScheme.secondary,
+ CornerSize(percent = 50),
+ ),
) {
val tint = model.iconTint?.let { Color(it) } ?: Color.Unspecified
Icon(model.icon, tint = tint, modifier = Modifier.size(20.dp))
@@ -291,7 +297,11 @@
shape = CircleShape,
onClick = onClick,
interactionSource = interactionSource,
- modifier = modifier,
+ modifier =
+ modifier.borderOnFocus(
+ color = MaterialTheme.colorScheme.secondary,
+ CornerSize(percent = 50),
+ ),
) {
Box(Modifier.size(40.dp)) {
Box(
@@ -342,7 +352,10 @@
color = colorAttr(R.attr.underSurface),
contentColor = MaterialTheme.colorScheme.onSurfaceVariant,
borderStroke = BorderStroke(1.dp, colorAttr(R.attr.shadeInactive)),
- modifier = modifier.padding(horizontal = 4.dp),
+ modifier =
+ modifier
+ .padding(horizontal = 4.dp)
+ .borderOnFocus(color = MaterialTheme.colorScheme.secondary, CornerSize(50)),
onClick = onClick,
) {
Row(
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt
index 2cde678..9de7a5d 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt
@@ -15,7 +15,10 @@
import com.android.systemui.scene.ui.composable.transitions.bouncerToLockscreenPreview
import com.android.systemui.scene.ui.composable.transitions.communalToBouncerTransition
import com.android.systemui.scene.ui.composable.transitions.communalToShadeTransition
+import com.android.systemui.scene.ui.composable.transitions.dreamToBouncerTransition
+import com.android.systemui.scene.ui.composable.transitions.dreamToCommunalTransition
import com.android.systemui.scene.ui.composable.transitions.dreamToGoneTransition
+import com.android.systemui.scene.ui.composable.transitions.dreamToShadeTransition
import com.android.systemui.scene.ui.composable.transitions.goneToQuickSettingsTransition
import com.android.systemui.scene.ui.composable.transitions.goneToShadeTransition
import com.android.systemui.scene.ui.composable.transitions.goneToSplitShadeTransition
@@ -55,7 +58,10 @@
// Scene transitions
from(Scenes.Bouncer, to = Scenes.Gone) { bouncerToGoneTransition() }
+ from(Scenes.Dream, to = Scenes.Bouncer) { dreamToBouncerTransition() }
+ from(Scenes.Dream, to = Scenes.Communal) { dreamToCommunalTransition() }
from(Scenes.Dream, to = Scenes.Gone) { dreamToGoneTransition() }
+ from(Scenes.Dream, to = Scenes.Shade) { dreamToShadeTransition() }
from(Scenes.Gone, to = Scenes.Shade) { goneToShadeTransition() }
from(Scenes.Gone, to = Scenes.Shade, key = ToSplitShade) { goneToSplitShadeTransition() }
from(Scenes.Gone, to = Scenes.Shade, key = SlightlyFasterShadeCollapse) {
diff --git a/core/java/android/text/ClientFlags.java b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromDreamToBouncerTransition.kt
similarity index 60%
copy from core/java/android/text/ClientFlags.java
copy to packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromDreamToBouncerTransition.kt
index ca88764..8cb89f9 100644
--- a/core/java/android/text/ClientFlags.java
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromDreamToBouncerTransition.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,16 +14,10 @@
* limitations under the License.
*/
-package android.text;
+package com.android.systemui.scene.ui.composable.transitions
-/**
- * An aconfig feature flags that can be accessible from application process without
- * ContentProvider IPCs.
- *
- * When you add new flags, you have to add flag string to {@link TextFlags#TEXT_ACONFIGS_FLAGS}.
- *
- * TODO(nona): Remove this class.
- * @hide
- */
-public class ClientFlags {
+import com.android.compose.animation.scene.TransitionBuilder
+
+fun TransitionBuilder.dreamToBouncerTransition() {
+ toBouncerTransition()
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromDreamToCommunalTransition.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromDreamToCommunalTransition.kt
new file mode 100644
index 0000000..93c10b6
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromDreamToCommunalTransition.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.scene.ui.composable.transitions
+
+import androidx.compose.animation.core.tween
+import com.android.compose.animation.scene.Edge
+import com.android.compose.animation.scene.TransitionBuilder
+import com.android.systemui.communal.ui.compose.AllElements
+import com.android.systemui.communal.ui.compose.Communal
+
+fun TransitionBuilder.dreamToCommunalTransition() {
+ spec = tween(durationMillis = 1000)
+
+ // Translate communal hub grid from the end direction.
+ translate(Communal.Elements.Grid, Edge.End)
+
+ // Fade all communal hub elements.
+ timestampRange(startMillis = 167, endMillis = 334) { fade(AllElements) }
+}
diff --git a/core/java/android/text/ClientFlags.java b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromDreamToShadeTransition.kt
similarity index 60%
copy from core/java/android/text/ClientFlags.java
copy to packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromDreamToShadeTransition.kt
index ca88764..e35aaae 100644
--- a/core/java/android/text/ClientFlags.java
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromDreamToShadeTransition.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,16 +14,10 @@
* limitations under the License.
*/
-package android.text;
+package com.android.systemui.scene.ui.composable.transitions
-/**
- * An aconfig feature flags that can be accessible from application process without
- * ContentProvider IPCs.
- *
- * When you add new flags, you have to add flag string to {@link TextFlags#TEXT_ACONFIGS_FLAGS}.
- *
- * TODO(nona): Remove this class.
- * @hide
- */
-public class ClientFlags {
+import com.android.compose.animation.scene.TransitionBuilder
+
+fun TransitionBuilder.dreamToShadeTransition(durationScale: Double = 1.0) {
+ toShadeTransition(durationScale = durationScale)
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromGoneToSplitShadeTransition.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromGoneToSplitShadeTransition.kt
index 71fa6c9..ce7a85b1 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromGoneToSplitShadeTransition.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromGoneToSplitShadeTransition.kt
@@ -19,11 +19,8 @@
import androidx.compose.animation.core.Spring
import androidx.compose.animation.core.spring
import androidx.compose.animation.core.tween
-import androidx.compose.foundation.gestures.Orientation
-import androidx.compose.ui.unit.IntSize
import com.android.compose.animation.scene.TransitionBuilder
import com.android.compose.animation.scene.UserActionDistance
-import com.android.compose.animation.scene.UserActionDistanceScope
import com.android.systemui.media.controls.ui.composable.MediaCarousel
import com.android.systemui.notifications.ui.composable.Notifications
import com.android.systemui.qs.ui.composable.QuickSettings
@@ -31,24 +28,17 @@
import com.android.systemui.shade.ui.composable.ShadeHeader
import kotlin.time.Duration.Companion.milliseconds
-fun TransitionBuilder.goneToSplitShadeTransition(
- durationScale: Double = 1.0,
-) {
+fun TransitionBuilder.goneToSplitShadeTransition(durationScale: Double = 1.0) {
spec = tween(durationMillis = (DefaultDuration * durationScale).inWholeMilliseconds.toInt())
swipeSpec =
spring(
stiffness = Spring.StiffnessMediumLow,
visibilityThreshold = Shade.Dimensions.ScrimVisibilityThreshold,
)
- distance =
- object : UserActionDistance {
- override fun UserActionDistanceScope.absoluteDistance(
- fromSceneSize: IntSize,
- orientation: Orientation,
- ): Float {
- return fromSceneSize.height.toFloat() * 2 / 3f
- }
- }
+ distance = UserActionDistance { fromContent, _, _ ->
+ val fromContentSize = checkNotNull(fromContent.targetSize())
+ fromContentSize.height.toFloat() * 2 / 3f
+ }
fractionRange(end = .33f) { fade(Shade.Elements.BackgroundScrim) }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToSplitShadeTransition.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToSplitShadeTransition.kt
index 1486ea7..1f7a738 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToSplitShadeTransition.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToSplitShadeTransition.kt
@@ -19,35 +19,25 @@
import androidx.compose.animation.core.Spring
import androidx.compose.animation.core.spring
import androidx.compose.animation.core.tween
-import androidx.compose.foundation.gestures.Orientation
-import androidx.compose.ui.unit.IntSize
import com.android.compose.animation.scene.TransitionBuilder
import com.android.compose.animation.scene.UserActionDistance
-import com.android.compose.animation.scene.UserActionDistanceScope
import com.android.systemui.notifications.ui.composable.Notifications
import com.android.systemui.qs.ui.composable.QuickSettings
import com.android.systemui.shade.ui.composable.Shade
import com.android.systemui.shade.ui.composable.ShadeHeader
import kotlin.time.Duration.Companion.milliseconds
-fun TransitionBuilder.lockscreenToSplitShadeTransition(
- durationScale: Double = 1.0,
-) {
+fun TransitionBuilder.lockscreenToSplitShadeTransition(durationScale: Double = 1.0) {
spec = tween(durationMillis = (DefaultDuration * durationScale).inWholeMilliseconds.toInt())
swipeSpec =
spring(
stiffness = Spring.StiffnessMediumLow,
visibilityThreshold = Shade.Dimensions.ScrimVisibilityThreshold,
)
- distance =
- object : UserActionDistance {
- override fun UserActionDistanceScope.absoluteDistance(
- fromSceneSize: IntSize,
- orientation: Orientation,
- ): Float {
- return fromSceneSize.height.toFloat() * 2 / 3f
- }
- }
+ distance = UserActionDistance { fromContent, _, _ ->
+ val fromContentSize = checkNotNull(fromContent.targetSize())
+ fromContentSize.height.toFloat() * 2 / 3f
+ }
fractionRange(end = .33f) { fade(Shade.Elements.BackgroundScrim) }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromShadeToQuickSettingsTransition.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromShadeToQuickSettingsTransition.kt
index 8cd357e..ba1972f 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromShadeToQuickSettingsTransition.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromShadeToQuickSettingsTransition.kt
@@ -1,46 +1,36 @@
package com.android.systemui.scene.ui.composable.transitions
import androidx.compose.animation.core.tween
-import androidx.compose.foundation.gestures.Orientation
-import androidx.compose.ui.unit.IntSize
import com.android.compose.animation.scene.Edge
import com.android.compose.animation.scene.TransitionBuilder
import com.android.compose.animation.scene.UserActionDistance
-import com.android.compose.animation.scene.UserActionDistanceScope
import com.android.systemui.notifications.ui.composable.Notifications
import com.android.systemui.qs.ui.composable.QuickSettings
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.shade.ui.composable.ShadeHeader
import kotlin.time.Duration.Companion.milliseconds
-fun TransitionBuilder.shadeToQuickSettingsTransition(
- durationScale: Double = 1.0,
-) {
+fun TransitionBuilder.shadeToQuickSettingsTransition(durationScale: Double = 1.0) {
spec = tween(durationMillis = (DefaultDuration * durationScale).inWholeMilliseconds.toInt())
- distance =
- object : UserActionDistance {
- override fun UserActionDistanceScope.absoluteDistance(
- fromSceneSize: IntSize,
- orientation: Orientation,
- ): Float {
- val distance =
- Notifications.Elements.NotificationScrim.targetOffset(Scenes.Shade)?.y
- ?: return 0f
- return fromSceneSize.height - distance
- }
- }
+ distance = UserActionDistance { fromContent, _, _ ->
+ val distance =
+ Notifications.Elements.NotificationScrim.targetOffset(Scenes.Shade)?.y
+ ?: return@UserActionDistance 0f
+ val fromContentSize = checkNotNull(fromContent.targetSize())
+ fromContentSize.height - distance
+ }
translate(Notifications.Elements.NotificationScrim, Edge.Bottom)
timestampRange(endMillis = 83) { fade(QuickSettings.Elements.FooterActions) }
translate(
ShadeHeader.Elements.CollapsedContentStart,
- y = ShadeHeader.Dimensions.CollapsedHeight
+ y = ShadeHeader.Dimensions.CollapsedHeight,
)
translate(ShadeHeader.Elements.CollapsedContentEnd, y = ShadeHeader.Dimensions.CollapsedHeight)
translate(
ShadeHeader.Elements.ExpandedContent,
- y = -(ShadeHeader.Dimensions.ExpandedHeight - ShadeHeader.Dimensions.CollapsedHeight)
+ y = -(ShadeHeader.Dimensions.ExpandedHeight - ShadeHeader.Dimensions.CollapsedHeight),
)
translate(ShadeHeader.Elements.ShadeCarrierGroup, y = -ShadeHeader.Dimensions.CollapsedHeight)
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToBouncerTransition.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToBouncerTransition.kt
index de76f70..d35537a 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToBouncerTransition.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToBouncerTransition.kt
@@ -28,8 +28,9 @@
fun TransitionBuilder.toBouncerTransition() {
spec = tween(durationMillis = 500)
- distance = UserActionDistance { fromSceneSize, _ ->
- fromSceneSize.height * TO_BOUNCER_SWIPE_DISTANCE_FRACTION
+ distance = UserActionDistance { fromContent, _, _ ->
+ val fromContentSize = checkNotNull(fromContent.targetSize())
+ fromContentSize.height * TO_BOUNCER_SWIPE_DISTANCE_FRACTION
}
translate(Bouncer.Elements.Content, y = 300.dp)
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToQuickSettingsShadeTransition.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToQuickSettingsShadeTransition.kt
index 55fa6ad..e78bc6a 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToQuickSettingsShadeTransition.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToQuickSettingsShadeTransition.kt
@@ -33,7 +33,10 @@
stiffness = Spring.StiffnessMediumLow,
visibilityThreshold = Shade.Dimensions.ScrimVisibilityThreshold,
)
- distance = UserActionDistance { fromSceneSize, _ -> fromSceneSize.height.toFloat() * 2 / 3f }
+ distance = UserActionDistance { fromContent, _, _ ->
+ val fromContentSize = checkNotNull(fromContent.targetSize())
+ fromContentSize.height.toFloat() * 2 / 3f
+ }
translate(OverlayShade.Elements.Panel, Edge.Top)
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToShadeTransition.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToShadeTransition.kt
index b677dff..bfae489 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToShadeTransition.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToShadeTransition.kt
@@ -19,12 +19,9 @@
import androidx.compose.animation.core.Spring
import androidx.compose.animation.core.spring
import androidx.compose.animation.core.tween
-import androidx.compose.foundation.gestures.Orientation
-import androidx.compose.ui.unit.IntSize
import com.android.compose.animation.scene.Edge
import com.android.compose.animation.scene.TransitionBuilder
import com.android.compose.animation.scene.UserActionDistance
-import com.android.compose.animation.scene.UserActionDistanceScope
import com.android.systemui.media.controls.ui.composable.MediaCarousel
import com.android.systemui.notifications.ui.composable.Notifications
import com.android.systemui.qs.ui.composable.QuickSettings
@@ -33,24 +30,16 @@
import com.android.systemui.shade.ui.composable.ShadeHeader
import kotlin.time.Duration.Companion.milliseconds
-fun TransitionBuilder.toShadeTransition(
- durationScale: Double = 1.0,
-) {
+fun TransitionBuilder.toShadeTransition(durationScale: Double = 1.0) {
spec = tween(durationMillis = (DefaultDuration * durationScale).inWholeMilliseconds.toInt())
swipeSpec =
spring(
stiffness = Spring.StiffnessMediumLow,
visibilityThreshold = Shade.Dimensions.ScrimVisibilityThreshold,
)
- distance =
- object : UserActionDistance {
- override fun UserActionDistanceScope.absoluteDistance(
- fromSceneSize: IntSize,
- orientation: Orientation,
- ): Float {
- return Notifications.Elements.NotificationScrim.targetOffset(Scenes.Shade)?.y ?: 0f
- }
- }
+ distance = UserActionDistance { _, _, _ ->
+ Notifications.Elements.NotificationScrim.targetOffset(Scenes.Shade)?.y ?: 0f
+ }
fractionRange(start = .58f) {
fade(ShadeHeader.Elements.Clock)
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/SingleShadeMeasurePolicy.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/SingleShadeMeasurePolicy.kt
index 352d29e2..7489648 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/SingleShadeMeasurePolicy.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/SingleShadeMeasurePolicy.kt
@@ -25,7 +25,6 @@
import androidx.compose.ui.layout.layoutId
import androidx.compose.ui.unit.Constraints
import androidx.compose.ui.unit.offset
-import androidx.compose.ui.util.fastFirst
import androidx.compose.ui.util.fastFirstOrNull
import com.android.systemui.shade.ui.composable.SingleShadeMeasurePolicy.LayoutId
import kotlin.math.max
@@ -60,18 +59,20 @@
val shadeHeaderPlaceable =
measurables
- .fastFirst { it.layoutId == LayoutId.ShadeHeader }
- .measure(constraintsWithCutout)
+ .fastFirstOrNull { it.layoutId == LayoutId.ShadeHeader }
+ ?.measure(constraintsWithCutout)
val mediaPlaceable =
measurables
.fastFirstOrNull { it.layoutId == LayoutId.Media }
?.measure(applyMediaConstraints(constraintsWithCutout, isMediaInRow))
val quickSettingsPlaceable =
measurables
- .fastFirst { it.layoutId == LayoutId.QuickSettings }
- .measure(constraintsWithCutout)
+ .fastFirstOrNull { it.layoutId == LayoutId.QuickSettings }
+ ?.measure(constraintsWithCutout)
val notificationsPlaceable =
- measurables.fastFirst { it.layoutId == LayoutId.Notifications }.measure(constraints)
+ measurables
+ .fastFirstOrNull { it.layoutId == LayoutId.Notifications }
+ ?.measure(constraints)
val notificationsTop =
calculateNotificationsTop(
@@ -84,23 +85,25 @@
onNotificationsTopChanged(notificationsTop)
return layout(constraints.maxWidth, constraints.maxHeight) {
- shadeHeaderPlaceable.placeRelative(x = insetsLeft, y = insetsTop)
- quickSettingsPlaceable.placeRelative(
+ shadeHeaderPlaceable?.placeRelative(x = insetsLeft, y = insetsTop)
+ val statusBarHeaderHeight = shadeHeaderPlaceable?.height ?: 0
+ quickSettingsPlaceable?.placeRelative(
x = insetsLeft,
- y = insetsTop + shadeHeaderPlaceable.height,
+ y = insetsTop + statusBarHeaderHeight,
)
- if (mediaPlaceable != null)
+ if (mediaPlaceable != null) {
+ val quickSettingsHeight = quickSettingsPlaceable?.height ?: 0
+
if (isMediaInRow) {
// mediaPlaceable height ranges from 0 to qsHeight. We want it to be centered
// vertically when it's smaller than the QS
- val mediaCenteringOffset =
- (quickSettingsPlaceable.height - mediaPlaceable.height) / 2
+ val mediaCenteringOffset = (quickSettingsHeight - mediaPlaceable.height) / 2
mediaPlaceable.placeRelative(
x = insetsLeft + constraintsWithCutout.maxWidth / 2,
y =
insetsTop +
- shadeHeaderPlaceable.height +
+ statusBarHeaderHeight +
mediaCenteringOffset +
mediaOffset(),
zIndex = mediaZIndex(),
@@ -108,30 +111,34 @@
} else {
mediaPlaceable.placeRelative(
x = insetsLeft,
- y = insetsTop + shadeHeaderPlaceable.height + quickSettingsPlaceable.height,
+ y = insetsTop + statusBarHeaderHeight + quickSettingsHeight,
zIndex = mediaZIndex(),
)
}
+ }
// Notifications don't need to accommodate for horizontal insets
- notificationsPlaceable.placeRelative(x = 0, y = notificationsTop)
+ notificationsPlaceable?.placeRelative(x = 0, y = notificationsTop)
}
}
private fun calculateNotificationsTop(
- statusBarHeaderPlaceable: Placeable,
- quickSettingsPlaceable: Placeable,
+ statusBarHeaderPlaceable: Placeable?,
+ quickSettingsPlaceable: Placeable?,
mediaPlaceable: Placeable?,
insetsTop: Int,
isMediaInRow: Boolean,
): Int {
val mediaHeight = mediaPlaceable?.height ?: 0
+ val statusBarHeaderHeight = statusBarHeaderPlaceable?.height ?: 0
+ val quickSettingsHeight = quickSettingsPlaceable?.height ?: 0
+
return insetsTop +
- statusBarHeaderPlaceable.height +
+ statusBarHeaderHeight +
if (isMediaInRow) {
- max(quickSettingsPlaceable.height, mediaHeight)
+ max(quickSettingsHeight, mediaHeight)
} else {
- quickSettingsPlaceable.height + mediaHeight
+ quickSettingsHeight + mediaHeight
}
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/statusbar/phone/SystemUIDialogFactoryExt.kt b/packages/SystemUI/compose/features/src/com/android/systemui/statusbar/phone/SystemUIDialogFactoryExt.kt
index 163f4b3..78e6056 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/statusbar/phone/SystemUIDialogFactoryExt.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/statusbar/phone/SystemUIDialogFactoryExt.kt
@@ -307,6 +307,6 @@
private object DraggableBottomSheet {
val DefaultTopPadding = 64.dp
- val LargeScreenTopPadding = 72.dp
+ val LargeScreenTopPadding = 56.dp
val MaxWidth = 640.dp
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt
index c33d655..04c5271 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt
@@ -530,16 +530,12 @@
}
internal class NestedScrollHandlerImpl(
- private val layoutImpl: SceneTransitionLayoutImpl,
- private val orientation: Orientation,
+ private val draggableHandler: DraggableHandlerImpl,
internal var topOrLeftBehavior: NestedScrollBehavior,
internal var bottomOrRightBehavior: NestedScrollBehavior,
internal var isExternalOverscrollGesture: () -> Boolean,
private val pointersInfoOwner: PointersInfoOwner,
) {
- private val layoutState = layoutImpl.state
- private val draggableHandler = layoutImpl.draggableHandler(orientation)
-
val connection: PriorityNestedScrollConnection = nestedScrollConnection()
private fun nestedScrollConnection(): PriorityNestedScrollConnection {
@@ -550,13 +546,15 @@
var lastPointersDown: PointersInfo.PointersDown? = null
fun shouldEnableSwipes(): Boolean {
- return layoutImpl.contentForUserActions().shouldEnableSwipes(orientation)
+ return draggableHandler.layoutImpl
+ .contentForUserActions()
+ .shouldEnableSwipes(draggableHandler.orientation)
}
var isIntercepting = false
return PriorityNestedScrollConnection(
- orientation = orientation,
+ orientation = draggableHandler.orientation,
canStartPreScroll = { offsetAvailable, offsetBeforeStart, _ ->
val pointersDown: PointersInfo.PointersDown? =
when (val info = pointersInfoOwner.pointersInfo()) {
@@ -578,8 +576,9 @@
draggableHandler.shouldImmediatelyIntercept(pointersDown)
if (!canInterceptSwipeTransition) return@PriorityNestedScrollConnection false
+ val layoutImpl = draggableHandler.layoutImpl
val threshold = layoutImpl.transitionInterceptionThreshold
- val hasSnappedToIdle = layoutState.snapToIdleIfClose(threshold)
+ val hasSnappedToIdle = layoutImpl.state.snapToIdleIfClose(threshold)
if (hasSnappedToIdle) {
// If the current swipe transition is closed to 0f or 1f, then we want to
// interrupt the transition (snapping it to Idle) and scroll the list.
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt
index 44f60cb..eb2a016 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt
@@ -350,8 +350,7 @@
val placeInThisContent =
elementContentWhenIdle(
layoutImpl,
- currentState.currentScene,
- currentState.currentOverlays,
+ currentState,
isInContent = { it in element.stateByContent },
) == content.key
@@ -639,20 +638,11 @@
internal inline fun elementContentWhenIdle(
layoutImpl: SceneTransitionLayoutImpl,
- idle: TransitionState.Idle,
+ currentState: TransitionState,
isInContent: (ContentKey) -> Boolean,
): ContentKey {
- val currentScene = idle.currentScene
- val overlays = idle.currentOverlays
- return elementContentWhenIdle(layoutImpl, currentScene, overlays, isInContent)
-}
-
-private inline fun elementContentWhenIdle(
- layoutImpl: SceneTransitionLayoutImpl,
- currentScene: SceneKey,
- overlays: Set<OverlayKey>,
- isInContent: (ContentKey) -> Boolean,
-): ContentKey {
+ val currentScene = currentState.currentScene
+ val overlays = currentState.currentOverlays
if (overlays.isEmpty()) {
return currentScene
}
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
index c790ff0..509a16c 100644
--- 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
@@ -188,7 +188,9 @@
return when (
val elementState = movableElementState(element, layoutImpl.state.transitionStates)
) {
- null -> false
+ null ->
+ movableElementContentWhenIdle(layoutImpl, element, layoutImpl.state.transitionState) ==
+ content
is TransitionState.Idle ->
movableElementContentWhenIdle(layoutImpl, element, elementState) == content
is TransitionState.Transition -> {
@@ -217,7 +219,7 @@
private fun movableElementContentWhenIdle(
layoutImpl: SceneTransitionLayoutImpl,
element: MovableElementKey,
- elementState: TransitionState.Idle,
+ elementState: TransitionState,
): ContentKey {
val contents = element.contentPicker.contents
return elementContentWhenIdle(layoutImpl, elementState, isInContent = { contents.contains(it) })
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt
index 1603267..0db545f 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt
@@ -519,8 +519,19 @@
// we intercept an ongoing swipe transition (i.e. startDragImmediately() returned
// true).
if (overSlop == 0f) {
- val delta = (drag.position - consumablePointer.position).toFloat()
- check(delta != 0f) { "delta is equal to 0" }
+ // If the user drags in the opposite direction, the delta becomes zero because
+ // we return to the original point. Therefore, we should use the previous event
+ // to calculate the direction.
+ val delta = (drag.position - drag.previousPosition).toFloat()
+ check(delta != 0f) {
+ buildString {
+ append("delta is equal to 0 ")
+ append("touchSlop ${currentValueOf(LocalViewConfiguration).touchSlop} ")
+ append("consumablePointer.position ${consumablePointer.position} ")
+ append("drag.position ${drag.position} ")
+ append("drag.previousPosition ${drag.previousPosition}")
+ }
+ }
overSlop = delta.sign
}
drag
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/NestedScrollToScene.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/NestedScrollToScene.kt
index fbd1cd5..955be60 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/NestedScrollToScene.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/NestedScrollToScene.kt
@@ -16,7 +16,6 @@
package com.android.compose.animation.scene
-import androidx.compose.foundation.gestures.Orientation
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
@@ -69,32 +68,28 @@
}
internal fun Modifier.nestedScrollToScene(
- layoutImpl: SceneTransitionLayoutImpl,
- orientation: Orientation,
+ draggableHandler: DraggableHandlerImpl,
topOrLeftBehavior: NestedScrollBehavior,
bottomOrRightBehavior: NestedScrollBehavior,
isExternalOverscrollGesture: () -> Boolean,
) =
this then
NestedScrollToSceneElement(
- layoutImpl = layoutImpl,
- orientation = orientation,
+ draggableHandler = draggableHandler,
topOrLeftBehavior = topOrLeftBehavior,
bottomOrRightBehavior = bottomOrRightBehavior,
isExternalOverscrollGesture = isExternalOverscrollGesture,
)
private data class NestedScrollToSceneElement(
- private val layoutImpl: SceneTransitionLayoutImpl,
- private val orientation: Orientation,
+ private val draggableHandler: DraggableHandlerImpl,
private val topOrLeftBehavior: NestedScrollBehavior,
private val bottomOrRightBehavior: NestedScrollBehavior,
private val isExternalOverscrollGesture: () -> Boolean,
) : ModifierNodeElement<NestedScrollToSceneNode>() {
override fun create() =
NestedScrollToSceneNode(
- layoutImpl = layoutImpl,
- orientation = orientation,
+ draggableHandler = draggableHandler,
topOrLeftBehavior = topOrLeftBehavior,
bottomOrRightBehavior = bottomOrRightBehavior,
isExternalOverscrollGesture = isExternalOverscrollGesture,
@@ -102,8 +97,7 @@
override fun update(node: NestedScrollToSceneNode) {
node.update(
- layoutImpl = layoutImpl,
- orientation = orientation,
+ draggableHandler = draggableHandler,
topOrLeftBehavior = topOrLeftBehavior,
bottomOrRightBehavior = bottomOrRightBehavior,
isExternalOverscrollGesture = isExternalOverscrollGesture,
@@ -112,16 +106,14 @@
override fun InspectorInfo.inspectableProperties() {
name = "nestedScrollToScene"
- properties["layoutImpl"] = layoutImpl
- properties["orientation"] = orientation
+ properties["draggableHandler"] = draggableHandler
properties["topOrLeftBehavior"] = topOrLeftBehavior
properties["bottomOrRightBehavior"] = bottomOrRightBehavior
}
}
private class NestedScrollToSceneNode(
- private var layoutImpl: SceneTransitionLayoutImpl,
- private var orientation: Orientation,
+ private var draggableHandler: DraggableHandlerImpl,
private var topOrLeftBehavior: NestedScrollBehavior,
private var bottomOrRightBehavior: NestedScrollBehavior,
private var isExternalOverscrollGesture: () -> Boolean,
@@ -129,12 +121,8 @@
private var scrollBehaviorOwner: ScrollBehaviorOwner? = null
private fun findScrollBehaviorOwner(): ScrollBehaviorOwner? {
- var behaviorOwner = scrollBehaviorOwner
- if (behaviorOwner == null) {
- behaviorOwner = findScrollBehaviorOwner(layoutImpl.draggableHandler(orientation))
- scrollBehaviorOwner = behaviorOwner
- }
- return behaviorOwner
+ return scrollBehaviorOwner
+ ?: findScrollBehaviorOwner(draggableHandler).also { scrollBehaviorOwner = it }
}
private val updateScrollBehaviorsConnection =
@@ -177,14 +165,12 @@
}
fun update(
- layoutImpl: SceneTransitionLayoutImpl,
- orientation: Orientation,
+ draggableHandler: DraggableHandlerImpl,
topOrLeftBehavior: NestedScrollBehavior,
bottomOrRightBehavior: NestedScrollBehavior,
isExternalOverscrollGesture: () -> Boolean,
) {
- this.layoutImpl = layoutImpl
- this.orientation = orientation
+ this.draggableHandler = draggableHandler
this.topOrLeftBehavior = topOrLeftBehavior
this.bottomOrRightBehavior = bottomOrRightBehavior
this.isExternalOverscrollGesture = isExternalOverscrollGesture
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
index dbf7d7b..d3ddb50 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
@@ -637,8 +637,8 @@
fun interface UserActionDistance {
/**
- * Return the **absolute** distance of the user action given the size of the scene we are
- * animating from and the [orientation].
+ * Return the **absolute** distance of the user action when going from [fromContent] to
+ * [toContent] in the given [orientation].
*
* Note: This function will be called for each drag event until it returns a value > 0f. This
* for instance allows you to return 0f or a negative value until the first layout pass of a
@@ -646,7 +646,8 @@
* transitioning to when computing this absolute distance.
*/
fun UserActionDistanceScope.absoluteDistance(
- fromSceneSize: IntSize,
+ fromContent: ContentKey,
+ toContent: ContentKey,
orientation: Orientation,
): Float
}
@@ -656,7 +657,8 @@
/** The user action has a fixed [absoluteDistance]. */
class FixedDistance(private val distance: Dp) : UserActionDistance {
override fun UserActionDistanceScope.absoluteDistance(
- fromSceneSize: IntSize,
+ fromContent: ContentKey,
+ toContent: ContentKey,
orientation: Orientation,
): Float = distance.toPx()
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt
index e93cf8f7..b916b0b 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt
@@ -125,8 +125,8 @@
}
// TODO(b/317958526): Lazily allocate scene gesture handlers the first time they are needed.
- private val horizontalDraggableHandler: DraggableHandlerImpl
- private val verticalDraggableHandler: DraggableHandlerImpl
+ internal val horizontalDraggableHandler: DraggableHandlerImpl
+ internal val verticalDraggableHandler: DraggableHandlerImpl
internal val elementStateScope = ElementStateScopeImpl(this)
internal val propertyTransformationScope = PropertyTransformationScopeImpl(this)
@@ -163,12 +163,6 @@
state.checkThread()
}
- internal fun draggableHandler(orientation: Orientation): DraggableHandlerImpl =
- when (orientation) {
- Orientation.Vertical -> verticalDraggableHandler
- Orientation.Horizontal -> horizontalDraggableHandler
- }
-
internal fun scene(key: SceneKey): Scene {
return scenes[key] ?: error("Scene $key is not configured")
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
index 7eb5a3f..4bccac1 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
@@ -267,11 +267,12 @@
private set
/**
- * The flattened list of [SharedElementTransformation] within all the transitions in
+ * The flattened list of [SharedElementTransformation.Factory] within all the transitions in
* [transitionStates].
*/
- private val transformationsWithElevation: List<SharedElementTransformation> by derivedStateOf {
- transformationsWithElevation(transitionStates)
+ private val transformationFactoriesWithElevation:
+ List<SharedElementTransformation.Factory> by derivedStateOf {
+ transformationFactoriesWithElevation(transitionStates)
}
override val currentScene: SceneKey
@@ -353,8 +354,15 @@
}
override suspend fun startTransition(transition: TransitionState.Transition, chain: Boolean) {
+ Log.i(TAG, "startTransition(transition=$transition, chain=$chain)")
checkThread()
+ // Prepare the transition before starting it. This is outside of the try/finally block on
+ // purpose because preparing a transition might throw an exception (e.g. if we find multiple
+ // specs matching this transition), in which case we want to throw that exception here
+ // before even starting the transition.
+ prepareTransitionBeforeStarting(transition)
+
try {
// Start the transition.
startTransitionInternal(transition, chain)
@@ -366,7 +374,7 @@
}
}
- private fun startTransitionInternal(transition: TransitionState.Transition, chain: Boolean) {
+ private fun prepareTransitionBeforeStarting(transition: TransitionState.Transition) {
// Set the current scene and overlays on the transition.
val currentState = transitionState
transition.currentSceneWhenTransitionStarted = currentState.currentScene
@@ -394,7 +402,9 @@
} else {
transition.updateOverscrollSpecs(fromSpec = null, toSpec = null)
}
+ }
+ private fun startTransitionInternal(transition: TransitionState.Transition, chain: Boolean) {
when (val currentState = transitionStates.last()) {
is TransitionState.Idle -> {
// Replace [Idle] by [transition].
@@ -420,14 +430,6 @@
check(transitionStates.size == 1)
check(transitionStates[0] is TransitionState.Idle)
transitionStates = listOf(transition)
- } else if (currentState == transition.replacedTransition) {
- // Replace the transition.
- transitionStates =
- transitionStates.subList(0, transitionStates.lastIndex) + transition
-
- // Make sure it is removed from the finishedTransitions set if it was already
- // finished.
- finishedTransitions.remove(currentState)
} else {
// Append the new transition.
transitionStates = transitionStates + transition
@@ -477,6 +479,7 @@
return
}
+ Log.i(TAG, "finishTransition(transition=$transition)")
check(transitionStates.fastAll { it is TransitionState.Transition })
// Mark this transition as finished.
@@ -504,13 +507,10 @@
// If all transitions are finished, we are idle.
if (i == nStates) {
check(finishedTransitions.isEmpty())
- this.transitionStates =
- listOf(
- TransitionState.Idle(
- lastTransition.currentScene,
- lastTransition.currentOverlays,
- )
- )
+ val idle =
+ TransitionState.Idle(lastTransition.currentScene, lastTransition.currentOverlays)
+ Log.i(TAG, "all transitions finished. idle=$idle")
+ this.transitionStates = listOf(idle)
} else if (i > 0) {
this.transitionStates = transitionStates.subList(fromIndex = i, toIndex = nStates)
}
@@ -692,22 +692,23 @@
animate()
}
- private fun transformationsWithElevation(
+ private fun transformationFactoriesWithElevation(
transitionStates: List<TransitionState>
- ): List<SharedElementTransformation> {
+ ): List<SharedElementTransformation.Factory> {
return buildList {
transitionStates.fastForEach { state ->
if (state !is TransitionState.Transition) {
return@fastForEach
}
- state.transformationSpec.transformations.fastForEach { transformationWithRange ->
- val transformation = transformationWithRange.transformation
+ state.transformationSpec.transformationMatchers.fastForEach { transformationMatcher
+ ->
+ val factory = transformationMatcher.factory
if (
- transformation is SharedElementTransformation &&
- transformation.elevateInContent != null
+ factory is SharedElementTransformation.Factory &&
+ factory.elevateInContent != null
) {
- add(transformation)
+ add(factory)
}
}
}
@@ -722,10 +723,10 @@
* necessary, for performance.
*/
internal fun isElevationPossible(content: ContentKey, element: ElementKey?): Boolean {
- if (transformationsWithElevation.isEmpty()) return false
- return transformationsWithElevation.fastAny { transformation ->
- transformation.elevateInContent == content &&
- (element == null || transformation.matcher.matches(element, content))
+ if (transformationFactoriesWithElevation.isEmpty()) return false
+ return transformationFactoriesWithElevation.fastAny { factory ->
+ factory.elevateInContent == content &&
+ (element == null || factory.matcher.matches(element, content))
}
}
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitions.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitions.kt
index 569593c..8df3f2d 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitions.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitions.kt
@@ -26,17 +26,9 @@
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.util.fastForEach
import com.android.compose.animation.scene.content.state.TransitionState
-import com.android.compose.animation.scene.transformation.CustomAlphaTransformation
-import com.android.compose.animation.scene.transformation.CustomOffsetTransformation
-import com.android.compose.animation.scene.transformation.CustomScaleTransformation
-import com.android.compose.animation.scene.transformation.CustomSizeTransformation
-import com.android.compose.animation.scene.transformation.InterpolatedAlphaTransformation
-import com.android.compose.animation.scene.transformation.InterpolatedOffsetTransformation
-import com.android.compose.animation.scene.transformation.InterpolatedScaleTransformation
-import com.android.compose.animation.scene.transformation.InterpolatedSizeTransformation
import com.android.compose.animation.scene.transformation.PropertyTransformation
import com.android.compose.animation.scene.transformation.SharedElementTransformation
-import com.android.compose.animation.scene.transformation.Transformation
+import com.android.compose.animation.scene.transformation.TransformationMatcher
import com.android.compose.animation.scene.transformation.TransformationWithRange
/** The transitions configuration of a [SceneTransitionLayout]. */
@@ -169,7 +161,7 @@
}
/** The definition of a transition between [from] and [to]. */
-interface TransitionSpec {
+internal interface TransitionSpec {
/** The key of this [TransitionSpec]. */
val key: TransitionKey?
@@ -209,7 +201,7 @@
fun previewTransformationSpec(transition: TransitionState.Transition): TransformationSpec?
}
-interface TransformationSpec {
+internal interface TransformationSpec {
/**
* The [AnimationSpec] used to animate the associated transition progress from `0` to `1` when
* the transition is triggered (i.e. it is not gesture-based).
@@ -232,8 +224,8 @@
*/
val distance: UserActionDistance?
- /** The list of [Transformation] applied to elements during this transition. */
- val transformations: List<TransformationWithRange<*>>
+ /** The list of [TransformationMatcher] applied to elements during this transformation. */
+ val transformationMatchers: List<TransformationMatcher>
companion object {
internal val Empty =
@@ -241,7 +233,7 @@
progressSpec = snap(),
swipeSpec = null,
distance = null,
- transformations = emptyList(),
+ transformationMatchers = emptyList(),
)
internal val EmptyProvider = { _: TransitionState.Transition -> Empty }
}
@@ -272,7 +264,14 @@
progressSpec = reverse.progressSpec,
swipeSpec = reverse.swipeSpec,
distance = reverse.distance,
- transformations = reverse.transformations.map { it.reversed() },
+ transformationMatchers =
+ reverse.transformationMatchers.map {
+ TransformationMatcher(
+ matcher = it.matcher,
+ factory = it.factory,
+ range = it.range?.reversed(),
+ )
+ },
)
},
)
@@ -288,7 +287,7 @@
}
/** The definition of the overscroll behavior of the [content]. */
-interface OverscrollSpec {
+internal interface OverscrollSpec {
/** The scene we are over scrolling. */
val content: ContentKey
@@ -325,7 +324,7 @@
override val progressSpec: AnimationSpec<Float>,
override val swipeSpec: SpringSpec<Float>?,
override val distance: UserActionDistance?,
- override val transformations: List<TransformationWithRange<*>>,
+ override val transformationMatchers: List<TransformationMatcher>,
) : TransformationSpec {
private val cache = mutableMapOf<ElementKey, MutableMap<ContentKey, ElementTransformations>>()
@@ -335,7 +334,7 @@
.getOrPut(content) { computeTransformations(element, content) }
}
- /** Filter [transformations] to compute the [ElementTransformations] of [element]. */
+ /** Filter [transformationMatchers] to compute the [ElementTransformations] of [element]. */
private fun computeTransformations(
element: ElementKey,
content: ContentKey,
@@ -346,46 +345,55 @@
var drawScale: TransformationWithRange<PropertyTransformation<Scale>>? = null
var alpha: TransformationWithRange<PropertyTransformation<Float>>? = null
- transformations.fastForEach { transformationWithRange ->
- val transformation = transformationWithRange.transformation
- if (!transformation.matcher.matches(element, content)) {
+ transformationMatchers.fastForEach { transformationMatcher ->
+ if (!transformationMatcher.matcher.matches(element, content)) {
return@fastForEach
}
- when (transformation) {
- is SharedElementTransformation -> {
- throwIfNotNull(shared, element, name = "shared")
- shared =
- transformationWithRange
- as TransformationWithRange<SharedElementTransformation>
+ val transformation = transformationMatcher.factory.create()
+ val property =
+ when (transformation) {
+ is SharedElementTransformation -> {
+ throwIfNotNull(shared, element, name = "shared")
+ shared =
+ TransformationWithRange(transformation, transformationMatcher.range)
+ return@fastForEach
+ }
+ is PropertyTransformation<*> -> transformation.property
}
- is InterpolatedOffsetTransformation,
- is CustomOffsetTransformation -> {
+
+ when (property) {
+ is PropertyTransformation.Property.Offset -> {
throwIfNotNull(offset, element, name = "offset")
offset =
- transformationWithRange
- as TransformationWithRange<PropertyTransformation<Offset>>
+ TransformationWithRange(
+ transformation as PropertyTransformation<Offset>,
+ transformationMatcher.range,
+ )
}
- is InterpolatedSizeTransformation,
- is CustomSizeTransformation -> {
+ is PropertyTransformation.Property.Size -> {
throwIfNotNull(size, element, name = "size")
size =
- transformationWithRange
- as TransformationWithRange<PropertyTransformation<IntSize>>
+ TransformationWithRange(
+ transformation as PropertyTransformation<IntSize>,
+ transformationMatcher.range,
+ )
}
- is InterpolatedScaleTransformation,
- is CustomScaleTransformation -> {
+ is PropertyTransformation.Property.Scale -> {
throwIfNotNull(drawScale, element, name = "drawScale")
drawScale =
- transformationWithRange
- as TransformationWithRange<PropertyTransformation<Scale>>
+ TransformationWithRange(
+ transformation as PropertyTransformation<Scale>,
+ transformationMatcher.range,
+ )
}
- is InterpolatedAlphaTransformation,
- is CustomAlphaTransformation -> {
+ is PropertyTransformation.Property.Alpha -> {
throwIfNotNull(alpha, element, name = "alpha")
alpha =
- transformationWithRange
- as TransformationWithRange<PropertyTransformation<Float>>
+ TransformationWithRange(
+ transformation as PropertyTransformation<Float>,
+ transformationMatcher.range,
+ )
}
}
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeAnimation.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeAnimation.kt
index f0043e1..dbfeb5c 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeAnimation.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeAnimation.kt
@@ -24,7 +24,6 @@
import androidx.compose.runtime.mutableFloatStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
-import androidx.compose.ui.unit.IntSize
import com.android.compose.animation.scene.content.state.TransitionState
import com.android.compose.animation.scene.content.state.TransitionState.HasOverscrollProperties.Companion.DistanceUnspecified
import kotlin.math.absoluteValue
@@ -66,8 +65,9 @@
val absoluteDistance =
with(animation.contentTransition.transformationSpec.distance ?: DefaultSwipeDistance) {
layoutImpl.userActionDistanceScope.absoluteDistance(
- layoutImpl.content(animation.fromContent).targetSize,
- orientation,
+ fromContent = animation.fromContent,
+ toContent = animation.toContent,
+ orientation = orientation,
)
}
@@ -475,12 +475,14 @@
private object DefaultSwipeDistance : UserActionDistance {
override fun UserActionDistanceScope.absoluteDistance(
- fromSceneSize: IntSize,
+ fromContent: ContentKey,
+ toContent: ContentKey,
orientation: Orientation,
): Float {
+ val fromContentSize = checkNotNull(fromContent.targetSize())
return when (orientation) {
- Orientation.Horizontal -> fromSceneSize.width
- Orientation.Vertical -> fromSceneSize.height
+ Orientation.Horizontal -> fromContentSize.width
+ Orientation.Vertical -> fromContentSize.height
}.toFloat()
}
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt
index a448ee4..5ab306a 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt
@@ -165,8 +165,7 @@
private val nestedScrollHandlerImpl =
NestedScrollHandlerImpl(
- layoutImpl = draggableHandler.layoutImpl,
- orientation = draggableHandler.orientation,
+ draggableHandler = draggableHandler,
topOrLeftBehavior = NestedScrollBehavior.Default,
bottomOrRightBehavior = NestedScrollBehavior.Default,
isExternalOverscrollGesture = { false },
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt
index 1fdfca9..48f08a7 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt
@@ -26,7 +26,7 @@
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import com.android.compose.animation.scene.content.state.TransitionState
-import com.android.compose.animation.scene.transformation.CustomPropertyTransformation
+import com.android.compose.animation.scene.transformation.Transformation
import kotlin.math.tanh
/** Define the [transitions][SceneTransitions] to be used with a [SceneTransitionLayout]. */
@@ -76,7 +76,7 @@
preview: (TransitionBuilder.() -> Unit)? = null,
reversePreview: (TransitionBuilder.() -> Unit)? = null,
builder: TransitionBuilder.() -> Unit = {},
- ): TransitionSpec
+ )
/**
* Define the animation to be played when transitioning [from] the specified content. For the
@@ -102,7 +102,7 @@
preview: (TransitionBuilder.() -> Unit)? = null,
reversePreview: (TransitionBuilder.() -> Unit)? = null,
builder: TransitionBuilder.() -> Unit = {},
- ): TransitionSpec
+ )
/**
* Define the animation to be played when the [content] is overscrolled in the given
@@ -115,13 +115,13 @@
content: ContentKey,
orientation: Orientation,
builder: OverscrollBuilder.() -> Unit,
- ): OverscrollSpec
+ )
/**
* Prevents overscroll the [content] in the given [orientation], allowing ancestors to
* eventually consume the remaining gesture.
*/
- fun overscrollDisabled(content: ContentKey, orientation: Orientation): OverscrollSpec
+ fun overscrollDisabled(content: ContentKey, orientation: Orientation)
}
interface BaseTransitionBuilder : PropertyTransformationBuilder {
@@ -529,15 +529,8 @@
anchorHeight: Boolean = true,
)
- /**
- * Apply a [CustomPropertyTransformation] to one or more elements.
- *
- * @see com.android.compose.animation.scene.transformation.CustomSizeTransformation
- * @see com.android.compose.animation.scene.transformation.CustomOffsetTransformation
- * @see com.android.compose.animation.scene.transformation.CustomAlphaTransformation
- * @see com.android.compose.animation.scene.transformation.CustomScaleTransformation
- */
- fun transformation(transformation: CustomPropertyTransformation<*>)
+ /** Apply a [transformation] to the element(s) matching [matcher]. */
+ fun transformation(matcher: ElementMatcher, transformation: Transformation.Factory)
}
/** This converter lets you change a linear progress into a function of your choice. */
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDslImpl.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDslImpl.kt
index 79f8cd4..6742b32 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDslImpl.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDslImpl.kt
@@ -30,7 +30,6 @@
import com.android.compose.animation.scene.content.state.TransitionState
import com.android.compose.animation.scene.transformation.AnchoredSize
import com.android.compose.animation.scene.transformation.AnchoredTranslate
-import com.android.compose.animation.scene.transformation.CustomPropertyTransformation
import com.android.compose.animation.scene.transformation.DrawScale
import com.android.compose.animation.scene.transformation.EdgeTranslate
import com.android.compose.animation.scene.transformation.Fade
@@ -38,8 +37,8 @@
import com.android.compose.animation.scene.transformation.ScaleSize
import com.android.compose.animation.scene.transformation.SharedElementTransformation
import com.android.compose.animation.scene.transformation.Transformation
+import com.android.compose.animation.scene.transformation.TransformationMatcher
import com.android.compose.animation.scene.transformation.TransformationRange
-import com.android.compose.animation.scene.transformation.TransformationWithRange
import com.android.compose.animation.scene.transformation.Translate
internal fun transitionsImpl(builder: SceneTransitionsBuilder.() -> Unit): SceneTransitions {
@@ -67,8 +66,8 @@
preview: (TransitionBuilder.() -> Unit)?,
reversePreview: (TransitionBuilder.() -> Unit)?,
builder: TransitionBuilder.() -> Unit,
- ): TransitionSpec {
- return transition(from = null, to = to, key = key, preview, reversePreview, builder)
+ ) {
+ transition(from = null, to = to, key = key, preview, reversePreview, builder)
}
override fun from(
@@ -78,25 +77,25 @@
preview: (TransitionBuilder.() -> Unit)?,
reversePreview: (TransitionBuilder.() -> Unit)?,
builder: TransitionBuilder.() -> Unit,
- ): TransitionSpec {
- return transition(from = from, to = to, key = key, preview, reversePreview, builder)
+ ) {
+ transition(from = from, to = to, key = key, preview, reversePreview, builder)
}
override fun overscroll(
content: ContentKey,
orientation: Orientation,
builder: OverscrollBuilder.() -> Unit,
- ): OverscrollSpec {
+ ) {
val impl = OverscrollBuilderImpl().apply(builder)
- check(impl.transformations.isNotEmpty()) {
+ check(impl.transformationMatchers.isNotEmpty()) {
"This method does not allow empty transformations. " +
"Use overscrollDisabled($content, $orientation) instead."
}
- return overscrollSpec(content, orientation, impl)
+ overscrollSpec(content, orientation, impl)
}
- override fun overscrollDisabled(content: ContentKey, orientation: Orientation): OverscrollSpec {
- return overscrollSpec(content, orientation, OverscrollBuilderImpl())
+ override fun overscrollDisabled(content: ContentKey, orientation: Orientation) {
+ overscrollSpec(content, orientation, OverscrollBuilderImpl())
}
private fun overscrollSpec(
@@ -113,7 +112,7 @@
progressSpec = snap(),
swipeSpec = null,
distance = impl.distance,
- transformations = impl.transformations,
+ transformationMatchers = impl.transformationMatchers,
),
progressConverter = impl.progressConverter,
)
@@ -138,7 +137,7 @@
progressSpec = impl.spec,
swipeSpec = impl.swipeSpec,
distance = impl.distance,
- transformations = impl.transformations,
+ transformationMatchers = impl.transformationMatchers,
)
}
@@ -158,7 +157,7 @@
}
internal abstract class BaseTransitionBuilderImpl : BaseTransitionBuilder {
- val transformations = mutableListOf<TransformationWithRange<*>>()
+ val transformationMatchers = mutableListOf<TransformationMatcher>()
private var range: TransformationRange? = null
protected var reversed = false
override var distance: UserActionDistance? = null
@@ -174,23 +173,31 @@
range = null
}
- protected fun addTransformation(transformation: Transformation) {
- val transformationWithRange = TransformationWithRange(transformation, range)
- transformations.add(
- if (reversed) {
- transformationWithRange.reversed()
- } else {
- transformationWithRange
- }
+ protected fun addTransformation(
+ matcher: ElementMatcher,
+ transformation: Transformation.Factory,
+ ) {
+ transformationMatchers.add(
+ TransformationMatcher(
+ matcher,
+ transformation,
+ range?.let { range ->
+ if (reversed) {
+ range.reversed()
+ } else {
+ range
+ }
+ },
+ )
)
}
override fun fade(matcher: ElementMatcher) {
- addTransformation(Fade(matcher))
+ addTransformation(matcher, Fade.Factory)
}
override fun translate(matcher: ElementMatcher, x: Dp, y: Dp) {
- addTransformation(Translate(matcher, x, y))
+ addTransformation(matcher, Translate.Factory(x, y))
}
override fun translate(
@@ -198,19 +205,19 @@
edge: Edge,
startsOutsideLayoutBounds: Boolean,
) {
- addTransformation(EdgeTranslate(matcher, edge, startsOutsideLayoutBounds))
+ addTransformation(matcher, EdgeTranslate.Factory(edge, startsOutsideLayoutBounds))
}
override fun anchoredTranslate(matcher: ElementMatcher, anchor: ElementKey) {
- addTransformation(AnchoredTranslate(matcher, anchor))
+ addTransformation(matcher, AnchoredTranslate.Factory(anchor))
}
override fun scaleSize(matcher: ElementMatcher, width: Float, height: Float) {
- addTransformation(ScaleSize(matcher, width, height))
+ addTransformation(matcher, ScaleSize.Factory(width, height))
}
override fun scaleDraw(matcher: ElementMatcher, scaleX: Float, scaleY: Float, pivot: Offset) {
- addTransformation(DrawScale(matcher, scaleX, scaleY, pivot))
+ addTransformation(matcher, DrawScale.Factory(scaleX, scaleY, pivot))
}
override fun anchoredSize(
@@ -219,12 +226,12 @@
anchorWidth: Boolean,
anchorHeight: Boolean,
) {
- addTransformation(AnchoredSize(matcher, anchor, anchorWidth, anchorHeight))
+ addTransformation(matcher, AnchoredSize.Factory(anchor, anchorWidth, anchorHeight))
}
- override fun transformation(transformation: CustomPropertyTransformation<*>) {
+ override fun transformation(matcher: ElementMatcher, transformation: Transformation.Factory) {
check(range == null) { "Custom transformations can not be applied inside a range" }
- addTransformation(transformation)
+ addTransformation(matcher, transformation)
}
}
@@ -263,7 +270,10 @@
"(${transition.toContent.debugName})"
}
- addTransformation(SharedElementTransformation(matcher, enabled, elevateInContent))
+ addTransformation(
+ matcher,
+ SharedElementTransformation.Factory(matcher, enabled, elevateInContent),
+ )
}
override fun timestampRange(
@@ -294,6 +304,6 @@
x: OverscrollScope.() -> Float,
y: OverscrollScope.() -> Float,
) {
- addTransformation(OverscrollTranslate(matcher, x, y))
+ addTransformation(matcher, OverscrollTranslate.Factory(x, y))
}
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/Content.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/Content.kt
index 8187e39..255a16c 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/Content.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/Content.kt
@@ -16,7 +16,7 @@
package com.android.compose.animation.scene.content
-import androidx.compose.foundation.gestures.Orientation
+import android.annotation.SuppressLint
import androidx.compose.foundation.layout.Box
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
@@ -72,6 +72,7 @@
var targetSize by mutableStateOf(IntSize.Zero)
var userActions by mutableStateOf(actions)
+ @SuppressLint("NotConstructor")
@Composable
fun Content(modifier: Modifier = Modifier) {
Box(
@@ -151,8 +152,7 @@
isExternalOverscrollGesture: () -> Boolean,
): Modifier {
return nestedScrollToScene(
- layoutImpl = layoutImpl,
- orientation = Orientation.Horizontal,
+ draggableHandler = layoutImpl.horizontalDraggableHandler,
topOrLeftBehavior = leftBehavior,
bottomOrRightBehavior = rightBehavior,
isExternalOverscrollGesture = isExternalOverscrollGesture,
@@ -165,8 +165,7 @@
isExternalOverscrollGesture: () -> Boolean,
): Modifier {
return nestedScrollToScene(
- layoutImpl = layoutImpl,
- orientation = Orientation.Vertical,
+ draggableHandler = layoutImpl.verticalDraggableHandler,
topOrLeftBehavior = topBehavior,
bottomOrRightBehavior = bottomBehavior,
isExternalOverscrollGesture = isExternalOverscrollGesture,
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/state/TransitionState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/state/TransitionState.kt
index 38b1aaa..d66fe42 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/state/TransitionState.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/state/TransitionState.kt
@@ -90,6 +90,10 @@
// The set of overlays does not change in a [ChangeCurrentScene] transition.
return currentOverlaysWhenTransitionStarted
}
+
+ override fun toString(): String {
+ return "ChangeScene(fromScene=$fromScene, toScene=$toScene)"
+ }
}
/**
@@ -146,6 +150,12 @@
currentOverlaysWhenTransitionStarted - overlay
}
}
+
+ override fun toString(): String {
+ val isShowing = overlay == toContent
+ return "ShowOrHideOverlay(overlay=$overlay, fromOrToScene=$fromOrToScene, " +
+ "isShowing=$isShowing)"
+ }
}
/** We are transitioning from [fromOverlay] to [toOverlay]. */
@@ -194,6 +204,10 @@
add(include)
}
}
+
+ override fun toString(): String {
+ return "ReplaceOverlay(fromOverlay=$fromOverlay, toOverlay=$toOverlay)"
+ }
}
/**
@@ -401,7 +415,7 @@
else -> null
} ?: return true
- return specForCurrentScene.transformationSpec.transformations.isNotEmpty()
+ return specForCurrentScene.transformationSpec.transformationMatchers.isNotEmpty()
}
internal open fun interruptionProgress(layoutImpl: SceneTransitionLayoutImpl): Float {
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/reveal/ContainerReveal.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/reveal/ContainerReveal.kt
new file mode 100644
index 0000000..bfb5ca7
--- /dev/null
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/reveal/ContainerReveal.kt
@@ -0,0 +1,289 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.compose.animation.scene.reveal
+
+import androidx.compose.animation.core.AnimationVector1D
+import androidx.compose.animation.core.DeferredTargetAnimation
+import androidx.compose.animation.core.ExperimentalAnimatableApi
+import androidx.compose.animation.core.FiniteAnimationSpec
+import androidx.compose.animation.core.Spring
+import androidx.compose.animation.core.VectorConverter
+import androidx.compose.animation.core.spring
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.IntSize
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.util.fastCoerceAtLeast
+import androidx.compose.ui.util.fastCoerceAtMost
+import com.android.compose.animation.scene.ContentKey
+import com.android.compose.animation.scene.ElementKey
+import com.android.compose.animation.scene.OverlayKey
+import com.android.compose.animation.scene.SceneKey
+import com.android.compose.animation.scene.TransitionBuilder
+import com.android.compose.animation.scene.UserActionDistance
+import com.android.compose.animation.scene.content.state.TransitionState
+import com.android.compose.animation.scene.transformation.CustomPropertyTransformation
+import com.android.compose.animation.scene.transformation.PropertyTransformation
+import com.android.compose.animation.scene.transformation.PropertyTransformationScope
+import kotlin.math.roundToInt
+import kotlinx.coroutines.CoroutineScope
+
+interface ContainerRevealHaptics {
+ /**
+ * Called when the reveal threshold is crossed while the user was dragging on screen.
+ *
+ * Important: This callback is called during layout and its implementation should therefore be
+ * very fast or posted to a different thread.
+ *
+ * @param revealed whether we go from hidden to revealed, i.e. whether the container size is
+ * going to jump from a smaller size to a bigger size.
+ */
+ fun onRevealThresholdCrossed(revealed: Boolean)
+}
+
+/** Animate the reveal of [container] by animating its size. */
+fun TransitionBuilder.verticalContainerReveal(
+ container: ElementKey,
+ haptics: ContainerRevealHaptics,
+) {
+ // Make the swipe distance be exactly the target height of the container.
+ // TODO(b/376438969): Make sure that this works correctly when the target size of the element
+ // is changing during the transition (e.g. a notification was added). At the moment, the user
+ // action distance is only called until it returns a value > 0f, which is then cached.
+ distance = UserActionDistance { fromContent, toContent, _ ->
+ val targetSizeInFromContent = container.targetSize(fromContent)
+ val targetSizeInToContent = container.targetSize(toContent)
+ if (targetSizeInFromContent != null && targetSizeInToContent != null) {
+ error(
+ "verticalContainerReveal should not be used with shared elements, but " +
+ "${container.debugName} is in both ${fromContent.debugName} and " +
+ toContent.debugName
+ )
+ }
+
+ (targetSizeInToContent?.height ?: targetSizeInFromContent?.height)?.toFloat() ?: 0f
+ }
+
+ // TODO(b/376438969): Improve the motion of this gesture using Motion Mechanics.
+
+ // The min distance to swipe before triggering the reveal spring.
+ val distanceThreshold = 80.dp
+
+ // The minimum height of the container.
+ val minHeight = 10.dp
+
+ // The amount removed from the container width at 0% progress.
+ val widthDelta = 140.dp
+
+ // The ratio at which the distance is tracked before reaching the threshold, e.g. if the user
+ // drags 60dp then the height will be 60dp * 0.25f = 15dp.
+ val trackingRatio = 0.25f
+
+ // The max progress starting from which the container should always be visible, even if we are
+ // animating the container out. This is used so that we don't immediately fade out the container
+ // when triggering a one-off animation that hides it.
+ val alphaProgressThreshold = 0.05f
+
+ // The spring animating the size of the container.
+ val sizeSpec = spring<Float>(stiffness = 380f, dampingRatio = 0.9f)
+
+ // The spring animating the alpha of the container.
+ val alphaSpec = spring<Float>(stiffness = 1200f, dampingRatio = 0.99f)
+
+ // The spring animating the progress when releasing the finger.
+ swipeSpec =
+ spring(
+ stiffness = Spring.StiffnessMediumLow,
+ dampingRatio = Spring.DampingRatioNoBouncy,
+ visibilityThreshold = 0.5f,
+ )
+
+ // Size transformation.
+ transformation(container) {
+ VerticalContainerRevealSizeTransformation(
+ haptics,
+ distanceThreshold,
+ trackingRatio,
+ minHeight,
+ widthDelta,
+ sizeSpec,
+ )
+ }
+
+ // Alpha transformation.
+ transformation(container) {
+ ContainerRevealAlphaTransformation(alphaSpec, alphaProgressThreshold)
+ }
+}
+
+@OptIn(ExperimentalAnimatableApi::class)
+private class VerticalContainerRevealSizeTransformation(
+ private val haptics: ContainerRevealHaptics,
+ private val distanceThreshold: Dp,
+ private val trackingRatio: Float,
+ private val minHeight: Dp,
+ private val widthDelta: Dp,
+ private val spec: FiniteAnimationSpec<Float>,
+) : CustomPropertyTransformation<IntSize> {
+ override val property = PropertyTransformation.Property.Size
+
+ private val widthAnimation = DeferredTargetAnimation(Float.VectorConverter)
+ private val heightAnimation = DeferredTargetAnimation(Float.VectorConverter)
+
+ private var previousHasReachedThreshold: Boolean? = null
+
+ override fun PropertyTransformationScope.transform(
+ content: ContentKey,
+ element: ElementKey,
+ transition: TransitionState.Transition,
+ transitionScope: CoroutineScope,
+ ): IntSize {
+ // The distance to go to 100%. Note that we don't use
+ // TransitionState.HasOverscrollProperties.absoluteDistance because the transition will not
+ // implement HasOverscrollProperties if the transition is triggered and not gesture based.
+ val idleSize = checkNotNull(element.targetSize(content))
+ val userActionDistance = idleSize.height
+ val progress =
+ when ((transition as? TransitionState.HasOverscrollProperties)?.bouncingContent) {
+ null -> transition.progressTo(content)
+ content -> 1f
+ else -> 0f
+ }
+ val distance = (progress * userActionDistance).fastCoerceAtLeast(0f)
+ val threshold = distanceThreshold.toPx()
+
+ // Width.
+ val widthDelta = widthDelta.toPx()
+ val width =
+ (idleSize.width - widthDelta +
+ animateSize(
+ size = widthDelta,
+ distance = distance,
+ threshold = threshold,
+ transitionScope = transitionScope,
+ animation = widthAnimation,
+ ))
+ .roundToInt()
+
+ // Height.
+ val minHeight = minHeight.toPx()
+ val height =
+ (
+ // 1) The minimum size of the container.
+ minHeight +
+
+ // 2) The animated size between the minimum size and the threshold.
+ animateSize(
+ size = threshold - minHeight,
+ distance = distance,
+ threshold = threshold,
+ transitionScope = transitionScope,
+ animation = heightAnimation,
+ ) +
+
+ // 3) The remaining height after the threshold, tracking the finger.
+ (distance - threshold).fastCoerceAtLeast(0f))
+ .roundToInt()
+ .fastCoerceAtMost(idleSize.height)
+
+ // Haptics.
+ val hasReachedThreshold = distance >= threshold
+ if (
+ previousHasReachedThreshold != null &&
+ hasReachedThreshold != previousHasReachedThreshold &&
+ transition.isUserInputOngoing
+ ) {
+ haptics.onRevealThresholdCrossed(revealed = hasReachedThreshold)
+ }
+ previousHasReachedThreshold = hasReachedThreshold
+
+ return IntSize(width = width, height = height)
+ }
+
+ /**
+ * Animate a size up to [size], so that it is equal to 0f when distance is 0f and equal to
+ * [size] when `distance >= threshold`, taking the [trackingRatio] into account.
+ */
+ @OptIn(ExperimentalAnimatableApi::class)
+ private fun animateSize(
+ size: Float,
+ distance: Float,
+ threshold: Float,
+ transitionScope: CoroutineScope,
+ animation: DeferredTargetAnimation<Float, AnimationVector1D>,
+ ): Float {
+ val trackingSize = distance.fastCoerceAtMost(threshold) / threshold * size * trackingRatio
+ val springTarget =
+ if (distance >= threshold) {
+ size * (1f - trackingRatio)
+ } else {
+ 0f
+ }
+ val springSize = animation.updateTarget(springTarget, transitionScope, spec)
+ return trackingSize + springSize
+ }
+}
+
+@OptIn(ExperimentalAnimatableApi::class)
+private class ContainerRevealAlphaTransformation(
+ private val spec: FiniteAnimationSpec<Float>,
+ private val progressThreshold: Float,
+) : CustomPropertyTransformation<Float> {
+ override val property = PropertyTransformation.Property.Alpha
+ private val alphaAnimation = DeferredTargetAnimation(Float.VectorConverter)
+
+ override fun PropertyTransformationScope.transform(
+ content: ContentKey,
+ element: ElementKey,
+ transition: TransitionState.Transition,
+ transitionScope: CoroutineScope,
+ ): Float {
+ return alphaAnimation.updateTarget(targetAlpha(transition, content), transitionScope, spec)
+ }
+
+ private fun targetAlpha(transition: TransitionState.Transition, content: ContentKey): Float {
+ if (transition.isUserInputOngoing) {
+ if (transition !is TransitionState.HasOverscrollProperties) {
+ error(
+ "Unsupported transition driven by user input but that does not have " +
+ "overscroll properties: $transition"
+ )
+ }
+
+ val bouncingContent = transition.bouncingContent
+ return if (bouncingContent != null) {
+ if (bouncingContent == content) 1f else 0f
+ } else {
+ if (transition.progressTo(content) > 0f) 1f else 0f
+ }
+ }
+
+ // The transition was committed (the user released their finger), so the alpha depends on
+ // whether we are animating towards the content (showing the container) or away from it
+ // (hiding the container).
+ val isShowingContainer =
+ when (content) {
+ is SceneKey -> transition.currentScene == content
+ is OverlayKey -> transition.currentOverlays.contains(content)
+ }
+
+ return if (isShowingContainer || transition.progressTo(content) >= progressThreshold) {
+ 1f
+ } else {
+ 0f
+ }
+ }
+}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredSize.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredSize.kt
index 85bb533..6575068 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredSize.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredSize.kt
@@ -19,16 +19,17 @@
import androidx.compose.ui.unit.IntSize
import com.android.compose.animation.scene.ContentKey
import com.android.compose.animation.scene.ElementKey
-import com.android.compose.animation.scene.ElementMatcher
import com.android.compose.animation.scene.content.state.TransitionState
/** Anchor the size of an element to the size of another element. */
-internal class AnchoredSize(
- override val matcher: ElementMatcher,
+internal class AnchoredSize
+private constructor(
private val anchor: ElementKey,
private val anchorWidth: Boolean,
private val anchorHeight: Boolean,
-) : InterpolatedSizeTransformation {
+) : InterpolatedPropertyTransformation<IntSize> {
+ override val property = PropertyTransformation.Property.Size
+
override fun PropertyTransformationScope.transform(
content: ContentKey,
element: ElementKey,
@@ -59,4 +60,12 @@
anchorSizeIn(transition.fromContent)
}
}
+
+ class Factory(
+ private val anchor: ElementKey,
+ private val anchorWidth: Boolean,
+ private val anchorHeight: Boolean,
+ ) : Transformation.Factory {
+ override fun create(): Transformation = AnchoredSize(anchor, anchorWidth, anchorHeight)
+ }
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredTranslate.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredTranslate.kt
index 04cd683..890902b 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredTranslate.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredTranslate.kt
@@ -19,14 +19,13 @@
import androidx.compose.ui.geometry.Offset
import com.android.compose.animation.scene.ContentKey
import com.android.compose.animation.scene.ElementKey
-import com.android.compose.animation.scene.ElementMatcher
import com.android.compose.animation.scene.content.state.TransitionState
/** Anchor the translation of an element to another element. */
-internal class AnchoredTranslate(
- override val matcher: ElementMatcher,
- private val anchor: ElementKey,
-) : InterpolatedOffsetTransformation {
+internal class AnchoredTranslate private constructor(private val anchor: ElementKey) :
+ InterpolatedPropertyTransformation<Offset> {
+ override val property = PropertyTransformation.Property.Offset
+
override fun PropertyTransformationScope.transform(
content: ContentKey,
element: ElementKey,
@@ -56,6 +55,10 @@
Offset(idleValue.x + offset.x, idleValue.y + offset.y)
}
}
+
+ class Factory(private val anchor: ElementKey) : Transformation.Factory {
+ override fun create(): Transformation = AnchoredTranslate(anchor)
+ }
}
internal fun throwMissingAnchorException(
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/DrawScale.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/DrawScale.kt
index 45d6d40..347f1c3 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/DrawScale.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/DrawScale.kt
@@ -19,7 +19,6 @@
import androidx.compose.ui.geometry.Offset
import com.android.compose.animation.scene.ContentKey
import com.android.compose.animation.scene.ElementKey
-import com.android.compose.animation.scene.ElementMatcher
import com.android.compose.animation.scene.Scale
import com.android.compose.animation.scene.content.state.TransitionState
@@ -27,12 +26,14 @@
* Scales the draw size of an element. Note this will only scale the draw inside of an element,
* therefore it won't impact layout of elements around it.
*/
-internal class DrawScale(
- override val matcher: ElementMatcher,
+internal class DrawScale
+private constructor(
private val scaleX: Float,
private val scaleY: Float,
- private val pivot: Offset = Offset.Unspecified,
-) : InterpolatedScaleTransformation {
+ private val pivot: Offset,
+) : InterpolatedPropertyTransformation<Scale> {
+ override val property = PropertyTransformation.Property.Scale
+
override fun PropertyTransformationScope.transform(
content: ContentKey,
element: ElementKey,
@@ -41,4 +42,9 @@
): Scale {
return Scale(scaleX, scaleY, pivot)
}
+
+ class Factory(private val scaleX: Float, private val scaleY: Float, private val pivot: Offset) :
+ Transformation.Factory {
+ override fun create(): Transformation = DrawScale(scaleX, scaleY, pivot)
+ }
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/EdgeTranslate.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/EdgeTranslate.kt
index 21d66d7..f8e6dc0 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/EdgeTranslate.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/EdgeTranslate.kt
@@ -20,15 +20,14 @@
import com.android.compose.animation.scene.ContentKey
import com.android.compose.animation.scene.Edge
import com.android.compose.animation.scene.ElementKey
-import com.android.compose.animation.scene.ElementMatcher
import com.android.compose.animation.scene.content.state.TransitionState
/** Translate an element from an edge of the layout. */
-internal class EdgeTranslate(
- override val matcher: ElementMatcher,
- private val edge: Edge,
- private val startsOutsideLayoutBounds: Boolean = true,
-) : InterpolatedOffsetTransformation {
+internal class EdgeTranslate
+private constructor(private val edge: Edge, private val startsOutsideLayoutBounds: Boolean) :
+ InterpolatedPropertyTransformation<Offset> {
+ override val property = PropertyTransformation.Property.Offset
+
override fun PropertyTransformationScope.transform(
content: ContentKey,
element: ElementKey,
@@ -67,4 +66,9 @@
}
}
}
+
+ class Factory(private val edge: Edge, private val startsOutsideLayoutBounds: Boolean) :
+ Transformation.Factory {
+ override fun create(): Transformation = EdgeTranslate(edge, startsOutsideLayoutBounds)
+ }
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Fade.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Fade.kt
index d942273..d92419e 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Fade.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Fade.kt
@@ -18,11 +18,12 @@
import com.android.compose.animation.scene.ContentKey
import com.android.compose.animation.scene.ElementKey
-import com.android.compose.animation.scene.ElementMatcher
import com.android.compose.animation.scene.content.state.TransitionState
/** Fade an element in or out. */
-internal class Fade(override val matcher: ElementMatcher) : InterpolatedAlphaTransformation {
+internal object Fade : InterpolatedPropertyTransformation<Float> {
+ override val property = PropertyTransformation.Property.Alpha
+
override fun PropertyTransformationScope.transform(
content: ContentKey,
element: ElementKey,
@@ -33,4 +34,8 @@
// fading out, which is `0` in both cases.
return 0f
}
+
+ object Factory : Transformation.Factory {
+ override fun create(): Transformation = Fade
+ }
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/ScaleSize.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/ScaleSize.kt
index 5f3cdab..5d31fd9 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/ScaleSize.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/ScaleSize.kt
@@ -19,7 +19,6 @@
import androidx.compose.ui.unit.IntSize
import com.android.compose.animation.scene.ContentKey
import com.android.compose.animation.scene.ElementKey
-import com.android.compose.animation.scene.ElementMatcher
import com.android.compose.animation.scene.content.state.TransitionState
import kotlin.math.roundToInt
@@ -27,11 +26,10 @@
* Scales the size of an element. Note that this makes the element resize every frame and will
* therefore impact the layout of other elements.
*/
-internal class ScaleSize(
- override val matcher: ElementMatcher,
- private val width: Float = 1f,
- private val height: Float = 1f,
-) : InterpolatedSizeTransformation {
+internal class ScaleSize private constructor(private val width: Float, private val height: Float) :
+ InterpolatedPropertyTransformation<IntSize> {
+ override val property = PropertyTransformation.Property.Size
+
override fun PropertyTransformationScope.transform(
content: ContentKey,
element: ElementKey,
@@ -43,4 +41,9 @@
height = (idleValue.height * height).roundToInt(),
)
}
+
+ class Factory(private val width: Float = 1f, private val height: Float = 1f) :
+ Transformation.Factory {
+ override fun create(): Transformation = ScaleSize(width, height)
+ }
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Transformation.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Transformation.kt
index d5143d7..e0b4218 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Transformation.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Transformation.kt
@@ -35,26 +35,59 @@
/** A transformation applied to one or more elements during a transition. */
sealed interface Transformation {
- /**
- * The matcher that should match the element(s) to which this transformation should be applied.
- */
- val matcher: ElementMatcher
+ fun interface Factory {
+ fun create(): Transformation
+ }
}
-internal class SharedElementTransformation(
- override val matcher: ElementMatcher,
+// Important: SharedElementTransformation must be a data class because we check that we don't
+// provide 2 different transformations for the same element in Element.kt
+internal data class SharedElementTransformation(
internal val enabled: Boolean,
internal val elevateInContent: ContentKey?,
-) : Transformation
+) : Transformation {
+ class Factory(
+ internal val matcher: ElementMatcher,
+ internal val enabled: Boolean,
+ internal val elevateInContent: ContentKey?,
+ ) : Transformation.Factory {
+ override fun create(): Transformation {
+ return SharedElementTransformation(enabled, elevateInContent)
+ }
+ }
+}
-/** A transformation that changes the value of an element property, like its size or offset. */
-sealed interface PropertyTransformation<T> : Transformation
+/**
+ * A transformation that changes the value of an element [Property], like its [size][Property.Size]
+ * or [offset][Property.Offset].
+ */
+sealed interface PropertyTransformation<T> : Transformation {
+ /** The property to which this transformation is applied. */
+ val property: Property<T>
+
+ sealed class Property<T> {
+ /** The size of an element. */
+ data object Size : Property<IntSize>()
+
+ /** The offset (position) of an element. */
+ data object Offset : Property<androidx.compose.ui.geometry.Offset>()
+
+ /** The alpha of an element. */
+ data object Alpha : Property<Float>()
+
+ /**
+ * The drawing scale of an element. Animating the scale does not have any effect on the
+ * layout.
+ */
+ data object Scale : Property<com.android.compose.animation.scene.Scale>()
+ }
+}
/**
* A transformation to a target/transformed value that is automatically interpolated using the
* transition progress and transformation range.
*/
-sealed interface InterpolatedPropertyTransformation<T> : PropertyTransformation<T> {
+interface InterpolatedPropertyTransformation<T> : PropertyTransformation<T> {
/**
* Return the transformed value for the given property, i.e.:
* - the value at progress = 0% for elements that are entering the layout (i.e. elements in the
@@ -73,19 +106,7 @@
): T
}
-/** An [InterpolatedPropertyTransformation] applied to the size of one or more elements. */
-interface InterpolatedSizeTransformation : InterpolatedPropertyTransformation<IntSize>
-
-/** An [InterpolatedPropertyTransformation] applied to the offset of one or more elements. */
-interface InterpolatedOffsetTransformation : InterpolatedPropertyTransformation<Offset>
-
-/** An [InterpolatedPropertyTransformation] applied to the alpha of one or more elements. */
-interface InterpolatedAlphaTransformation : InterpolatedPropertyTransformation<Float>
-
-/** An [InterpolatedPropertyTransformation] applied to the scale of one or more elements. */
-interface InterpolatedScaleTransformation : InterpolatedPropertyTransformation<Scale>
-
-sealed interface CustomPropertyTransformation<T> : PropertyTransformation<T> {
+interface CustomPropertyTransformation<T> : PropertyTransformation<T> {
/**
* Return the value that the property should have in the current frame for the given [content]
* and [element].
@@ -105,25 +126,20 @@
): T
}
-/** A [CustomPropertyTransformation] applied to the size of one or more elements. */
-interface CustomSizeTransformation : CustomPropertyTransformation<IntSize>
-
-/** A [CustomPropertyTransformation] applied to the offset of one or more elements. */
-interface CustomOffsetTransformation : CustomPropertyTransformation<Offset>
-
-/** A [CustomPropertyTransformation] applied to the alpha of one or more elements. */
-interface CustomAlphaTransformation : CustomPropertyTransformation<Float>
-
-/** A [CustomPropertyTransformation] applied to the scale of one or more elements. */
-interface CustomScaleTransformation : CustomPropertyTransformation<Scale>
-
interface PropertyTransformationScope : Density, ElementStateScope {
/** The current [direction][LayoutDirection] of the layout. */
val layoutDirection: LayoutDirection
}
+/** Defines the transformation-type to be applied to all elements matching [matcher]. */
+internal class TransformationMatcher(
+ val matcher: ElementMatcher,
+ val factory: Transformation.Factory,
+ val range: TransformationRange?,
+)
+
/** A pair consisting of a [transformation] and optional [range]. */
-class TransformationWithRange<out T : Transformation>(
+internal data class TransformationWithRange<out T : Transformation>(
val transformation: T,
val range: TransformationRange?,
) {
@@ -135,7 +151,7 @@
}
/** The progress-based range of a [PropertyTransformation]. */
-data class TransformationRange(val start: Float, val end: Float, val easing: Easing) {
+internal data class TransformationRange(val start: Float, val end: Float, val easing: Easing) {
constructor(
start: Float? = null,
end: Float? = null,
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Translate.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Translate.kt
index d756c86..2f4d5bff 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Translate.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Translate.kt
@@ -19,18 +19,15 @@
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.Dp
-import androidx.compose.ui.unit.dp
import com.android.compose.animation.scene.ContentKey
import com.android.compose.animation.scene.ElementKey
-import com.android.compose.animation.scene.ElementMatcher
import com.android.compose.animation.scene.OverscrollScope
import com.android.compose.animation.scene.content.state.TransitionState
-internal class Translate(
- override val matcher: ElementMatcher,
- private val x: Dp = 0.dp,
- private val y: Dp = 0.dp,
-) : InterpolatedOffsetTransformation {
+internal class Translate private constructor(private val x: Dp, private val y: Dp) :
+ InterpolatedPropertyTransformation<Offset> {
+ override val property = PropertyTransformation.Property.Offset
+
override fun PropertyTransformationScope.transform(
content: ContentKey,
element: ElementKey,
@@ -39,13 +36,19 @@
): Offset {
return Offset(idleValue.x + x.toPx(), idleValue.y + y.toPx())
}
+
+ class Factory(private val x: Dp, private val y: Dp) : Transformation.Factory {
+ override fun create(): Transformation = Translate(x, y)
+ }
}
-internal class OverscrollTranslate(
- override val matcher: ElementMatcher,
- val x: OverscrollScope.() -> Float = { 0f },
- val y: OverscrollScope.() -> Float = { 0f },
-) : InterpolatedOffsetTransformation {
+internal class OverscrollTranslate
+private constructor(
+ private val x: OverscrollScope.() -> Float,
+ private val y: OverscrollScope.() -> Float,
+) : InterpolatedPropertyTransformation<Offset> {
+ override val property = PropertyTransformation.Property.Offset
+
private val cachedOverscrollScope = CachedOverscrollScope()
override fun PropertyTransformationScope.transform(
@@ -63,6 +66,13 @@
return Offset(x = value.x + overscrollScope.x(), y = value.y + overscrollScope.y())
}
+
+ class Factory(
+ private val x: OverscrollScope.() -> Float,
+ private val y: OverscrollScope.() -> Float,
+ ) : Transformation.Factory {
+ override fun create(): Transformation = OverscrollTranslate(x, y)
+ }
}
/**
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt
index 7e6f3a8..10057b2 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt
@@ -133,8 +133,8 @@
)
.apply { setContentsAndLayoutTargetSizeForTest(LAYOUT_SIZE) }
- val draggableHandler = layoutImpl.draggableHandler(Orientation.Vertical)
- val horizontalDraggableHandler = layoutImpl.draggableHandler(Orientation.Horizontal)
+ val draggableHandler = layoutImpl.verticalDraggableHandler
+ val horizontalDraggableHandler = layoutImpl.horizontalDraggableHandler
var pointerInfoOwner: () -> PointersInfo = { pointersDown() }
@@ -143,8 +143,7 @@
isExternalOverscrollGesture: Boolean = false,
) =
NestedScrollHandlerImpl(
- layoutImpl = layoutImpl,
- orientation = draggableHandler.orientation,
+ draggableHandler = draggableHandler,
topOrLeftBehavior = nestedScrollBehavior,
bottomOrRightBehavior = nestedScrollBehavior,
isExternalOverscrollGesture = { isExternalOverscrollGesture },
@@ -645,7 +644,7 @@
mutableUserActionsA = emptyMap()
mutableUserActionsB = emptyMap()
- // start accelaratedScroll and scroll over to B -> null
+ // start acceleratedScroll and scroll over to B -> null
val dragController2 = onDragStartedImmediately()
dragController2.onDragDelta(pixels = up(fractionOfScreen = 0.5f), expectedConsumed = 0f)
dragController2.onDragDelta(pixels = up(fractionOfScreen = 0.5f), expectedConsumed = 0f)
@@ -1531,7 +1530,7 @@
fun interceptingTransitionKeepsDistance() = runGestureTest {
var swipeDistance = 75f
layoutState.transitions = transitions {
- from(SceneA, to = SceneB) { distance = UserActionDistance { _, _ -> swipeDistance } }
+ from(SceneA, to = SceneB) { distance = UserActionDistance { _, _, _ -> swipeDistance } }
}
// Start transition.
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/InterruptionHandlerTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/InterruptionHandlerTest.kt
index b87cc5c..3622369 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/InterruptionHandlerTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/InterruptionHandlerTest.kt
@@ -132,6 +132,9 @@
assertThat(state.currentTransitions)
.comparingElementsUsing(FromToCurrentTriple)
.containsExactly(
+ // Initial transition, A => B.
+ Triple(SceneA, SceneB, SceneB),
+
// Initial transition reversed, B back to A.
Triple(SceneA, SceneB, SceneA),
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
index 09b5939..b4c8ad7 100644
--- 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
@@ -35,6 +35,7 @@
import androidx.compose.ui.test.assertIsNotDisplayed
import androidx.compose.ui.test.assertPositionInRootIsEqualTo
import androidx.compose.ui.test.hasParent
+import androidx.compose.ui.test.hasTestTag
import androidx.compose.ui.test.hasText
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onAllNodesWithText
@@ -404,4 +405,40 @@
rule.waitForIdle()
rule.onNodeWithTag(fooParentInOverlayTag).assertSizeIsEqualTo(fooSize)
}
+
+ @Test
+ fun movableElementInOverlayShouldBeComposed() {
+ val fooKey = MovableElementKey("foo", contents = setOf(OverlayA))
+ val fooContentTag = "fooContentTag"
+
+ @Composable
+ fun ContentScope.MovableFoo(modifier: Modifier = Modifier) {
+ MovableElement(fooKey, modifier) {
+ content { Box(Modifier.testTag(fooContentTag).size(100.dp)) }
+ }
+ }
+
+ val state =
+ rule.runOnUiThread {
+ MutableSceneTransitionLayoutState(
+ initialScene = SceneA,
+ initialOverlays = setOf(OverlayA),
+ )
+ }
+
+ val scope =
+ rule.setContentAndCreateMainScope {
+ SceneTransitionLayout(state) {
+ scene(SceneA) { Box(Modifier.fillMaxSize()) }
+ overlay(OverlayA) { MovableFoo() }
+ overlay(OverlayB) { Box(Modifier.size(50.dp)) }
+ }
+ }
+
+ rule.onNode(hasTestTag(fooContentTag)).assertIsDisplayed().assertSizeIsEqualTo(100.dp)
+
+ // Show overlay B. This shouldn't have any impact on Foo that should still be composed in A.
+ scope.launch { state.startTransition(transition(SceneA, OverlayB)) }
+ rule.onNode(hasTestTag(fooContentTag)).assertIsDisplayed().assertSizeIsEqualTo(100.dp)
+ }
}
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MultiPointerDraggableTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MultiPointerDraggableTest.kt
index 5ec74f8..85d8b60 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MultiPointerDraggableTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MultiPointerDraggableTest.kt
@@ -593,7 +593,7 @@
}
}
- fun continueDraggingDown() {
+ fun dragDown() {
rule.onRoot().performTouchInput { moveBy(Offset(0f, touchSlop)) }
}
@@ -603,11 +603,78 @@
assertThat(started).isFalse()
swipeConsume = true
- continueDraggingDown()
+ // Drag in same direction
+ dragDown()
assertThat(capturedChange).isNotNull()
capturedChange = null
- continueDraggingDown()
+ dragDown()
+ assertThat(capturedChange).isNull()
+
+ assertThat(started).isTrue()
+ }
+
+ @Test
+ fun multiPointerSwipeDetectorInteractionZeroOffsetFromStartPosition() {
+ val size = 200f
+ val middle = Offset(size / 2f, size / 2f)
+
+ var started = false
+
+ var capturedChange: PointerInputChange? = null
+ var swipeConsume = false
+
+ var touchSlop = 0f
+ rule.setContent {
+ touchSlop = LocalViewConfiguration.current.touchSlop
+ Box(
+ Modifier.size(with(LocalDensity.current) { Size(size, size).toDpSize() })
+ .nestedScrollDispatcher()
+ .multiPointerDraggable(
+ orientation = Orientation.Vertical,
+ startDragImmediately = { false },
+ swipeDetector =
+ object : SwipeDetector {
+ override fun detectSwipe(change: PointerInputChange): Boolean {
+ capturedChange = change
+ return swipeConsume
+ }
+ },
+ onDragStarted = { _, _ ->
+ started = true
+ SimpleDragController(
+ onDrag = { /* do nothing */ },
+ onStop = { /* do nothing */ },
+ )
+ },
+ dispatcher = defaultDispatcher,
+ )
+ ) {}
+ }
+
+ fun startDraggingDown() {
+ rule.onRoot().performTouchInput {
+ down(middle)
+ moveBy(Offset(0f, touchSlop))
+ }
+ }
+
+ fun dragUp() {
+ rule.onRoot().performTouchInput { moveBy(Offset(0f, -touchSlop)) }
+ }
+
+ startDraggingDown()
+ assertThat(capturedChange).isNotNull()
+ capturedChange = null
+ assertThat(started).isFalse()
+
+ swipeConsume = true
+ // Drag in the opposite direction
+ dragUp()
+ assertThat(capturedChange).isNotNull()
+ capturedChange = null
+
+ dragUp()
assertThat(capturedChange).isNull()
assertThat(started).isTrue()
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt
index 57ad81e..79ca891 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt
@@ -153,7 +153,7 @@
// Default transition from A to B.
assertThat(state.setTargetScene(SceneB, animationScope = this)).isNotNull()
- assertThat(state.currentTransition?.transformationSpec?.transformations).hasSize(1)
+ assertThat(state.currentTransition?.transformationSpec?.transformationMatchers).hasSize(1)
// Go back to A.
state.setTargetScene(SceneA, animationScope = this)
@@ -166,7 +166,7 @@
state.setTargetScene(SceneB, animationScope = this, transitionKey = transitionkey)
)
.isNotNull()
- assertThat(state.currentTransition?.transformationSpec?.transformations).hasSize(2)
+ assertThat(state.currentTransition?.transformationSpec?.transformationMatchers).hasSize(2)
}
@Test
@@ -686,4 +686,30 @@
assertThat(state.transitionState).isIdle()
assertThat(job.isCancelled).isTrue()
}
+
+ @Test
+ fun badTransitionSpecThrowsMeaningfulMessageWhenStartingTransition() {
+ val state =
+ MutableSceneTransitionLayoutState(
+ SceneA,
+ transitions {
+ // This transition definition is bad because they both match when transitioning
+ // from A to B.
+ from(SceneA) {}
+ to(SceneB) {}
+ },
+ )
+
+ val exception =
+ assertThrows(IllegalStateException::class.java) {
+ runBlocking { state.startTransition(transition(from = SceneA, to = SceneB)) }
+ }
+
+ assertThat(exception)
+ .hasMessageThat()
+ .isEqualTo(
+ "Found multiple transition specs for transition SceneKey(debugName=SceneA) => " +
+ "SceneKey(debugName=SceneB)"
+ )
+ }
}
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt
index 97a96a4..b3a3261 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt
@@ -55,7 +55,6 @@
import androidx.compose.ui.test.swipeUp
import androidx.compose.ui.test.swipeWithVelocity
import androidx.compose.ui.unit.Density
-import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -672,12 +671,12 @@
}
assertThat(state.isTransitioning(from = SceneA, to = SceneB)).isTrue()
- assertThat(state.currentTransition?.transformationSpec?.transformations).hasSize(1)
+ assertThat(state.currentTransition?.transformationSpec?.transformationMatchers).hasSize(1)
// Move the pointer up to swipe to scene B using the new transition.
rule.onRoot().performTouchInput { moveBy(Offset(0f, -1.dp.toPx()), delayMillis = 1_000) }
assertThat(state.isTransitioning(from = SceneA, to = SceneB)).isTrue()
- assertThat(state.currentTransition?.transformationSpec?.transformations).hasSize(2)
+ assertThat(state.currentTransition?.transformationSpec?.transformationMatchers).hasSize(2)
}
@Test
@@ -685,7 +684,8 @@
val swipeDistance =
object : UserActionDistance {
override fun UserActionDistanceScope.absoluteDistance(
- fromSceneSize: IntSize,
+ fromContent: ContentKey,
+ toContent: ContentKey,
orientation: Orientation,
): Float {
// Foo is going to have a vertical offset of 50dp. Let's make the swipe distance
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/TransitionDslTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/TransitionDslTest.kt
index 1f9ba9e..70f2ff80 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/TransitionDslTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/TransitionDslTest.kt
@@ -28,11 +28,12 @@
import com.android.compose.animation.scene.TestScenes.SceneB
import com.android.compose.animation.scene.TestScenes.SceneC
import com.android.compose.animation.scene.content.state.TransitionState
-import com.android.compose.animation.scene.transformation.CustomSizeTransformation
+import com.android.compose.animation.scene.transformation.CustomPropertyTransformation
import com.android.compose.animation.scene.transformation.OverscrollTranslate
+import com.android.compose.animation.scene.transformation.PropertyTransformation
import com.android.compose.animation.scene.transformation.PropertyTransformationScope
+import com.android.compose.animation.scene.transformation.TransformationMatcher
import com.android.compose.animation.scene.transformation.TransformationRange
-import com.android.compose.animation.scene.transformation.TransformationWithRange
import com.android.compose.test.transition
import com.google.common.truth.Correspondence
import com.google.common.truth.Truth.assertThat
@@ -103,7 +104,7 @@
val transitions = transitions { from(SceneA, to = SceneB) { fade(TestElements.Foo) } }
val transformations =
- transitions.transitionSpecs.single().transformationSpec(aToB()).transformations
+ transitions.transitionSpecs.single().transformationSpec(aToB()).transformationMatchers
assertThat(transformations.size).isEqualTo(1)
assertThat(transformations.single().range).isEqualTo(null)
}
@@ -126,7 +127,7 @@
}
val transformations =
- transitions.transitionSpecs.single().transformationSpec(aToB()).transformations
+ transitions.transitionSpecs.single().transformationSpec(aToB()).transformationMatchers
assertThat(transformations)
.comparingElementsUsing(TRANSFORMATION_RANGE)
.containsExactly(
@@ -157,7 +158,7 @@
}
val transformations =
- transitions.transitionSpecs.single().transformationSpec(aToB()).transformations
+ transitions.transitionSpecs.single().transformationSpec(aToB()).transformationMatchers
assertThat(transformations)
.comparingElementsUsing(TRANSFORMATION_RANGE)
.containsExactly(
@@ -185,7 +186,7 @@
}
val transformations =
- transitions.transitionSpecs.single().transformationSpec(aToB()).transformations
+ transitions.transitionSpecs.single().transformationSpec(aToB()).transformationMatchers
assertThat(transformations)
.comparingElementsUsing(TRANSFORMATION_RANGE)
.containsExactly(
@@ -215,7 +216,7 @@
// to B we defined.
val transitionSpec = transitions.transitionSpec(from = SceneB, to = SceneA, key = null)
- val transformations = transitionSpec.transformationSpec(aToB()).transformations
+ val transformations = transitionSpec.transformationSpec(aToB()).transformationMatchers
assertThat(transformations)
.comparingElementsUsing(TRANSFORMATION_RANGE)
@@ -225,7 +226,7 @@
)
val previewTransformations =
- transitionSpec.previewTransformationSpec(aToB())?.transformations
+ transitionSpec.previewTransformationSpec(aToB())?.transformationMatchers
assertThat(previewTransformations)
.comparingElementsUsing(TRANSFORMATION_RANGE)
@@ -255,7 +256,7 @@
key = TransitionKey.PredictiveBack,
)
- val transformations = transitionSpec.transformationSpec(aToB()).transformations
+ val transformations = transitionSpec.transformationSpec(aToB()).transformationMatchers
assertThat(transformations)
.comparingElementsUsing(TRANSFORMATION_RANGE)
@@ -265,7 +266,7 @@
)
val previewTransformations =
- transitionSpec.previewTransformationSpec(aToB())?.transformations
+ transitionSpec.previewTransformationSpec(aToB())?.transformationMatchers
assertThat(previewTransformations)
.comparingElementsUsing(TRANSFORMATION_RANGE)
@@ -316,7 +317,7 @@
val overscrollSpec = transitions.overscrollSpecs.single()
val transformation =
- overscrollSpec.transformationSpec.transformations.single().transformation
+ overscrollSpec.transformationSpec.transformationMatchers.single().factory.create()
assertThat(transformation).isInstanceOf(OverscrollTranslate::class.java)
}
@@ -324,7 +325,7 @@
fun overscrollSpec_for_overscrollDisabled() {
val transitions = transitions { overscrollDisabled(SceneA, Orientation.Vertical) }
val overscrollSpec = transitions.overscrollSpecs.single()
- assertThat(overscrollSpec.transformationSpec.transformations).isEmpty()
+ assertThat(overscrollSpec.transformationSpec.transformationMatchers).isEmpty()
}
@Test
@@ -353,9 +354,9 @@
val transitions = transitions {
from(SceneA, to = SceneB) {
fractionRange {
- transformation(
- object : CustomSizeTransformation {
- override val matcher: ElementMatcher = TestElements.Foo
+ transformation(TestElements.Foo) {
+ object : CustomPropertyTransformation<IntSize> {
+ override val property = PropertyTransformation.Property.Size
override fun PropertyTransformationScope.transform(
content: ContentKey,
@@ -364,7 +365,7 @@
transitionScope: CoroutineScope,
): IntSize = IntSize.Zero
}
- )
+ }
}
}
}
@@ -377,7 +378,7 @@
companion object {
private val TRANSFORMATION_RANGE =
- Correspondence.transforming<TransformationWithRange<*>, TransformationRange?>(
+ Correspondence.transforming<TransformationMatcher, TransformationRange?>(
{ it?.range },
"has range equal to",
)
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/subjects/TransitionStateSubject.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/subjects/TransitionStateSubject.kt
index 313379f..0adb480 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/subjects/TransitionStateSubject.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/subjects/TransitionStateSubject.kt
@@ -157,7 +157,7 @@
check("isUserInputOngoing").that(actual.isUserInputOngoing).isEqualTo(isUserInputOngoing)
}
- fun hasOverscrollSpec(): OverscrollSpec {
+ internal fun hasOverscrollSpec(): OverscrollSpec {
check("currentOverscrollSpec").that(actual.currentOverscrollSpec).isNotNull()
return actual.currentOverscrollSpec!!
}
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/transformation/CustomTransformationTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/transformation/CustomTransformationTest.kt
index 487b099..444ec4e 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/transformation/CustomTransformationTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/transformation/CustomTransformationTest.kt
@@ -30,7 +30,6 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.compose.animation.scene.ContentKey
import com.android.compose.animation.scene.ElementKey
-import com.android.compose.animation.scene.ElementMatcher
import com.android.compose.animation.scene.TestElements
import com.android.compose.animation.scene.content.state.TransitionState
import com.android.compose.animation.scene.testTransition
@@ -47,8 +46,9 @@
@Test
fun customSize() {
/** A size transformation that adds [add] to the size of the transformed element(s). */
- class AddSizeTransformation(override val matcher: ElementMatcher, private val add: Dp) :
- CustomSizeTransformation {
+ class AddSizeTransformation(private val add: Dp) : CustomPropertyTransformation<IntSize> {
+ override val property = PropertyTransformation.Property.Size
+
override fun PropertyTransformationScope.transform(
content: ContentKey,
element: ElementKey,
@@ -69,7 +69,7 @@
spec = tween(16 * 4, easing = LinearEasing)
// Add 80dp to the width and height of Foo.
- transformation(AddSizeTransformation(TestElements.Foo, 80.dp))
+ transformation(TestElements.Foo) { AddSizeTransformation(80.dp) }
},
) {
before { onElement(TestElements.Foo).assertSizeIsEqualTo(40.dp, 20.dp) }
@@ -84,8 +84,9 @@
@Test
fun customOffset() {
/** An offset transformation that adds [add] to the offset of the transformed element(s). */
- class AddOffsetTransformation(override val matcher: ElementMatcher, private val add: Dp) :
- CustomOffsetTransformation {
+ class AddOffsetTransformation(private val add: Dp) : CustomPropertyTransformation<Offset> {
+ override val property = PropertyTransformation.Property.Offset
+
override fun PropertyTransformationScope.transform(
content: ContentKey,
element: ElementKey,
@@ -106,7 +107,7 @@
spec = tween(16 * 4, easing = LinearEasing)
// Add 80dp to the offset of Foo.
- transformation(AddOffsetTransformation(TestElements.Foo, 80.dp))
+ transformation(TestElements.Foo) { AddOffsetTransformation(80.dp) }
},
) {
before { onElement(TestElements.Foo).assertPositionInRootIsEqualTo(0.dp, 0.dp) }
diff --git a/libs/WindowManager/Shell/tests/e2e/mediaprojection/flicker-service/res/xml/network_security_config.xml b/packages/SystemUI/customization/res/values-land/dimens.xml
similarity index 66%
rename from libs/WindowManager/Shell/tests/e2e/mediaprojection/flicker-service/res/xml/network_security_config.xml
rename to packages/SystemUI/customization/res/values-land/dimens.xml
index 4bd9ca0..50f220c8 100644
--- a/libs/WindowManager/Shell/tests/e2e/mediaprojection/flicker-service/res/xml/network_security_config.xml
+++ b/packages/SystemUI/customization/res/values-land/dimens.xml
@@ -1,6 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2023 The Android Open Source Project
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2024 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
@@ -15,8 +14,6 @@
~ limitations under the License.
-->
-<network-security-config>
- <domain-config cleartextTrafficPermitted="true">
- <domain includeSubdomains="true">localhost</domain>
- </domain-config>
-</network-security-config>
+<resources>
+ <dimen name="lock_icon_margin_bottom">24dp</dimen>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-sw600dp-land/dimens.xml b/packages/SystemUI/customization/res/values-sw600dp-land/dimens.xml
index 651e401..8760281 100644
--- a/packages/SystemUI/customization/res/values-sw600dp-land/dimens.xml
+++ b/packages/SystemUI/customization/res/values-sw600dp-land/dimens.xml
@@ -16,4 +16,6 @@
<resources>
<dimen name="keyguard_smartspace_top_offset">0dp</dimen>
+ <dimen name="status_view_margin_horizontal">8dp</dimen>
+ <dimen name="lock_icon_margin_bottom">60dp</dimen>
</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-sw600dp/dimens.xml b/packages/SystemUI/customization/res/values-sw600dp/dimens.xml
index 10e630d..37cd590 100644
--- a/packages/SystemUI/customization/res/values-sw600dp/dimens.xml
+++ b/packages/SystemUI/customization/res/values-sw600dp/dimens.xml
@@ -17,4 +17,5 @@
<resources>
<!-- For portrait direction in unfold foldable device, we don't need keyguard_smartspace_top_offset-->
<dimen name="keyguard_smartspace_top_offset">0dp</dimen>
+ <dimen name="status_view_margin_horizontal">62dp</dimen>
</resources>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/e2e/mediaprojection/flicker-service/res/xml/network_security_config.xml b/packages/SystemUI/customization/res/values-sw720dp-land/dimens.xml
similarity index 66%
copy from libs/WindowManager/Shell/tests/e2e/mediaprojection/flicker-service/res/xml/network_security_config.xml
copy to packages/SystemUI/customization/res/values-sw720dp-land/dimens.xml
index 4bd9ca0..c1cf42c 100644
--- a/libs/WindowManager/Shell/tests/e2e/mediaprojection/flicker-service/res/xml/network_security_config.xml
+++ b/packages/SystemUI/customization/res/values-sw720dp-land/dimens.xml
@@ -1,6 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2023 The Android Open Source Project
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2024 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
@@ -15,8 +14,6 @@
~ limitations under the License.
-->
-<network-security-config>
- <domain-config cleartextTrafficPermitted="true">
- <domain includeSubdomains="true">localhost</domain>
- </domain-config>
-</network-security-config>
+<resources>
+ <dimen name="status_view_margin_horizontal">24dp</dimen>
+</resources>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/e2e/mediaprojection/flicker-service/res/xml/network_security_config.xml b/packages/SystemUI/customization/res/values-sw720dp-port/dimens.xml
similarity index 66%
copy from libs/WindowManager/Shell/tests/e2e/mediaprojection/flicker-service/res/xml/network_security_config.xml
copy to packages/SystemUI/customization/res/values-sw720dp-port/dimens.xml
index 4bd9ca0..54dbeaa 100644
--- a/libs/WindowManager/Shell/tests/e2e/mediaprojection/flicker-service/res/xml/network_security_config.xml
+++ b/packages/SystemUI/customization/res/values-sw720dp-port/dimens.xml
@@ -1,6 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2023 The Android Open Source Project
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2024 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
@@ -15,8 +14,6 @@
~ limitations under the License.
-->
-<network-security-config>
- <domain-config cleartextTrafficPermitted="true">
- <domain includeSubdomains="true">localhost</domain>
- </domain-config>
-</network-security-config>
+<resources>
+ <dimen name="status_view_margin_horizontal">124dp</dimen>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values/dimens.xml b/packages/SystemUI/customization/res/values/dimens.xml
index 7feea6e..21b4c71 100644
--- a/packages/SystemUI/customization/res/values/dimens.xml
+++ b/packages/SystemUI/customization/res/values/dimens.xml
@@ -39,4 +39,6 @@
<!--Dimens used in both lockscreen preview and smartspace -->
<dimen name="date_weather_view_height">24dp</dimen>
<dimen name="enhanced_smartspace_height">104dp</dimen>
+ <dimen name="status_view_margin_horizontal">0dp</dimen>
+ <dimen name="lock_icon_margin_bottom">74dp</dimen>
</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AssetLoader.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AssetLoader.kt
deleted file mode 100644
index 2a2d333..0000000
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AssetLoader.kt
+++ /dev/null
@@ -1,252 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.shared.clocks
-
-import android.content.Context
-import android.content.res.Resources
-import android.graphics.Typeface
-import android.graphics.drawable.Drawable
-import android.util.TypedValue
-import com.android.internal.policy.SystemBarUtils
-import com.android.systemui.log.core.Logger
-import com.android.systemui.log.core.MessageBuffer
-import com.android.systemui.monet.Style as MonetStyle
-import java.io.IOException
-
-class AssetLoader
-private constructor(
- private val pluginCtx: Context,
- private val sysuiCtx: Context,
- private val baseDir: String,
- var seedColor: Int?,
- var overrideChroma: Float?,
- val typefaceCache: TypefaceCache,
- messageBuffer: MessageBuffer,
-) {
- val logger = Logger(messageBuffer, TAG)
- private val resources =
- listOf(
- Pair(pluginCtx.resources, pluginCtx.packageName),
- Pair(sysuiCtx.resources, sysuiCtx.packageName),
- )
-
- constructor(
- pluginCtx: Context,
- sysuiCtx: Context,
- baseDir: String,
- messageBuffer: MessageBuffer,
- ) : this(
- pluginCtx,
- sysuiCtx,
- baseDir,
- seedColor = null,
- overrideChroma = null,
- typefaceCache =
- TypefaceCache(messageBuffer) {
- // TODO(b/364680873): Move constant to config_clockFontFamily when shipping
- return@TypefaceCache Typeface.create("google-sans-flex-clock", Typeface.NORMAL)
- },
- messageBuffer = messageBuffer,
- )
-
- fun listAssets(path: String): List<String> {
- return pluginCtx.resources.assets.list("$baseDir$path")?.toList() ?: emptyList()
- }
-
- fun tryReadString(resStr: String): String? = tryRead(resStr, ::readString)
-
- fun readString(resStr: String): String {
- val resPair = resolveResourceId(resStr)
- if (resPair == null) {
- throw IOException("Failed to parse string: $resStr")
- }
-
- val (res, id) = resPair
- return res.getString(id)
- }
-
- fun readFontAsset(resStr: String): Typeface = typefaceCache.getTypeface(resStr)
-
- fun tryReadTextAsset(path: String?): String? = tryRead(path, ::readTextAsset)
-
- fun readTextAsset(path: String): String {
- return pluginCtx.resources.assets.open("$baseDir$path").use { stream ->
- val buffer = ByteArray(stream.available())
- stream.read(buffer)
- String(buffer)
- }
- }
-
- fun tryReadDrawableAsset(path: String?): Drawable? = tryRead(path, ::readDrawableAsset)
-
- fun readDrawableAsset(path: String): Drawable {
- var result: Drawable?
-
- if (path.startsWith("@")) {
- val pair = resolveResourceId(path)
- if (pair == null) {
- throw IOException("Failed to parse $path to an id")
- }
- val (res, id) = pair
- result = res.getDrawable(id)
- } else if (path.endsWith("xml")) {
- // TODO(b/248609434): Support xml files in assets
- throw IOException("Cannot load xml files from assets")
- } else {
- // Attempt to load as if it's a bitmap and directly loadable
- result =
- pluginCtx.resources.assets.open("$baseDir$path").use { stream ->
- Drawable.createFromResourceStream(
- pluginCtx.resources,
- TypedValue(),
- stream,
- null,
- )
- }
- }
-
- return result ?: throw IOException("Failed to load: $baseDir$path")
- }
-
- fun parseResourceId(resStr: String): Triple<String?, String, String> {
- if (!resStr.startsWith("@")) {
- throw IOException("Invalid resource id: $resStr; Must start with '@'")
- }
-
- // Parse out resource string
- val parts = resStr.drop(1).split('/', ':')
- return when (parts.size) {
- 2 -> Triple(null, parts[0], parts[1])
- 3 -> Triple(parts[0], parts[1], parts[2])
- else -> throw IOException("Failed to parse resource string: $resStr")
- }
- }
-
- fun resolveResourceId(resStr: String): Pair<Resources, Int>? {
- val (packageName, category, name) = parseResourceId(resStr)
- return resolveResourceId(packageName, category, name)
- }
-
- fun resolveResourceId(
- packageName: String?,
- category: String,
- name: String,
- ): Pair<Resources, Int>? {
- for ((res, ctxPkgName) in resources) {
- val result = res.getIdentifier(name, category, packageName ?: ctxPkgName)
- if (result != 0) {
- return Pair(res, result)
- }
- }
- return null
- }
-
- private fun <TArg : Any, TRes : Any> tryRead(arg: TArg?, fn: (TArg) -> TRes): TRes? {
- try {
- if (arg == null) {
- return null
- }
- return fn(arg)
- } catch (ex: IOException) {
- logger.w("Failed to read $arg", ex)
- return null
- }
- }
-
- fun assetExists(path: String): Boolean {
- try {
- if (path.startsWith("@")) {
- val pair = resolveResourceId(path)
- return pair != null
- } else {
- val stream = pluginCtx.resources.assets.open("$baseDir$path")
- if (stream == null) {
- return false
- }
-
- stream.close()
- return true
- }
- } catch (ex: IOException) {
- return false
- }
- }
-
- fun copy(messageBuffer: MessageBuffer? = null): AssetLoader =
- AssetLoader(
- pluginCtx,
- sysuiCtx,
- baseDir,
- seedColor,
- overrideChroma,
- typefaceCache,
- messageBuffer ?: logger.buffer,
- )
-
- fun setSeedColor(seedColor: Int?, style: MonetStyle?) {
- this.seedColor = seedColor
- }
-
- fun getClockPaddingStart(): Int {
- val result = resolveResourceId(null, "dimen", "clock_padding_start")
- if (result != null) {
- val (res, id) = result
- return res.getDimensionPixelSize(id)
- }
- return -1
- }
-
- fun getStatusBarHeight(): Int {
- val display = pluginCtx.getDisplayNoVerify()
- if (display != null) {
- return SystemBarUtils.getStatusBarHeight(pluginCtx.resources, display.cutout)
- }
-
- logger.w("No display available; falling back to android.R.dimen.status_bar_height")
- val statusBarHeight = resolveResourceId("android", "dimen", "status_bar_height")
- if (statusBarHeight != null) {
- val (res, resId) = statusBarHeight
- return res.getDimensionPixelSize(resId)
- }
-
- throw Exception("Could not fetch StatusBarHeight")
- }
-
- fun getResourcesId(name: String): Int = getResource("id", name) { _, id -> id }
-
- fun getDimen(name: String): Int = getResource("dimen", name, Resources::getDimensionPixelSize)
-
- fun getString(name: String): String = getResource("string", name, Resources::getString)
-
- private fun <T> getResource(
- category: String,
- name: String,
- getter: (res: Resources, id: Int) -> T,
- ): T {
- val result = resolveResourceId(null, category, name)
- if (result != null) {
- val (res, id) = result
- if (id == -1) throw Exception("Cannot find id of $id from $TAG")
- return getter(res, id)
- }
- throw Exception("Cannot find id of $name from $TAG")
- }
-
- companion object {
- private val TAG = AssetLoader::class.simpleName!!
- }
-}
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ComposedDigitalLayerController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ComposedDigitalLayerController.kt
index 4ed8fd8..d0a32dc 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ComposedDigitalLayerController.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ComposedDigitalLayerController.kt
@@ -16,12 +16,9 @@
package com.android.systemui.shared.clocks
-import android.content.Context
-import android.content.res.Resources
import android.graphics.Rect
import androidx.annotation.VisibleForTesting
import com.android.systemui.log.core.Logger
-import com.android.systemui.log.core.MessageBuffer
import com.android.systemui.plugins.clocks.AlarmData
import com.android.systemui.plugins.clocks.ClockAnimations
import com.android.systemui.plugins.clocks.ClockEvents
@@ -37,31 +34,22 @@
import java.util.TimeZone
class ComposedDigitalLayerController(
- private val ctx: Context,
- private val resources: Resources,
- private val assets: AssetLoader, // TODO(b/364680879): Remove and replace w/ resources
+ private val clockCtx: ClockContext,
private val layer: ComposedDigitalHandLayer,
- messageBuffer: MessageBuffer,
) : SimpleClockLayerController {
- private val logger = Logger(messageBuffer, ComposedDigitalLayerController::class.simpleName!!)
+ private val logger =
+ Logger(clockCtx.messageBuffer, ComposedDigitalLayerController::class.simpleName!!)
val layerControllers = mutableListOf<SimpleClockLayerController>()
val dozeState = DefaultClockController.AnimationState(1F)
- override val view = FlexClockView(ctx, assets, messageBuffer)
+ override val view = FlexClockView(clockCtx)
init {
layer.digitalLayers.forEach {
- val childView = SimpleDigitalClockTextView(ctx, messageBuffer)
+ val childView = SimpleDigitalClockTextView(clockCtx)
val controller =
- SimpleDigitalHandLayerController(
- ctx,
- resources,
- assets,
- it as DigitalHandLayer,
- childView,
- messageBuffer,
- )
+ SimpleDigitalHandLayerController(clockCtx, it as DigitalHandLayer, childView)
view.addView(childView)
layerControllers.add(controller)
@@ -156,8 +144,9 @@
val color =
when {
theme.seedColor != null -> theme.seedColor!!
- theme.isDarkTheme -> resources.getColor(android.R.color.system_accent1_100)
- else -> resources.getColor(android.R.color.system_accent2_600)
+ theme.isDarkTheme ->
+ clockCtx.resources.getColor(android.R.color.system_accent1_100)
+ else -> clockCtx.resources.getColor(android.R.color.system_accent2_600)
}
view.updateColor(color)
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt
index be4ebdf..9bb92bc 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt
@@ -15,10 +15,10 @@
import android.content.Context
import android.content.res.Resources
+import android.graphics.Typeface
import android.view.LayoutInflater
import com.android.systemui.customization.R
-import com.android.systemui.log.core.LogLevel
-import com.android.systemui.log.core.LogcatOnlyMessageBuffer
+import com.android.systemui.log.core.MessageBuffer
import com.android.systemui.plugins.clocks.ClockController
import com.android.systemui.plugins.clocks.ClockFontAxis
import com.android.systemui.plugins.clocks.ClockFontAxisSetting
@@ -33,6 +33,15 @@
private val TAG = DefaultClockProvider::class.simpleName
const val DEFAULT_CLOCK_ID = "DEFAULT"
+data class ClockContext(
+ val context: Context,
+ val resources: Resources,
+ val settings: ClockSettings,
+ val typefaceCache: TypefaceCache,
+ val messageBuffers: ClockMessageBuffers,
+ val messageBuffer: MessageBuffer,
+)
+
/** Provides the default system clock */
class DefaultClockProvider(
val ctx: Context,
@@ -55,18 +64,20 @@
}
return if (isClockReactiveVariantsEnabled) {
- val buffer =
- messageBuffers?.infraMessageBuffer ?: LogcatOnlyMessageBuffer(LogLevel.INFO)
- val assets = AssetLoader(ctx, ctx, "clocks/", buffer)
- assets.setSeedColor(settings.seedColor, null)
+ val buffers = messageBuffers ?: ClockMessageBuffers(LogUtil.DEFAULT_MESSAGE_BUFFER)
val fontAxes = ClockFontAxis.merge(FlexClockController.FONT_AXES, settings.axes)
+ val clockSettings = settings.copy(axes = fontAxes.map { it.toSetting() })
+ val typefaceCache = TypefaceCache(buffers.infraMessageBuffer) { FLEX_TYPEFACE }
FlexClockController(
- ctx,
- resources,
- settings.copy(axes = fontAxes.map { it.toSetting() }),
- assets,
+ ClockContext(
+ ctx,
+ resources,
+ clockSettings,
+ typefaceCache,
+ buffers,
+ buffers.infraMessageBuffer,
+ ),
FLEX_DESIGN,
- messageBuffers,
)
} else {
DefaultClockController(
@@ -116,6 +127,11 @@
ClockFontAxisSetting("slnt", 0f),
)
+ val FLEX_TYPEFACE by lazy {
+ // TODO(b/364680873): Move constant to config_clockFontFamily when shipping
+ Typeface.create("google-sans-flex-clock", Typeface.NORMAL)
+ }
+
val FLEX_DESIGN = run {
val largeLayer =
listOf(
@@ -139,7 +155,7 @@
alignment =
DigitalAlignment(
HorizontalAlignment.CENTER,
- VerticalAlignment.CENTER,
+ VerticalAlignment.BASELINE,
),
dateTimeFormat = "hh",
),
@@ -158,7 +174,7 @@
alignment =
DigitalAlignment(
HorizontalAlignment.CENTER,
- VerticalAlignment.CENTER,
+ VerticalAlignment.BASELINE,
),
dateTimeFormat = "hh",
),
@@ -177,7 +193,7 @@
alignment =
DigitalAlignment(
HorizontalAlignment.CENTER,
- VerticalAlignment.CENTER,
+ VerticalAlignment.BASELINE,
),
dateTimeFormat = "mm",
),
@@ -196,7 +212,7 @@
alignment =
DigitalAlignment(
HorizontalAlignment.CENTER,
- VerticalAlignment.CENTER,
+ VerticalAlignment.BASELINE,
),
dateTimeFormat = "mm",
),
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockController.kt
index 6c627e2..c7a3f63 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockController.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockController.kt
@@ -16,8 +16,6 @@
package com.android.systemui.shared.clocks
-import android.content.Context
-import android.content.res.Resources
import com.android.systemui.customization.R
import com.android.systemui.plugins.clocks.AlarmData
import com.android.systemui.plugins.clocks.AxisType
@@ -26,8 +24,6 @@
import com.android.systemui.plugins.clocks.ClockEvents
import com.android.systemui.plugins.clocks.ClockFontAxis
import com.android.systemui.plugins.clocks.ClockFontAxisSetting
-import com.android.systemui.plugins.clocks.ClockMessageBuffers
-import com.android.systemui.plugins.clocks.ClockSettings
import com.android.systemui.plugins.clocks.ThemeConfig
import com.android.systemui.plugins.clocks.WeatherData
import com.android.systemui.plugins.clocks.ZenData
@@ -38,42 +34,28 @@
/** Controller for the default flex clock */
class FlexClockController(
- private val ctx: Context,
- private val resources: Resources,
- private val settings: ClockSettings,
- private val assets: AssetLoader, // TODO(b/364680879): Remove and replace w/ resources
+ private val clockCtx: ClockContext,
val design: ClockDesign, // TODO(b/364680879): Remove when done inlining
- val messageBuffers: ClockMessageBuffers?,
) : ClockController {
- override val smallClock = run {
- val buffer = messageBuffers?.smallClockMessageBuffer ?: LogUtil.DEFAULT_MESSAGE_BUFFER
+ override val smallClock =
FlexClockFaceController(
- ctx,
- resources,
- assets.copy(messageBuffer = buffer),
+ clockCtx.copy(messageBuffer = clockCtx.messageBuffers.smallClockMessageBuffer),
design.small ?: design.large!!,
- false,
- buffer,
+ isLargeClock = false,
)
- }
- override val largeClock = run {
- val buffer = messageBuffers?.largeClockMessageBuffer ?: LogUtil.DEFAULT_MESSAGE_BUFFER
+ override val largeClock =
FlexClockFaceController(
- ctx,
- resources,
- assets.copy(messageBuffer = buffer),
+ clockCtx.copy(messageBuffer = clockCtx.messageBuffers.largeClockMessageBuffer),
design.large ?: design.small!!,
- true,
- buffer,
+ isLargeClock = true,
)
- }
override val config: ClockConfig by lazy {
ClockConfig(
DEFAULT_CLOCK_ID,
- resources.getString(R.string.clock_default_name),
- resources.getString(R.string.clock_default_description),
+ clockCtx.resources.getString(R.string.clock_default_name),
+ clockCtx.resources.getString(R.string.clock_default_description),
isReactiveToTone = true,
)
}
@@ -125,8 +107,8 @@
}
override fun initialize(isDarkTheme: Boolean, dozeFraction: Float, foldFraction: Float) {
- val theme = ThemeConfig(isDarkTheme, assets.seedColor)
- events.onFontAxesChanged(settings.axes)
+ val theme = ThemeConfig(isDarkTheme, clockCtx.settings.seedColor)
+ events.onFontAxesChanged(clockCtx.settings.axes)
smallClock.run {
events.onThemeChanged(theme)
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockFaceController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockFaceController.kt
index ee21ea6..a8890e6 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockFaceController.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockFaceController.kt
@@ -16,15 +16,12 @@
package com.android.systemui.shared.clocks
-import android.content.Context
-import android.content.res.Resources
import android.graphics.Rect
import android.view.Gravity
import android.view.View
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
import android.widget.FrameLayout
import com.android.systemui.customization.R
-import com.android.systemui.log.core.MessageBuffer
import com.android.systemui.plugins.clocks.AlarmData
import com.android.systemui.plugins.clocks.ClockAnimations
import com.android.systemui.plugins.clocks.ClockEvents
@@ -45,22 +42,19 @@
// TODO(b/364680879): Merge w/ ComposedDigitalLayerController
class FlexClockFaceController(
- ctx: Context,
- private val resources: Resources,
- val assets: AssetLoader, // TODO(b/364680879): Remove and replace w/ resources
+ clockCtx: ClockContext,
face: ClockFace,
private val isLargeClock: Boolean,
- messageBuffer: MessageBuffer,
) : ClockFaceController {
override val view: View
get() = layerController.view
override val config = ClockFaceConfig(hasCustomPositionUpdatedAnimation = true)
- override var theme = ThemeConfig(true, assets.seedColor)
+ override var theme = ThemeConfig(true, clockCtx.settings.seedColor)
private val keyguardLargeClockTopMargin =
- resources.getDimensionPixelSize(R.dimen.keyguard_large_clock_top_margin)
+ clockCtx.resources.getDimensionPixelSize(R.dimen.keyguard_large_clock_top_margin)
val layerController: SimpleClockLayerController
val timespecHandler = DigitalTimespecHandler(DigitalTimespec.TIME_FULL_FORMAT, "hh:mm")
@@ -72,23 +66,10 @@
layerController =
if (isLargeClock) {
- ComposedDigitalLayerController(
- ctx,
- resources,
- assets,
- layer as ComposedDigitalHandLayer,
- messageBuffer,
- )
+ ComposedDigitalLayerController(clockCtx, layer as ComposedDigitalHandLayer)
} else {
- val childView = SimpleDigitalClockTextView(ctx, messageBuffer)
- SimpleDigitalHandLayerController(
- ctx,
- resources,
- assets,
- layer as DigitalHandLayer,
- childView,
- messageBuffer,
- )
+ val childView = SimpleDigitalClockTextView(clockCtx)
+ SimpleDigitalHandLayerController(clockCtx, layer as DigitalHandLayer, childView)
}
layerController.view.layoutParams = lp
}
@@ -97,7 +78,7 @@
private fun offsetGlyphsForStepClockAnimation(
clockStartLeft: Int,
direction: Int,
- fraction: Float
+ fraction: Float,
) {
(view as? FlexClockView)?.offsetGlyphsForStepClockAnimation(
clockStartLeft,
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/SimpleDigitalHandLayerController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/SimpleDigitalHandLayerController.kt
index 143b28f..ebac4b24 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/SimpleDigitalHandLayerController.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/SimpleDigitalHandLayerController.kt
@@ -16,8 +16,6 @@
package com.android.systemui.shared.clocks
-import android.content.Context
-import android.content.res.Resources
import android.graphics.Rect
import android.view.View
import android.view.ViewGroup
@@ -25,7 +23,6 @@
import androidx.annotation.VisibleForTesting
import com.android.systemui.customization.R
import com.android.systemui.log.core.Logger
-import com.android.systemui.log.core.MessageBuffer
import com.android.systemui.plugins.clocks.AlarmData
import com.android.systemui.plugins.clocks.ClockAnimations
import com.android.systemui.plugins.clocks.ClockEvents
@@ -42,14 +39,11 @@
private val TAG = SimpleDigitalHandLayerController::class.simpleName!!
open class SimpleDigitalHandLayerController<T>(
- private val ctx: Context,
- private val resources: Resources,
- private val assets: AssetLoader, // TODO(b/364680879): Remove and replace w/ resources
+ private val clockCtx: ClockContext,
private val layer: DigitalHandLayer,
override val view: T,
- messageBuffer: MessageBuffer,
) : SimpleClockLayerController where T : View, T : SimpleDigitalClockView {
- private val logger = Logger(messageBuffer, TAG)
+ private val logger = Logger(clockCtx.messageBuffer, TAG)
val timespec = DigitalTimespecHandler(layer.timespec, layer.dateTimeFormat)
@VisibleForTesting
@@ -75,12 +69,12 @@
layer.alignment.verticalAlignment?.let { view.verticalAlignment = it }
layer.alignment.horizontalAlignment?.let { view.horizontalAlignment = it }
}
- view.applyStyles(assets, layer.style, layer.aodStyle)
+ view.applyStyles(layer.style, layer.aodStyle)
view.id =
- ctx.resources.getIdentifier(
+ clockCtx.resources.getIdentifier(
generateDigitalLayerIdString(layer),
"id",
- ctx.getPackageName(),
+ clockCtx.context.getPackageName(),
)
}
@@ -306,8 +300,9 @@
val color =
when {
theme.seedColor != null -> theme.seedColor!!
- theme.isDarkTheme -> resources.getColor(android.R.color.system_accent1_100)
- else -> resources.getColor(android.R.color.system_accent2_600)
+ theme.isDarkTheme ->
+ clockCtx.resources.getColor(android.R.color.system_accent1_100)
+ else -> clockCtx.resources.getColor(android.R.color.system_accent2_600)
}
view.updateColor(color)
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/DigitalClockFaceView.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/DigitalClockFaceView.kt
index b09332f..d4eb767 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/DigitalClockFaceView.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/DigitalClockFaceView.kt
@@ -16,24 +16,23 @@
package com.android.systemui.shared.clocks.view
-import android.content.Context
import android.graphics.Canvas
import android.graphics.Point
import android.view.View
import android.widget.FrameLayout
import androidx.annotation.VisibleForTesting
import com.android.systemui.log.core.Logger
-import com.android.systemui.log.core.MessageBuffer
import com.android.systemui.plugins.clocks.AlarmData
import com.android.systemui.plugins.clocks.ClockFontAxisSetting
import com.android.systemui.plugins.clocks.WeatherData
import com.android.systemui.plugins.clocks.ZenData
+import com.android.systemui.shared.clocks.ClockContext
import com.android.systemui.shared.clocks.LogUtil
import java.util.Locale
// TODO(b/364680879): Merge w/ only subclass FlexClockView
-abstract class DigitalClockFaceView(ctx: Context, messageBuffer: MessageBuffer) : FrameLayout(ctx) {
- protected val logger = Logger(messageBuffer, this::class.simpleName!!)
+abstract class DigitalClockFaceView(clockCtx: ClockContext) : FrameLayout(clockCtx.context) {
+ protected val logger = Logger(clockCtx.messageBuffer, this::class.simpleName!!)
get() = field ?: LogUtil.FALLBACK_INIT_LOGGER
abstract var digitalClockTextViewMap: MutableMap<Int, SimpleDigitalClockTextView>
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/FlexClockView.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/FlexClockView.kt
index 593eba9..faef18c 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/FlexClockView.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/FlexClockView.kt
@@ -16,31 +16,34 @@
package com.android.systemui.shared.clocks.view
-import android.content.Context
import android.graphics.Canvas
import android.graphics.Point
+import android.icu.text.NumberFormat
import android.util.MathUtils.constrainedMap
import android.view.View
import android.view.ViewGroup
import android.widget.RelativeLayout
import com.android.app.animation.Interpolators
import com.android.systemui.customization.R
-import com.android.systemui.log.core.MessageBuffer
-import com.android.systemui.shared.clocks.AssetLoader
+import com.android.systemui.shared.clocks.ClockContext
import com.android.systemui.shared.clocks.DigitTranslateAnimator
+import java.util.Locale
import kotlin.math.abs
import kotlin.math.max
import kotlin.math.min
fun clamp(value: Float, minVal: Float, maxVal: Float): Float = max(min(value, maxVal), minVal)
-class FlexClockView(context: Context, val assets: AssetLoader, messageBuffer: MessageBuffer) :
- DigitalClockFaceView(context, messageBuffer) {
+class FlexClockView(clockCtx: ClockContext) : DigitalClockFaceView(clockCtx) {
override var digitalClockTextViewMap = mutableMapOf<Int, SimpleDigitalClockTextView>()
- val digitLeftTopMap = mutableMapOf<Int, Point>()
- var maxSingleDigitSize = Point(-1, -1)
- val lockscreenTranslate = Point(0, 0)
- var aodTranslate = Point(0, 0)
+ private val digitLeftTopMap = mutableMapOf<Int, Point>()
+
+ private var maxSingleDigitSize = Point(-1, -1)
+ private val lockscreenTranslate = Point(0, 0)
+ private var aodTranslate = Point(0, 0)
+
+ // Does the current language have mono vertical size when displaying numerals
+ private var isMonoVerticalNumericLineSpacing = true
init {
setWillNotDraw(false)
@@ -49,6 +52,7 @@
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT,
)
+ updateLocale(Locale.getDefault())
}
private val digitOffsets = mutableMapOf<Int, Float>()
@@ -61,12 +65,19 @@
protected override fun calculateSize(widthMeasureSpec: Int, heightMeasureSpec: Int): Point {
maxSingleDigitSize = Point(-1, -1)
+ val bottomLocation: (textView: SimpleDigitalClockTextView) -> Int = { textView ->
+ if (isMonoVerticalNumericLineSpacing) {
+ maxSingleDigitSize.y
+ } else {
+ (textView.paint.fontMetrics.descent - textView.paint.fontMetrics.ascent).toInt()
+ }
+ }
+
digitalClockTextViewMap.forEach { (_, textView) ->
textView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED)
maxSingleDigitSize.x = max(maxSingleDigitSize.x, textView.measuredWidth)
- maxSingleDigitSize.y = max(maxSingleDigitSize.y, textView.measuredHeight)
+ maxSingleDigitSize.y = max(bottomLocation(textView), textView.measuredHeight)
}
- val textView = digitalClockTextViewMap[R.id.HOUR_FIRST_DIGIT]!!
aodTranslate = Point(0, 0)
return Point(
((maxSingleDigitSize.x + abs(aodTranslate.x)) * 2),
@@ -106,6 +117,11 @@
}
}
+ override fun onLocaleChanged(locale: Locale) {
+ updateLocale(locale)
+ requestLayout()
+ }
+
override fun animateDoze(isDozing: Boolean, isAnimated: Boolean) {
dozeControlState.animateDoze = {
super.animateDoze(isDozing, isAnimated)
@@ -166,6 +182,18 @@
}
}
+ private fun updateLocale(locale: Locale) {
+ isMonoVerticalNumericLineSpacing =
+ !NON_MONO_VERTICAL_NUMERIC_LINE_SPACING_LANGUAGES.any {
+ val newLocaleNumberFormat =
+ NumberFormat.getInstance(locale).format(FORMAT_NUMBER.toLong())
+ val nonMonoVerticalNumericLineSpaceNumberFormat =
+ NumberFormat.getInstance(Locale.forLanguageTag(it))
+ .format(FORMAT_NUMBER.toLong())
+ newLocaleNumberFormat == nonMonoVerticalNumericLineSpaceNumberFormat
+ }
+ }
+
/**
* Offsets the textViews of the clock for the step clock animation.
*
@@ -264,10 +292,18 @@
// Constants for the animation
private val MOVE_INTERPOLATOR = Interpolators.EMPHASIZED
+ private const val FORMAT_NUMBER = 1234567890
+
// Total available transition time for each digit, taking into account the step. If step is
// 0.1, then digit 0 would animate over 0.0 - 0.7, making availableTime 0.7.
private const val AVAILABLE_ANIMATION_TIME = 1.0f - MOVE_DIGIT_STEP * (NUM_DIGITS - 1)
+ // Add language tags below that do not have vertically mono spaced numerals
+ private val NON_MONO_VERTICAL_NUMERIC_LINE_SPACING_LANGUAGES =
+ setOf(
+ "my", // Burmese
+ )
+
// Use the sign of targetTranslation to control the direction of digit translation
fun updateDirectionalTargetTranslate(id: Int, targetTranslation: Point): Point {
val outPoint = Point(targetTranslation)
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextView.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextView.kt
index 5c84f2d..48761c0 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextView.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextView.kt
@@ -17,7 +17,6 @@
package com.android.systemui.shared.clocks.view
import android.annotation.SuppressLint
-import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
@@ -37,13 +36,11 @@
import android.widget.TextView
import com.android.internal.annotations.VisibleForTesting
import com.android.systemui.animation.TextAnimator
-import com.android.systemui.animation.TypefaceVariantCache
import com.android.systemui.customization.R
import com.android.systemui.log.core.Logger
-import com.android.systemui.log.core.MessageBuffer
import com.android.systemui.plugins.clocks.ClockFontAxisSetting
-import com.android.systemui.shared.clocks.AssetLoader
import com.android.systemui.shared.clocks.ClockAnimation
+import com.android.systemui.shared.clocks.ClockContext
import com.android.systemui.shared.clocks.DigitTranslateAnimator
import com.android.systemui.shared.clocks.DimensionParser
import com.android.systemui.shared.clocks.FontTextStyle
@@ -57,18 +54,15 @@
private val TAG = SimpleDigitalClockTextView::class.simpleName!!
@SuppressLint("AppCompatCustomView")
-open class SimpleDigitalClockTextView(
- ctx: Context,
- messageBuffer: MessageBuffer,
- attrs: AttributeSet? = null,
-) : TextView(ctx, attrs), SimpleDigitalClockView {
+open class SimpleDigitalClockTextView(clockCtx: ClockContext, attrs: AttributeSet? = null) :
+ TextView(clockCtx.context, attrs), SimpleDigitalClockView {
val lockScreenPaint = TextPaint()
override lateinit var textStyle: FontTextStyle
lateinit var aodStyle: FontTextStyle
private var lsFontVariation = ClockFontAxisSetting.toFVar(DEFAULT_LS_VARIATION)
private var aodFontVariation = ClockFontAxisSetting.toFVar(DEFAULT_AOD_VARIATION)
- private val parser = DimensionParser(ctx)
+ private val parser = DimensionParser(clockCtx.context)
var maxSingleDigitHeight = -1
var maxSingleDigitWidth = -1
var digitTranslateAnimator: DigitTranslateAnimator? = null
@@ -91,33 +85,23 @@
private val prevTextBounds = Rect()
// targetTextBounds holds the state we are interpolating to
private val targetTextBounds = Rect()
- protected val logger = Logger(messageBuffer, this::class.simpleName!!)
+ protected val logger = Logger(clockCtx.messageBuffer, this::class.simpleName!!)
get() = field ?: LogUtil.FALLBACK_INIT_LOGGER
private var aodDozingInterpolator: Interpolator? = null
@VisibleForTesting lateinit var textAnimator: TextAnimator
- lateinit var typefaceCache: TypefaceVariantCache
- private set
-
- private fun setTypefaceCache(value: TypefaceVariantCache) {
- typefaceCache = value
- if (this::textAnimator.isInitialized) {
- textAnimator.typefaceCache = value
- }
- }
+ private val typefaceCache = clockCtx.typefaceCache.getVariantCache("")
@VisibleForTesting
var textAnimatorFactory: (Layout, () -> Unit) -> TextAnimator = { layout, invalidateCb ->
TextAnimator(layout, ClockAnimation.NUM_CLOCK_FONT_ANIMATION_STEPS, invalidateCb).also {
- if (this::typefaceCache.isInitialized) {
- it.typefaceCache = typefaceCache
- }
+ it.typefaceCache = typefaceCache
}
}
- override var verticalAlignment: VerticalAlignment = VerticalAlignment.CENTER
+ override var verticalAlignment: VerticalAlignment = VerticalAlignment.BASELINE
override var horizontalAlignment: HorizontalAlignment = HorizontalAlignment.LEFT
override var isAnimationEnabled = true
override var dozeFraction: Float = 0F
@@ -258,7 +242,7 @@
-translation.y.toFloat(),
(-translation.x + measuredWidth).toFloat(),
(-translation.y + measuredHeight).toFloat(),
- Paint().also { it.xfermode = PorterDuffXfermode(PorterDuff.Mode.DST_OUT) },
+ PORTER_DUFF_XFER_MODE_PAINT,
)
canvas.restore()
canvas.restore()
@@ -403,7 +387,7 @@
// translation of reference point of text
// used for translation when calling textInterpolator
- fun getLocalTranslation(): Point {
+ private fun getLocalTranslation(): Point {
val viewHeight = if (isVertical) measuredWidth else measuredHeight
val interpolatedTextBounds = updateInterpolatedTextBounds()
val localTranslation = Point(0, 0)
@@ -429,17 +413,18 @@
correctedBaseline
}
VerticalAlignment.BASELINE -> {
- localTranslation.y = -lockScreenPaint.strokeWidth.toInt()
+ // account for max bottom distance of font, so clock doesn't collide with elements
+ localTranslation.y =
+ -lockScreenPaint.strokeWidth.toInt() - paint.fontMetrics.descent.toInt()
}
}
return updateXtranslation(localTranslation, interpolatedTextBounds)
}
- override fun applyStyles(assets: AssetLoader, textStyle: TextStyle, aodStyle: TextStyle?) {
+ override fun applyStyles(textStyle: TextStyle, aodStyle: TextStyle?) {
this.textStyle = textStyle as FontTextStyle
val typefaceName = "fonts/" + textStyle.fontFamily
- setTypefaceCache(assets.typefaceCache.getVariantCache(typefaceName))
lockScreenPaint.strokeJoin = Paint.Join.ROUND
lockScreenPaint.typeface = typefaceCache.getTypefaceForVariant(lsFontVariation)
textStyle.fontFeatureSettings?.let {
@@ -550,7 +535,9 @@
}
companion object {
- val AOD_STROKE_WIDTH = "2dp"
+ private val PORTER_DUFF_XFER_MODE_PAINT =
+ Paint().also { it.xfermode = PorterDuffXfermode(PorterDuff.Mode.DST_OUT) }
+
val AOD_COLOR = Color.WHITE
val OPTICAL_SIZE_AXIS = ClockFontAxisSetting("opsz", 144f)
val DEFAULT_LS_VARIATION =
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockView.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockView.kt
index 3d2ed4a1..e8be28f 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockView.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockView.kt
@@ -18,7 +18,6 @@
import androidx.annotation.VisibleForTesting
import com.android.systemui.plugins.clocks.ClockFontAxisSetting
-import com.android.systemui.shared.clocks.AssetLoader
import com.android.systemui.shared.clocks.TextStyle
interface SimpleDigitalClockView {
@@ -29,7 +28,7 @@
val textStyle: TextStyle
@VisibleForTesting var isAnimationEnabled: Boolean
- fun applyStyles(assets: AssetLoader, textStyle: TextStyle, aodStyle: TextStyle?)
+ fun applyStyles(textStyle: TextStyle, aodStyle: TextStyle?)
fun applyTextSize(targetFontSizePx: Float?, constrainedByHeight: Boolean = false)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardDisplayManagerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardDisplayManagerTest.kt
index 57a6797..85bdf92 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardDisplayManagerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardDisplayManagerTest.kt
@@ -28,7 +28,7 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.navigationbar.NavigationBarController
import com.android.systemui.settings.FakeDisplayTracker
-import com.android.systemui.shade.data.repository.FakeShadePositionRepository
+import com.android.systemui.shade.data.repository.FakeShadeDisplayRepository
import com.android.systemui.statusbar.policy.KeyguardStateController
import java.util.concurrent.Executor
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -62,7 +62,7 @@
mock(ConnectedDisplayKeyguardPresentation::class.java)
@Mock private val deviceStateHelper = mock(DeviceStateHelper::class.java)
@Mock private val keyguardStateController = mock(KeyguardStateController::class.java)
- private val shadePositionRepository = FakeShadePositionRepository()
+ private val shadePositionRepository = FakeShadeDisplayRepository()
private val mainExecutor = Executor { it.run() }
private val backgroundExecutor = Executor { it.run() }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/BatteryMeterDrawableTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/BatteryMeterDrawableTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/BatteryMeterDrawableTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/BatteryMeterDrawableTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/BootCompleteCacheTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/BootCompleteCacheTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/BootCompleteCacheTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/BootCompleteCacheTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/CameraAvailabilityListenerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/CameraAvailabilityListenerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/CameraAvailabilityListenerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/CameraAvailabilityListenerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/FaceScanningProviderFactoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/FaceScanningProviderFactoryTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/FaceScanningProviderFactoryTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/FaceScanningProviderFactoryTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/FakeCameraProtectionLoader.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/FakeCameraProtectionLoader.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/FakeCameraProtectionLoader.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/FakeCameraProtectionLoader.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorHwcLayerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/ScreenDecorHwcLayerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/ScreenDecorHwcLayerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/ScreenDecorHwcLayerTest.kt
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryTest.kt
index 72163e4..f82c8b0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryTest.kt
@@ -31,7 +31,8 @@
import com.android.systemui.coroutines.collectValues
import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.kosmos.testScope
-import com.android.systemui.statusbar.pipeline.mobile.data.repository.fakeMobileConnectionsRepository
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.fake
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.mobileConnectionsRepository
import com.android.systemui.testKosmos
import com.android.systemui.user.data.repository.FakeUserRepository
import com.android.systemui.util.mockito.whenever
@@ -62,7 +63,7 @@
private val testScope = kosmos.testScope
private val clock = FakeSystemClock()
private val userRepository = FakeUserRepository()
- private val mobileConnectionsRepository = kosmos.fakeMobileConnectionsRepository
+ private val mobileConnectionsRepository = kosmos.mobileConnectionsRepository
private lateinit var underTest: AuthenticationRepository
@@ -110,7 +111,7 @@
.isEqualTo(AuthenticationMethodModel.None)
currentSecurityMode = KeyguardSecurityModel.SecurityMode.SimPin
- mobileConnectionsRepository.isAnySimSecure.value = true
+ mobileConnectionsRepository.fake.isAnySimSecure.value = true
assertThat(authMethod).isEqualTo(AuthenticationMethodModel.Sim)
assertThat(underTest.getAuthenticationMethod()).isEqualTo(AuthenticationMethodModel.Sim)
}
@@ -199,7 +200,7 @@
}
private fun setSecurityModeAndDispatchBroadcast(
- securityMode: KeyguardSecurityModel.SecurityMode,
+ securityMode: KeyguardSecurityModel.SecurityMode
) {
currentSecurityMode = securityMode
dispatchBroadcast()
@@ -208,23 +209,15 @@
private fun dispatchBroadcast() {
fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(
context,
- Intent(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED)
+ Intent(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED),
)
}
companion object {
private val USER_INFOS =
listOf(
- UserInfo(
- /* id= */ 100,
- /* name= */ "First user",
- /* flags= */ 0,
- ),
- UserInfo(
- /* id= */ 101,
- /* name= */ "Second user",
- /* flags= */ 0,
- ),
+ UserInfo(/* id= */ 100, /* name= */ "First user", /* flags= */ 0),
+ UserInfo(/* id= */ 101, /* name= */ "Second user", /* flags= */ 0),
)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractorTest.kt
index 13f2c72..4e64c50 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractorTest.kt
@@ -17,22 +17,23 @@
package com.android.systemui.biometrics.domain.interactor
import android.graphics.Rect
-import android.hardware.fingerprint.FingerprintManager
import android.view.MotionEvent
import android.view.Surface
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.AuthController
+import com.android.systemui.biometrics.authController
import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams
import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.user.domain.interactor.SelectedUserInteractor
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.res.R
+import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
-import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@@ -44,29 +45,25 @@
import org.mockito.Mockito.`when` as whenever
import org.mockito.junit.MockitoJUnit
+@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(AndroidJUnit4::class)
class UdfpsOverlayInteractorTest : SysuiTestCase() {
@JvmField @Rule var mockitoRule = MockitoJUnit.rule()
- private lateinit var testScope: TestScope
+ private val kosmos = testKosmos()
- @Mock private lateinit var fingerprintManager: FingerprintManager
- @Mock private lateinit var authController: AuthController
+ private val testScope: TestScope = kosmos.testScope
+
+ private val authController: AuthController = kosmos.authController
@Captor private lateinit var authControllerCallback: ArgumentCaptor<AuthController.Callback>
@Mock private lateinit var udfpsOverlayParams: UdfpsOverlayParams
@Mock private lateinit var overlayBounds: Rect
- @Mock private lateinit var selectedUserInteractor: SelectedUserInteractor
private lateinit var underTest: UdfpsOverlayInteractor
- @Before
- fun setUp() {
- testScope = TestScope(StandardTestDispatcher())
- }
-
@Test
fun testShouldInterceptTouch() =
testScope.runTest {
@@ -107,15 +104,26 @@
assertThat(udfpsOverlayParams()).isEqualTo(firstParams)
}
+ @Test
+ fun testPaddingIsNeverNegative() =
+ testScope.runTest {
+ context.orCreateTestableResources.addOverride(R.dimen.pixel_pitch, 60.0f)
+ createUdfpsOverlayInteractor()
+ val padding by collectLastValue(underTest.iconPadding)
+ runCurrent()
+
+ verify(authController).addCallback(authControllerCallback.capture())
+
+ // Simulate the first empty udfps overlay params value.
+ authControllerCallback.value.onUdfpsLocationChanged(UdfpsOverlayParams())
+ runCurrent()
+
+ assertThat(padding).isEqualTo(0)
+ context.orCreateTestableResources.removeOverride(R.dimen.pixel_pitch)
+ }
+
private fun createUdfpsOverlayInteractor() {
- underTest =
- UdfpsOverlayInteractor(
- context,
- authController,
- selectedUserInteractor,
- fingerprintManager,
- testScope.backgroundScope
- )
+ underTest = kosmos.udfpsOverlayInteractor
testScope.runCurrent()
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingDialogViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingDialogViewModelTest.kt
similarity index 98%
rename from packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingDialogViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingDialogViewModelTest.kt
index beb816c..32606e0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingDialogViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingDialogViewModelTest.kt
@@ -17,8 +17,8 @@
package com.android.systemui.bluetooth.qsdialog
import android.bluetooth.BluetoothDevice
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.settingslib.bluetooth.LeAudioProfile
import com.android.settingslib.bluetooth.LocalBluetoothProfileManager
@@ -48,7 +48,7 @@
import org.mockito.kotlin.whenever
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
@OptIn(ExperimentalCoroutinesApi::class)
class AudioSharingDialogViewModelTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingInteractorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingInteractorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingInteractorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingRepositoryTest.kt
similarity index 98%
rename from packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingRepositoryTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingRepositoryTest.kt
index c9e8813..acfe9dd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingRepositoryTest.kt
@@ -17,8 +17,8 @@
import android.bluetooth.BluetoothDevice
import android.bluetooth.BluetoothLeBroadcastMetadata
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant
@@ -46,7 +46,7 @@
@ExperimentalCoroutinesApi
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
class AudioSharingRepositoryTest : SysuiTestCase() {
@get:Rule val mockitoRule: MockitoRule = MockitoJUnit.rule()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothStateInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bluetooth/qsdialog/BluetoothStateInteractorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothStateInteractorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/bluetooth/qsdialog/BluetoothStateInteractorTest.kt
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractorTest.kt
index 566cd70..10bf523 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractorTest.kt
@@ -35,7 +35,8 @@
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository
-import com.android.systemui.statusbar.pipeline.mobile.data.repository.fakeMobileConnectionsRepository
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.fake
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.mobileConnectionsRepository
import com.android.systemui.telephony.data.repository.fakeTelephonyRepository
import com.android.systemui.testKosmos
import com.android.systemui.user.domain.interactor.SelectedUserInteractor
@@ -78,7 +79,7 @@
fun setUp() {
MockitoAnnotations.initMocks(this)
- mobileConnectionsRepository = kosmos.fakeMobileConnectionsRepository
+ mobileConnectionsRepository = kosmos.mobileConnectionsRepository.fake
overrideResource(R.string.lockscreen_emergency_call, MESSAGE_EMERGENCY_CALL)
overrideResource(R.string.lockscreen_return_to_call, MESSAGE_RETURN_TO_CALL)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/brightness/ui/viewmodel/BrightnessSliderViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/brightness/ui/viewmodel/BrightnessSliderViewModelTest.kt
index 7cdfb0e..0f148f8 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/brightness/ui/viewmodel/BrightnessSliderViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/brightness/ui/viewmodel/BrightnessSliderViewModelTest.kt
@@ -30,11 +30,11 @@
import com.android.systemui.common.shared.model.Text
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.haptics.slider.sliderHapticsViewModelFactory
-import com.android.systemui.kosmos.brightnessWarningToast
import com.android.systemui.kosmos.testScope
import com.android.systemui.lifecycle.activateIn
import com.android.systemui.res.R
import com.android.systemui.settings.brightness.domain.interactor.brightnessMirrorShowingInteractor
+import com.android.systemui.settings.brightness.ui.brightnessWarningToast
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/CommunalTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/CommunalTransitionViewModelTest.kt
index baeb2dd..0084e18 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/CommunalTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/CommunalTransitionViewModelTest.kt
@@ -16,38 +16,64 @@
package com.android.systemui.communal.ui.viewmodel
-import androidx.test.ext.junit.runners.AndroidJUnit4
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.FlagsParameterization
import androidx.test.filters.SmallTest
+import com.android.compose.animation.scene.ObservableTransitionState
+import com.android.compose.animation.scene.SceneKey
+import com.android.systemui.Flags.FLAG_SCENE_CONTAINER
import com.android.systemui.SysuiTestCase
import com.android.systemui.communal.data.repository.fakeCommunalSceneRepository
import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.flags.andSceneContainer
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.kosmos.testScope
+import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@RunWith(AndroidJUnit4::class)
-class CommunalTransitionViewModelTest : SysuiTestCase() {
+@RunWith(ParameterizedAndroidJunit4::class)
+class CommunalTransitionViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {
+
+ companion object {
+ @JvmStatic
+ @Parameters(name = "{0}")
+ fun getParams(): List<FlagsParameterization> {
+ return FlagsParameterization.allCombinationsOf().andSceneContainer()
+ }
+ }
+
+ init {
+ mSetFlagsRule.setFlagsParameterization(flags)
+ }
+
private val kosmos = testKosmos()
private val testScope = kosmos.testScope
private val keyguardTransitionRepository = kosmos.fakeKeyguardTransitionRepository
private val communalSceneRepository = kosmos.fakeCommunalSceneRepository
+ private val sceneInteractor = kosmos.sceneInteractor
private val underTest: CommunalTransitionViewModel by lazy {
kosmos.communalTransitionViewModel
}
+ @DisableFlags(FLAG_SCENE_CONTAINER)
@Test
fun testIsUmoOnCommunalDuringTransitionBetweenLockscreenAndGlanceableHub() =
testScope.runTest {
@@ -61,6 +87,7 @@
assertThat(isUmoOnCommunal).isFalse()
}
+ @DisableFlags(FLAG_SCENE_CONTAINER)
@Test
fun testIsUmoOnCommunalDuringTransitionBetweenDreamingAndGlanceableHub() =
testScope.runTest {
@@ -74,6 +101,7 @@
assertThat(isUmoOnCommunal).isFalse()
}
+ @DisableFlags(FLAG_SCENE_CONTAINER)
@Test
fun testIsUmoOnCommunalDuringTransitionBetweenOccludedAndGlanceableHub() =
testScope.runTest {
@@ -87,6 +115,7 @@
assertThat(isUmoOnCommunal).isFalse()
}
+ @DisableFlags(FLAG_SCENE_CONTAINER)
@Test
fun isUmoOnCommunal_noLongerVisible_returnsFalse() =
testScope.runTest {
@@ -103,6 +132,7 @@
assertThat(isUmoOnCommunal).isFalse()
}
+ @DisableFlags(FLAG_SCENE_CONTAINER)
@Test
fun isUmoOnCommunal_idleOnCommunal_returnsTrue() =
testScope.runTest {
@@ -116,11 +146,25 @@
assertThat(isUmoOnCommunal).isTrue()
}
+ @EnableFlags(FLAG_SCENE_CONTAINER)
+ @Test
+ fun isUmoOnCommunal_sceneContainerEnabled_idleOnCommunal_returnsTrue() =
+ testScope.runTest {
+ val isUmoOnCommunal by collectLastValue(underTest.isUmoOnCommunal)
+ assertThat(isUmoOnCommunal).isFalse()
+
+ // Change to communal scene.
+ setIdleScene(Scenes.Communal)
+
+ // isUmoOnCommunal returns true, even without any keyguard transition.
+ assertThat(isUmoOnCommunal).isTrue()
+ }
+
private suspend fun TestScope.enterCommunal(from: KeyguardState) {
keyguardTransitionRepository.sendTransitionSteps(
from = from,
to = KeyguardState.GLANCEABLE_HUB,
- testScope
+ testScope,
)
communalSceneRepository.changeScene(CommunalScenes.Communal)
runCurrent()
@@ -130,9 +174,17 @@
keyguardTransitionRepository.sendTransitionSteps(
from = KeyguardState.GLANCEABLE_HUB,
to = to,
- testScope
+ testScope,
)
communalSceneRepository.changeScene(CommunalScenes.Blank)
runCurrent()
}
+
+ private fun setIdleScene(scene: SceneKey) {
+ sceneInteractor.changeScene(scene, "test")
+ val transitionState =
+ MutableStateFlow<ObservableTransitionState>(ObservableTransitionState.Idle(scene))
+ sceneInteractor.setTransitionState(transitionState)
+ testScope.runCurrent()
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt
index 6b851cb..5bbd3ff 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt
@@ -56,6 +56,7 @@
import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.kosmos.testScope
import com.android.systemui.log.logcatLogBuffer
+import com.android.systemui.media.controls.ui.controller.mediaCarouselController
import com.android.systemui.media.controls.ui.view.MediaHost
import com.android.systemui.settings.fakeUserTracker
import com.android.systemui.testKosmos
@@ -133,6 +134,7 @@
accessibilityManager,
packageManager,
WIDGET_PICKER_PACKAGE_NAME,
+ kosmos.mediaCarouselController,
)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt
index 09daa51..3eba8ff 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt
@@ -69,6 +69,7 @@
import com.android.systemui.kosmos.testScope
import com.android.systemui.log.logcatLogBuffer
import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager
+import com.android.systemui.media.controls.ui.controller.mediaCarouselController
import com.android.systemui.media.controls.ui.view.MediaHost
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest
import com.android.systemui.power.domain.interactor.powerInteractor
@@ -178,6 +179,7 @@
mediaHost,
logcatLogBuffer("CommunalViewModelTest"),
metricsLogger,
+ kosmos.mediaCarouselController,
)
}
@@ -627,10 +629,7 @@
kosmos.setTransition(
sceneTransition = Idle(Scenes.Communal),
stateTransition =
- TransitionStep(
- from = KeyguardState.DREAMING,
- to = KeyguardState.GLANCEABLE_HUB
- ),
+ TransitionStep(from = KeyguardState.DREAMING, to = KeyguardState.GLANCEABLE_HUB),
)
// Then flow is not frozen
@@ -647,10 +646,7 @@
kosmos.setTransition(
sceneTransition = Idle(Scenes.Lockscreen),
stateTransition =
- TransitionStep(
- from = KeyguardState.GLANCEABLE_HUB,
- to = KeyguardState.OCCLUDED
- ),
+ TransitionStep(from = KeyguardState.GLANCEABLE_HUB, to = KeyguardState.OCCLUDED),
)
// Then flow is not frozen
@@ -735,10 +731,7 @@
kosmos.setTransition(
sceneTransition = Idle(Scenes.Communal),
stateTransition =
- TransitionStep(
- from = KeyguardState.DREAMING,
- to = KeyguardState.GLANCEABLE_HUB
- ),
+ TransitionStep(from = KeyguardState.DREAMING, to = KeyguardState.GLANCEABLE_HUB),
)
// Widgets available
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/development/data/repository/DevelopmentSettingRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/development/data/repository/DevelopmentSettingRepositoryTest.kt
new file mode 100644
index 0000000..e17b66e
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/development/data/repository/DevelopmentSettingRepositoryTest.kt
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.development.data.repository
+
+import android.content.pm.UserInfo
+import android.os.Build
+import android.os.UserHandle
+import android.os.UserManager
+import android.os.userManager
+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.kosmos.Kosmos
+import com.android.systemui.kosmos.collectLastValue
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import com.android.systemui.util.settings.fakeGlobalSettings
+import com.google.common.truth.Truth.assertThat
+import kotlin.test.Test
+import kotlinx.coroutines.test.runTest
+import org.junit.runner.RunWith
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.stub
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class DevelopmentSettingRepositoryTest : SysuiTestCase() {
+
+ private val kosmos = testKosmos()
+
+ private val underTest = kosmos.developmentSettingRepository
+
+ @Test
+ fun nonAdminUser_unrestricted_neverDevelopmentEnabled() =
+ with(kosmos) {
+ testScope.runTest {
+ val userInfo = nonAdminUserInfo
+ val settingEnabled by
+ collectLastValue(underTest.isDevelopmentSettingEnabled(userInfo))
+
+ setUserRestriction(userInfo.userHandle, restricted = false)
+
+ assertThat(settingEnabled).isFalse()
+
+ setSettingValue(false)
+ assertThat(settingEnabled).isFalse()
+
+ setSettingValue(true)
+ assertThat(settingEnabled).isFalse()
+ }
+ }
+
+ @Test
+ fun nonAdminUser_restricted_neverDevelopmentEnabled() =
+ with(kosmos) {
+ testScope.runTest {
+ val userInfo = nonAdminUserInfo
+ val settingEnabled by
+ collectLastValue(underTest.isDevelopmentSettingEnabled(userInfo))
+
+ setUserRestriction(userInfo.userHandle, restricted = true)
+
+ assertThat(settingEnabled).isFalse()
+
+ setSettingValue(false)
+ assertThat(settingEnabled).isFalse()
+
+ setSettingValue(true)
+ assertThat(settingEnabled).isFalse()
+ }
+ }
+
+ @Test
+ fun adminUser_unrestricted_defaultValueOfSetting() =
+ with(kosmos) {
+ testScope.runTest {
+ val userInfo = adminUserInfo
+ val settingEnabled by
+ collectLastValue(underTest.isDevelopmentSettingEnabled(userInfo))
+
+ setUserRestriction(userInfo.userHandle, restricted = false)
+
+ val defaultValue = Build.TYPE == "eng"
+
+ assertThat(settingEnabled).isEqualTo(defaultValue)
+ }
+ }
+
+ @Test
+ fun adminUser_unrestricted_enabledTracksSetting() =
+ with(kosmos) {
+ testScope.runTest {
+ val userInfo = adminUserInfo
+ val settingEnabled by
+ collectLastValue(underTest.isDevelopmentSettingEnabled(userInfo))
+
+ setUserRestriction(userInfo.userHandle, restricted = false)
+
+ setSettingValue(false)
+ assertThat(settingEnabled).isFalse()
+
+ setSettingValue(true)
+ assertThat(settingEnabled).isTrue()
+ }
+ }
+
+ @Test
+ fun adminUser_restricted_neverDevelopmentEnabled() =
+ with(kosmos) {
+ testScope.runTest {
+ val userInfo = adminUserInfo
+ val settingEnabled by
+ collectLastValue(underTest.isDevelopmentSettingEnabled(userInfo))
+
+ setUserRestriction(userInfo.userHandle, restricted = true)
+
+ assertThat(settingEnabled).isFalse()
+
+ setSettingValue(false)
+ assertThat(settingEnabled).isFalse()
+
+ setSettingValue(true)
+ assertThat(settingEnabled).isFalse()
+ }
+ }
+
+ private companion object {
+ const val USER_RESTRICTION = UserManager.DISALLOW_DEBUGGING_FEATURES
+ const val SETTING_NAME = Settings.Global.DEVELOPMENT_SETTINGS_ENABLED
+
+ val adminUserInfo =
+ UserInfo(
+ /* id= */ 10,
+ /* name= */ "",
+ /* flags */ UserInfo.FLAG_ADMIN or UserInfo.FLAG_FULL,
+ )
+ val nonAdminUserInfo =
+ UserInfo(/* id= */ 11, /* name= */ "", /* flags */ UserInfo.FLAG_FULL)
+
+ fun Kosmos.setUserRestriction(userHandle: UserHandle, restricted: Boolean) {
+ userManager.stub {
+ on { hasUserRestrictionForUser(eq(USER_RESTRICTION), eq(userHandle)) } doReturn
+ restricted
+ }
+ }
+
+ fun Kosmos.setSettingValue(enabled: Boolean) {
+ fakeGlobalSettings.putInt(SETTING_NAME, if (enabled) 1 else 0)
+ }
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/development/domain/interactor/BuildNumberInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/development/domain/interactor/BuildNumberInteractorTest.kt
new file mode 100644
index 0000000..f29dabe
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/development/domain/interactor/BuildNumberInteractorTest.kt
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.development.domain.interactor
+
+import android.content.ClipData
+import android.content.ClipDescription
+import android.content.clipboardManager
+import android.content.pm.UserInfo
+import android.content.res.mainResources
+import android.os.Build
+import android.provider.Settings
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.internal.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.development.shared.model.BuildNumber
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.collectLastValue
+import com.android.systemui.kosmos.runCurrent
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import com.android.systemui.user.data.repository.fakeUserRepository
+import com.android.systemui.util.settings.fakeGlobalSettings
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.argumentCaptor
+import org.mockito.kotlin.verify
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class BuildNumberInteractorTest : SysuiTestCase() {
+
+ private val kosmos =
+ testKosmos().apply {
+ fakeUserRepository.setUserInfos(listOf(adminUserInfo, nonAdminUserInfo))
+ }
+
+ private val expectedBuildNumber =
+ BuildNumber(
+ kosmos.mainResources.getString(
+ R.string.bugreport_status,
+ Build.VERSION.RELEASE_OR_CODENAME,
+ Build.ID,
+ )
+ )
+
+ private val clipLabel =
+ kosmos.mainResources.getString(
+ com.android.systemui.res.R.string.build_number_clip_data_label
+ )
+
+ private val underTest = kosmos.buildNumberInteractor
+
+ @Test
+ fun nonAdminUser_settingEnabled_buildNumberNull() =
+ with(kosmos) {
+ testScope.runTest {
+ val buildNumber by collectLastValue(underTest.buildNumber)
+
+ fakeUserRepository.setSelectedUserInfo(nonAdminUserInfo)
+ setSettingValue(true)
+
+ assertThat(buildNumber).isNull()
+ }
+ }
+
+ @Test
+ fun adminUser_buildNumberCorrect_onlyWhenSettingEnabled() =
+ with(kosmos) {
+ testScope.runTest {
+ val buildNumber by collectLastValue(underTest.buildNumber)
+
+ fakeUserRepository.setSelectedUserInfo(adminUserInfo)
+
+ setSettingValue(false)
+ assertThat(buildNumber).isNull()
+
+ setSettingValue(true)
+ assertThat(buildNumber).isEqualTo(expectedBuildNumber)
+ }
+ }
+
+ @Test
+ fun copyToClipboard() =
+ with(kosmos) {
+ testScope.runTest {
+ fakeUserRepository.setSelectedUserInfo(adminUserInfo)
+
+ underTest.copyBuildNumber()
+ runCurrent()
+
+ val argumentCaptor = argumentCaptor<ClipData>()
+
+ verify(clipboardManager).setPrimaryClip(argumentCaptor.capture())
+
+ with(argumentCaptor.firstValue) {
+ assertThat(description.label).isEqualTo(clipLabel)
+ assertThat(description.hasMimeType(ClipDescription.MIMETYPE_TEXT_PLAIN))
+ .isTrue()
+ assertThat(itemCount).isEqualTo(1)
+ assertThat(getItemAt(0).text).isEqualTo(expectedBuildNumber.value)
+ }
+ }
+ }
+
+ private companion object {
+ const val SETTING_NAME = Settings.Global.DEVELOPMENT_SETTINGS_ENABLED
+
+ val adminUserInfo =
+ UserInfo(
+ /* id= */ 10,
+ /* name= */ "",
+ /* flags */ UserInfo.FLAG_ADMIN or UserInfo.FLAG_FULL,
+ )
+ val nonAdminUserInfo =
+ UserInfo(/* id= */ 11, /* name= */ "", /* flags */ UserInfo.FLAG_FULL)
+
+ fun Kosmos.setSettingValue(enabled: Boolean) {
+ fakeGlobalSettings.putInt(SETTING_NAME, if (enabled) 1 else 0)
+ }
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractorTest.kt
index f0d79bb..47cba07 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractorTest.kt
@@ -568,6 +568,41 @@
assertThat(isUnlocked).isFalse()
}
+ @Test
+ fun lockNow() =
+ testScope.runTest {
+ setLockAfterScreenTimeout(5000)
+ val isUnlocked by collectLastValue(underTest.deviceUnlockStatus.map { it.isUnlocked })
+ unlockDevice()
+ assertThat(isUnlocked).isTrue()
+
+ underTest.lockNow()
+ runCurrent()
+
+ assertThat(isUnlocked).isFalse()
+ }
+
+ @Test
+ fun deviceUnlockStatus_isResetToFalse_whenDeviceGoesToSleep_fromSleepButton() =
+ testScope.runTest {
+ setLockAfterScreenTimeout(5000)
+ kosmos.fakeAuthenticationRepository.powerButtonInstantlyLocks = false
+ val deviceUnlockStatus by collectLastValue(underTest.deviceUnlockStatus)
+
+ kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus(
+ SuccessFingerprintAuthenticationStatus(0, true)
+ )
+ runCurrent()
+ assertThat(deviceUnlockStatus?.isUnlocked).isTrue()
+
+ kosmos.powerInteractor.setAsleepForTest(
+ sleepReason = PowerManager.GO_TO_SLEEP_REASON_SLEEP_BUTTON
+ )
+ runCurrent()
+
+ assertThat(deviceUnlockStatus?.isUnlocked).isFalse()
+ }
+
private fun TestScope.unlockDevice() {
val deviceUnlockStatus by collectLastValue(underTest.deviceUnlockStatus)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/ui/viewmodel/UdfpsAccessibilityOverlayViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/ui/viewmodel/UdfpsAccessibilityOverlayViewModelTest.kt
index c39c3fe..2d54337 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/ui/viewmodel/UdfpsAccessibilityOverlayViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/ui/viewmodel/UdfpsAccessibilityOverlayViewModelTest.kt
@@ -36,6 +36,7 @@
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.keyguard.ui.viewmodel.fakeDeviceEntryIconViewModelTransition
import com.android.systemui.kosmos.testScope
+import com.android.systemui.res.R
import com.android.systemui.shade.shadeTestUtil
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
@@ -43,6 +44,7 @@
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
+import org.junit.After
import org.junit.Before
import org.junit.runner.RunWith
import platform.test.runner.parameterized.ParameterizedAndroidJunit4
@@ -84,6 +86,12 @@
@Before
fun setup() {
underTest = kosmos.deviceEntryUdfpsAccessibilityOverlayViewModel
+ overrideResource(R.integer.udfps_padding_debounce_duration, 0)
+ }
+
+ @After
+ fun teardown() {
+ mContext.orCreateTestableResources.removeOverride(R.integer.udfps_padding_debounce_duration)
}
@Test
@@ -118,6 +126,7 @@
runCurrent()
assertThat(visible).isFalse()
}
+
fun fpNotRunning_overlayNotVisible() =
testScope.runTest {
val visible by collectLastValue(underTest.visible)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DisplayScopeRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/DisplayScopeRepositoryImplTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DisplayScopeRepositoryImplTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/DisplayScopeRepositoryImplTest.kt
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/display/domain/interactor/RearDisplayStateInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/display/domain/interactor/RearDisplayStateInteractorTest.kt
new file mode 100644
index 0000000..7891787
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/display/domain/interactor/RearDisplayStateInteractorTest.kt
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.display.domain.interactor
+
+import android.hardware.display.defaultDisplay
+import android.hardware.display.rearDisplay
+import android.view.Display
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.display.data.repository.DeviceStateRepository
+import com.android.systemui.display.data.repository.FakeDeviceStateRepository
+import com.android.systemui.display.data.repository.FakeDisplayRepository
+import com.android.systemui.kosmos.runTest
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.channels.Channel
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.test.TestScope
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.whenever
+
+/** atest RearDisplayStateInteractorTest */
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class RearDisplayStateInteractorTest : SysuiTestCase() {
+
+ private val kosmos = testKosmos().useUnconfinedTestDispatcher()
+ private val fakeDisplayRepository = FakeDisplayRepository()
+ private val fakeDeviceStateRepository = FakeDeviceStateRepository()
+ private val rearDisplayStateInteractor =
+ RearDisplayStateInteractorImpl(
+ fakeDisplayRepository,
+ fakeDeviceStateRepository,
+ kosmos.testDispatcher,
+ )
+ private val emissionTracker = EmissionTracker(rearDisplayStateInteractor, kosmos.testScope)
+
+ @Before
+ fun setup() {
+ whenever(kosmos.rearDisplay.flags).thenReturn(Display.FLAG_REAR)
+ }
+
+ @Test
+ fun enableRearDisplayWhenDisplayImmediatelyAvailable() =
+ kosmos.runTest {
+ emissionTracker.use { tracker ->
+ fakeDisplayRepository.addDisplay(kosmos.rearDisplay)
+ assertThat(tracker.enabledCount).isEqualTo(0)
+ fakeDeviceStateRepository.emit(
+ DeviceStateRepository.DeviceState.REAR_DISPLAY_OUTER_DEFAULT
+ )
+
+ assertThat(tracker.enabledCount).isEqualTo(1)
+ assertThat(tracker.lastDisplay).isEqualTo(kosmos.rearDisplay)
+ }
+ }
+
+ @Test
+ fun enableAndDisableRearDisplay() =
+ kosmos.runTest {
+ emissionTracker.use { tracker ->
+ // The fake FakeDeviceStateRepository will always start with state UNKNOWN, thus
+ // triggering one initial emission
+ assertThat(tracker.disabledCount).isEqualTo(1)
+
+ fakeDeviceStateRepository.emit(
+ DeviceStateRepository.DeviceState.REAR_DISPLAY_OUTER_DEFAULT
+ )
+
+ // Adding a non-rear display does not trigger an emission
+ fakeDisplayRepository.addDisplay(kosmos.defaultDisplay)
+ assertThat(tracker.enabledCount).isEqualTo(0)
+
+ // Adding a rear display triggers the emission
+ fakeDisplayRepository.addDisplay(kosmos.rearDisplay)
+ assertThat(tracker.enabledCount).isEqualTo(1)
+ assertThat(tracker.lastDisplay).isEqualTo(kosmos.rearDisplay)
+
+ fakeDeviceStateRepository.emit(DeviceStateRepository.DeviceState.UNFOLDED)
+ assertThat(tracker.disabledCount).isEqualTo(2)
+ }
+ }
+
+ @Test
+ fun enableRearDisplayShouldOnlyReactToFirstRearDisplay() =
+ kosmos.runTest {
+ emissionTracker.use { tracker ->
+ fakeDeviceStateRepository.emit(
+ DeviceStateRepository.DeviceState.REAR_DISPLAY_OUTER_DEFAULT
+ )
+
+ // Adding a rear display triggers the emission
+ fakeDisplayRepository.addDisplay(kosmos.rearDisplay)
+ assertThat(tracker.enabledCount).isEqualTo(1)
+
+ // Adding additional rear displays does not trigger additional emissions
+ fakeDisplayRepository.addDisplay(kosmos.rearDisplay)
+ assertThat(tracker.enabledCount).isEqualTo(1)
+ }
+ }
+
+ @Test
+ fun rearDisplayAddedWhenNoLongerInRdm() =
+ kosmos.runTest {
+ emissionTracker.use { tracker ->
+ fakeDeviceStateRepository.emit(
+ DeviceStateRepository.DeviceState.REAR_DISPLAY_OUTER_DEFAULT
+ )
+ fakeDeviceStateRepository.emit(DeviceStateRepository.DeviceState.UNFOLDED)
+
+ // Adding a rear display when no longer in the correct device state does not trigger
+ // an emission
+ fakeDisplayRepository.addDisplay(kosmos.rearDisplay)
+ assertThat(tracker.enabledCount).isEqualTo(0)
+ }
+ }
+
+ @Test
+ fun rearDisplayDisabledDoesNotSpam() =
+ kosmos.runTest {
+ emissionTracker.use { tracker ->
+ fakeDeviceStateRepository.emit(DeviceStateRepository.DeviceState.UNFOLDED)
+ assertThat(tracker.disabledCount).isEqualTo(1)
+
+ // No additional emission
+ fakeDeviceStateRepository.emit(DeviceStateRepository.DeviceState.FOLDED)
+ assertThat(tracker.disabledCount).isEqualTo(1)
+ }
+ }
+
+ class EmissionTracker(rearDisplayInteractor: RearDisplayStateInteractor, scope: TestScope) :
+ AutoCloseable {
+ var enabledCount = 0
+ var disabledCount = 0
+ var lastDisplay: Display? = null
+
+ val job: Job
+
+ init {
+ val channel = Channel<RearDisplayStateInteractor.State>(Channel.UNLIMITED)
+ job =
+ scope.launch {
+ rearDisplayInteractor.state.collect {
+ channel.send(it)
+ if (it is RearDisplayStateInteractor.State.Enabled) {
+ enabledCount++
+ lastDisplay = it.innerDisplay
+ }
+ if (it is RearDisplayStateInteractor.State.Disabled) {
+ disabledCount++
+ }
+ }
+ }
+ }
+
+ override fun close() {
+ job.cancel()
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/doze/DozeMachineTest.java
similarity index 99%
rename from packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/doze/DozeMachineTest.java
index eb72f29..3347180 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/doze/DozeMachineTest.java
@@ -48,10 +48,10 @@
import android.app.ActivityManager;
import android.content.res.Configuration;
import android.hardware.display.AmbientDisplayConfiguration;
-import androidx.test.annotation.UiThreadTest;
import android.view.Display;
import androidx.annotation.NonNull;
+import androidx.test.annotation.UiThreadTest;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt
index 8062358..a65e7ed 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt
@@ -19,7 +19,9 @@
import android.content.ComponentName
import android.content.Intent
import android.os.RemoteException
+import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.FlagsParameterization
import android.service.dreams.Flags
import android.service.dreams.IDreamOverlay
import android.service.dreams.IDreamOverlayCallback
@@ -33,7 +35,6 @@
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.LifecycleRegistry
-import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.app.viewcapture.ViewCapture
import com.android.app.viewcapture.ViewCaptureAwareWindowManager
@@ -43,6 +44,7 @@
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.systemui.Flags.FLAG_COMMUNAL_HUB
+import com.android.systemui.Flags.FLAG_SCENE_CONTAINER
import com.android.systemui.SysuiTestCase
import com.android.systemui.ambient.touch.TouchMonitor
import com.android.systemui.ambient.touch.dagger.AmbientTouchComponent
@@ -62,12 +64,16 @@
import com.android.systemui.complication.dagger.ComplicationComponent
import com.android.systemui.dreams.complication.HideComplicationTouchHandler
import com.android.systemui.dreams.dagger.DreamOverlayComponent
+import com.android.systemui.flags.andSceneContainer
import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
import com.android.systemui.keyguard.gesture.domain.gestureInteractor
import com.android.systemui.kosmos.testScope
import com.android.systemui.navigationbar.gestural.domain.GestureInteractor
import com.android.systemui.navigationbar.gestural.domain.TaskInfo
import com.android.systemui.navigationbar.gestural.domain.TaskMatcher
+import com.android.systemui.scene.data.repository.sceneContainerRepository
+import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.testKosmos
import com.android.systemui.touch.TouchInsetManager
import com.android.systemui.util.concurrency.FakeExecutor
@@ -98,12 +104,14 @@
import org.mockito.kotlin.times
import org.mockito.kotlin.verifyNoMoreInteractions
import org.mockito.kotlin.whenever
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@TestableLooper.RunWithLooper(setAsMainLooper = true)
-@RunWith(AndroidJUnit4::class)
-class DreamOverlayServiceTest : SysuiTestCase() {
+@RunWith(ParameterizedAndroidJunit4::class)
+class DreamOverlayServiceTest(flags: FlagsParameterization?) : SysuiTestCase() {
private val mFakeSystemClock = FakeSystemClock()
private val mMainExecutor = FakeExecutor(mFakeSystemClock)
private val kosmos = testKosmos()
@@ -245,6 +253,10 @@
)
}
+ init {
+ mSetFlagsRule.setFlagsParameterization(flags!!)
+ }
+
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
@@ -287,6 +299,7 @@
mKeyguardUpdateMonitor,
mScrimManager,
mCommunalInteractor,
+ kosmos.sceneInteractor,
mSystemDialogsCloser,
mUiEventLogger,
mTouchInsetManager,
@@ -768,6 +781,7 @@
@Test
@EnableFlags(Flags.FLAG_DREAM_WAKE_REDIRECT, FLAG_COMMUNAL_HUB)
+ @DisableFlags(FLAG_SCENE_CONTAINER)
@kotlin.Throws(RemoteException::class)
fun testTransitionToGlanceableHub() =
testScope.runTest {
@@ -793,6 +807,35 @@
}
@Test
+ @EnableFlags(Flags.FLAG_DREAM_WAKE_REDIRECT, FLAG_SCENE_CONTAINER, FLAG_COMMUNAL_HUB)
+ @kotlin.Throws(RemoteException::class)
+ fun testTransitionToGlanceableHub_sceneContainer() =
+ testScope.runTest {
+ // Inform the overlay service of dream starting. Do not show dream complications.
+ client.startDream(
+ mWindowParams,
+ mDreamOverlayCallback,
+ DREAM_COMPONENT,
+ false /*isPreview*/,
+ false, /*shouldShowComplication*/
+ )
+ mMainExecutor.runAllReady()
+
+ verify(mDreamOverlayCallback).onRedirectWake(false)
+ clearInvocations(mDreamOverlayCallback)
+ kosmos.setCommunalAvailable(true)
+ mMainExecutor.runAllReady()
+ runCurrent()
+ verify(mDreamOverlayCallback).onRedirectWake(true)
+ client.onWakeRequested()
+ mMainExecutor.runAllReady()
+ runCurrent()
+ assertThat(kosmos.sceneContainerRepository.currentScene.value)
+ .isEqualTo(Scenes.Communal)
+ verify(mUiEventLogger).log(CommunalUiEvent.DREAM_TO_COMMUNAL_HUB_DREAM_AWAKE_START)
+ }
+
+ @Test
@EnableFlags(Flags.FLAG_DREAM_WAKE_REDIRECT, FLAG_COMMUNAL_HUB)
@Throws(RemoteException::class)
fun testRedirectExit() =
@@ -911,6 +954,7 @@
// Verifies that the touch handling lifecycle is STARTED even if the dream starts while not
// focused.
@Test
+ @DisableFlags(FLAG_SCENE_CONTAINER)
fun testLifecycle_dreamNotFocusedOnStart_isStarted() {
val transitionState: MutableStateFlow<ObservableTransitionState> =
MutableStateFlow(ObservableTransitionState.Idle(CommunalScenes.Blank))
@@ -1024,6 +1068,7 @@
}
@Test
+ @DisableFlags(FLAG_SCENE_CONTAINER)
fun testCommunalVisible_setsLifecycleState() {
val client = client
@@ -1060,6 +1105,7 @@
// Verifies the dream's lifecycle
@Test
+ @DisableFlags(FLAG_SCENE_CONTAINER)
fun testLifecycleStarted_whenAnyOcclusion() {
val client = client
@@ -1256,5 +1302,11 @@
ComponentName("package", "homeControlPanel")
private const val DREAM_COMPONENT = "package/dream"
private const val WINDOW_NAME = "test"
+
+ @JvmStatic
+ @Parameters(name = "{0}")
+ fun getParams(): List<FlagsParameterization> {
+ return FlagsParameterization.allCombinationsOf(FLAG_COMMUNAL_HUB).andSceneContainer()
+ }
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/ui/viewmodel/DreamUserActionsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/ui/viewmodel/DreamUserActionsViewModelTest.kt
new file mode 100644
index 0000000..aacfaed
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/ui/viewmodel/DreamUserActionsViewModelTest.kt
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
+package com.android.systemui.dreams.ui.viewmodel
+
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.compose.animation.scene.Swipe
+import com.android.compose.animation.scene.UserActionResult
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor
+import com.android.systemui.flags.EnableSceneContainer
+import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository
+import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.lifecycle.activateIn
+import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest
+import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest
+import com.android.systemui.power.domain.interactor.powerInteractor
+import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.scene.shared.model.Overlays
+import com.android.systemui.scene.shared.model.SceneFamilies
+import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.scene.shared.model.TransitionKeys.ToSplitShade
+import com.android.systemui.shade.data.repository.fakeShadeRepository
+import com.android.systemui.shade.shared.flag.DualShade
+import com.android.systemui.shade.shared.model.ShadeMode
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlin.test.Test
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@EnableSceneContainer
+class DreamUserActionsViewModelTest : SysuiTestCase() {
+
+ private val kosmos = testKosmos()
+ private val testScope = kosmos.testScope
+
+ private lateinit var underTest: DreamUserActionsViewModel
+
+ @Before
+ fun setUp() {
+ underTest = kosmos.dreamUserActionsViewModel
+ underTest.activateIn(testScope)
+ }
+
+ @Test
+ @DisableFlags(DualShade.FLAG_NAME)
+ fun actions_singleShade() =
+ testScope.runTest {
+ val actions by collectLastValue(underTest.actions)
+
+ setUpState(
+ isShadeTouchable = true,
+ isDeviceUnlocked = false,
+ shadeMode = ShadeMode.Single,
+ )
+ assertThat(actions).isNotEmpty()
+ assertThat(actions?.get(Swipe.End)).isEqualTo(UserActionResult(SceneFamilies.Home))
+ assertThat(actions?.get(Swipe.Up)).isEqualTo(UserActionResult(Scenes.Bouncer))
+ assertThat(actions?.get(Swipe.Down))
+ .isEqualTo(UserActionResult(Scenes.Shade, isIrreversible = true))
+
+ setUpState(
+ isShadeTouchable = false,
+ isDeviceUnlocked = false,
+ shadeMode = ShadeMode.Single,
+ )
+ assertThat(actions).isEmpty()
+
+ setUpState(
+ isShadeTouchable = true,
+ isDeviceUnlocked = true,
+ shadeMode = ShadeMode.Single,
+ )
+ assertThat(actions).isNotEmpty()
+ assertThat(actions?.get(Swipe.End)).isEqualTo(UserActionResult(SceneFamilies.Home))
+ assertThat(actions?.get(Swipe.Up)).isEqualTo(UserActionResult(Scenes.Gone))
+ assertThat(actions?.get(Swipe.Down))
+ .isEqualTo(UserActionResult(Scenes.Shade, isIrreversible = true))
+ }
+
+ @Test
+ @DisableFlags(DualShade.FLAG_NAME)
+ fun actions_splitShade() =
+ testScope.runTest {
+ val actions by collectLastValue(underTest.actions)
+
+ setUpState(
+ isShadeTouchable = true,
+ isDeviceUnlocked = false,
+ shadeMode = ShadeMode.Split,
+ )
+ assertThat(actions).isNotEmpty()
+ assertThat(actions?.get(Swipe.End)).isEqualTo(UserActionResult(SceneFamilies.Home))
+ assertThat(actions?.get(Swipe.Up)).isEqualTo(UserActionResult(Scenes.Bouncer))
+ assertThat(actions?.get(Swipe.Down))
+ .isEqualTo(UserActionResult(Scenes.Shade, ToSplitShade, isIrreversible = true))
+
+ setUpState(
+ isShadeTouchable = false,
+ isDeviceUnlocked = false,
+ shadeMode = ShadeMode.Split,
+ )
+ assertThat(actions).isEmpty()
+
+ setUpState(
+ isShadeTouchable = true,
+ isDeviceUnlocked = true,
+ shadeMode = ShadeMode.Split,
+ )
+ assertThat(actions).isNotEmpty()
+ assertThat(actions?.get(Swipe.End)).isEqualTo(UserActionResult(SceneFamilies.Home))
+ assertThat(actions?.get(Swipe.Up)).isEqualTo(UserActionResult(Scenes.Gone))
+ assertThat(actions?.get(Swipe.Down))
+ .isEqualTo(UserActionResult(Scenes.Shade, ToSplitShade, isIrreversible = true))
+ }
+
+ @Test
+ @EnableFlags(DualShade.FLAG_NAME)
+ fun actions_dualShade() =
+ testScope.runTest {
+ val actions by collectLastValue(underTest.actions)
+
+ setUpState(
+ isShadeTouchable = true,
+ isDeviceUnlocked = false,
+ shadeMode = ShadeMode.Dual,
+ )
+ assertThat(actions).isNotEmpty()
+ assertThat(actions?.get(Swipe.End)).isEqualTo(UserActionResult(SceneFamilies.Home))
+ assertThat(actions?.get(Swipe.Up)).isEqualTo(UserActionResult(Scenes.Bouncer))
+ assertThat(actions?.get(Swipe.Down))
+ .isEqualTo(
+ UserActionResult.ShowOverlay(Overlays.NotificationsShade, isIrreversible = true)
+ )
+
+ setUpState(
+ isShadeTouchable = false,
+ isDeviceUnlocked = false,
+ shadeMode = ShadeMode.Dual,
+ )
+ assertThat(actions).isEmpty()
+
+ setUpState(isShadeTouchable = true, isDeviceUnlocked = true, shadeMode = ShadeMode.Dual)
+ assertThat(actions).isNotEmpty()
+ assertThat(actions?.get(Swipe.End)).isEqualTo(UserActionResult(SceneFamilies.Home))
+ assertThat(actions?.get(Swipe.Up)).isEqualTo(UserActionResult(Scenes.Gone))
+ assertThat(actions?.get(Swipe.Down))
+ .isEqualTo(
+ UserActionResult.ShowOverlay(Overlays.NotificationsShade, isIrreversible = true)
+ )
+ }
+
+ private fun TestScope.setUpState(
+ isShadeTouchable: Boolean,
+ isDeviceUnlocked: Boolean,
+ shadeMode: ShadeMode,
+ ) {
+ if (isShadeTouchable) {
+ kosmos.powerInteractor.setAwakeForTest()
+ } else {
+ kosmos.powerInteractor.setAsleepForTest()
+ }
+
+ if (isDeviceUnlocked) {
+ unlockDevice()
+ } else {
+ lockDevice()
+ }
+
+ if (shadeMode == ShadeMode.Dual) {
+ assertThat(DualShade.isEnabled).isTrue()
+ } else {
+ assertThat(DualShade.isEnabled).isFalse()
+ kosmos.fakeShadeRepository.setShadeLayoutWide(shadeMode == ShadeMode.Split)
+ }
+ runCurrent()
+ }
+
+ private fun TestScope.lockDevice() {
+ val deviceUnlockStatus by
+ collectLastValue(kosmos.deviceUnlockedInteractor.deviceUnlockStatus)
+
+ kosmos.fakeAuthenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
+ assertThat(deviceUnlockStatus?.isUnlocked).isFalse()
+ kosmos.sceneInteractor.changeScene(Scenes.Lockscreen, "reason")
+ runCurrent()
+ }
+
+ private fun TestScope.unlockDevice() {
+ val deviceUnlockStatus by
+ collectLastValue(kosmos.deviceUnlockedInteractor.deviceUnlockStatus)
+
+ kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus(
+ SuccessFingerprintAuthenticationStatus(0, true)
+ )
+ assertThat(deviceUnlockStatus?.isUnlocked).isTrue()
+ kosmos.sceneInteractor.changeScene(Scenes.Gone, "reason")
+ runCurrent()
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/msdl/qs/TileHapticsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/msdl/qs/TileHapticsViewModelTest.kt
index 5efb617..f6a6e54 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/msdl/qs/TileHapticsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/msdl/qs/TileHapticsViewModelTest.kt
@@ -119,22 +119,6 @@
}
@Test
- fun onLongClick_whenTileDoesNotHandleLongClick_playsFailureHaptics() =
- testScope.runTest {
- // WHEN the tile is long-clicked but the tile does not handle a long-click
- val state = QSTile.State().apply { handlesLongClick = false }
- qsTile.changeState(state)
- underTest.setTileInteractionState(
- TileHapticsViewModel.TileInteractionState.LONG_CLICKED
- )
- runCurrent()
-
- // THEN the failure token plays
- assertThat(msdlPlayer.latestTokenPlayed).isEqualTo(MSDLToken.FAILURE)
- assertThat(msdlPlayer.latestPropertiesPlayed).isNull()
- }
-
- @Test
fun whenLaunchingFromClick_doesNotPlayHaptics() =
testScope.runTest {
// WHEN the tile is clicked and its action state changes accordingly
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyboard/data/repository/KeyboardRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/data/repository/KeyboardRepositoryTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/keyboard/data/repository/KeyboardRepositoryTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/data/repository/KeyboardRepositoryTest.kt
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepositoryTest.kt
index c646d8f..27b8c59 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepositoryTest.kt
@@ -18,13 +18,17 @@
import android.content.Context
import android.content.Context.INPUT_SERVICE
-import android.hardware.input.InputGestureData
-import android.hardware.input.InputGestureData.createKeyTrigger
-import android.hardware.input.KeyGestureEvent
import android.hardware.input.fakeInputManager
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
-import android.view.KeyEvent
+import android.view.KeyEvent.KEYCODE_SLASH
+import android.view.KeyEvent.META_ALT_ON
+import android.view.KeyEvent.META_CAPS_LOCK_ON
+import android.view.KeyEvent.META_CTRL_ON
+import android.view.KeyEvent.META_FUNCTION_ON
+import android.view.KeyEvent.META_META_ON
+import android.view.KeyEvent.META_SHIFT_ON
+import android.view.KeyEvent.META_SYM_ON
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.hardware.input.Flags.FLAG_ENABLE_CUSTOMIZABLE_INPUT_GESTURES
@@ -32,17 +36,14 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.keyboard.shortcut.customShortcutCategoriesRepository
-import com.android.systemui.keyboard.shortcut.shared.model.Shortcut
-import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategory
-import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType
-import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.AppCategories
-import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.MultiTasking
-import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.System
-import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCommand
+import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.allCustomizableInputGesturesWithSimpleShortcutCombinations
+import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.customizableInputGestureWithUnknownKeyGestureType
+import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.expectedShortcutCategoriesWithSimpleShortcutCombination
+import com.android.systemui.keyboard.shortcut.shared.model.KeyCombination
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutKey
-import com.android.systemui.keyboard.shortcut.shared.model.ShortcutSubCategory
import com.android.systemui.keyboard.shortcut.shortcutHelperTestHelper
import com.android.systemui.kosmos.testScope
+import com.android.systemui.res.R
import com.android.systemui.settings.FakeUserTracker
import com.android.systemui.settings.userTracker
import com.android.systemui.testKosmos
@@ -83,7 +84,7 @@
whenever(
fakeInputManager.inputManager.getCustomInputGestures(/* filter= */ anyOrNull())
)
- .thenReturn(customizableInputGesturesWithSimpleShortcutCombinations)
+ .thenReturn(allCustomizableInputGesturesWithSimpleShortcutCombinations)
helper.toggle(deviceId = 123)
val categories by collectLastValue(repo.categories)
@@ -100,7 +101,7 @@
whenever(
fakeInputManager.inputManager.getCustomInputGestures(/* filter= */ anyOrNull())
)
- .thenReturn(customizableInputGesturesWithSimpleShortcutCombinations)
+ .thenReturn(allCustomizableInputGesturesWithSimpleShortcutCombinations)
helper.toggle(deviceId = 123)
val categories by collectLastValue(repo.categories)
@@ -125,167 +126,73 @@
}
}
- private fun simpleInputGestureData(
- keyCode: Int = KeyEvent.KEYCODE_A,
- modifiers: Int = KeyEvent.META_CTRL_ON or KeyEvent.META_ALT_ON,
- keyGestureType: Int,
- ): InputGestureData {
- val builder = InputGestureData.Builder()
- builder.setKeyGestureType(keyGestureType)
- builder.setTrigger(createKeyTrigger(keyCode, modifiers))
- return builder.build()
+ @Test
+ fun pressedKeys_isEmptyByDefault() {
+ testScope.runTest {
+ val pressedKeys by collectLastValue(repo.pressedKeys)
+ assertThat(pressedKeys).isEmpty()
+
+ helper.toggle(deviceId = 123)
+ assertThat(pressedKeys).isEmpty()
+ }
}
- private fun simpleShortcutCategory(
- category: ShortcutCategoryType,
- subcategoryLabel: String,
- shortcutLabel: String,
- ): ShortcutCategory {
- return ShortcutCategory(
- type = category,
- subCategories =
- listOf(
- ShortcutSubCategory(
- label = subcategoryLabel,
- shortcuts = listOf(simpleShortcut(shortcutLabel)),
- )
- ),
- )
+ @Test
+ fun pressedKeys_recognizesAllSupportedModifiers() {
+ testScope.runTest {
+ helper.toggle(deviceId = 123)
+ val pressedKeys by collectLastValue(repo.pressedKeys)
+ repo.updateUserKeyCombination(
+ KeyCombination(modifiers = allSupportedModifiers, keyCode = null)
+ )
+
+ assertThat(pressedKeys)
+ .containsExactly(
+ ShortcutKey.Icon.ResIdIcon(R.drawable.ic_ksh_key_meta),
+ ShortcutKey.Text("Ctrl"),
+ ShortcutKey.Text("Fn"),
+ ShortcutKey.Text("Shift"),
+ ShortcutKey.Text("Alt"),
+ ShortcutKey.Text("Sym"),
+ )
+ }
}
- private fun simpleShortcut(label: String) =
- Shortcut(
- label = label,
- commands =
- listOf(
- ShortcutCommand(
- isCustom = true,
- keys =
- listOf(
- ShortcutKey.Text("Ctrl"),
- ShortcutKey.Text("Alt"),
- ShortcutKey.Text("A"),
- ),
- )
- ),
- )
+ @Test
+ fun pressedKeys_ignoresUnsupportedModifiers() {
+ testScope.runTest {
+ helper.toggle(deviceId = 123)
+ val pressedKeys by collectLastValue(repo.pressedKeys)
+ repo.updateUserKeyCombination(
+ KeyCombination(modifiers = META_CAPS_LOCK_ON, keyCode = null)
+ )
- private val customizableInputGestureWithUnknownKeyGestureType =
- // These key gesture events are currently not supported by shortcut helper customizer
- listOf(
- simpleInputGestureData(
- keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_GLOBAL_ACTIONS
- ),
- simpleInputGestureData(keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_MEDIA_KEY),
- )
+ assertThat(pressedKeys).isEmpty()
+ }
+ }
- private val expectedShortcutCategoriesWithSimpleShortcutCombination =
- listOf(
- simpleShortcutCategory(System, "System apps", "Open assistant"),
- simpleShortcutCategory(System, "System controls", "Go to home screen"),
- simpleShortcutCategory(System, "System apps", "Open settings"),
- simpleShortcutCategory(System, "System controls", "Lock screen"),
- simpleShortcutCategory(System, "System controls", "View notifications"),
- simpleShortcutCategory(System, "System apps", "Take a note"),
- simpleShortcutCategory(System, "System controls", "Take screenshot"),
- simpleShortcutCategory(System, "System controls", "Go back"),
- simpleShortcutCategory(
- MultiTasking,
- "Split screen",
- "Switch from split screen to full screen",
- ),
- simpleShortcutCategory(
- MultiTasking,
- "Split screen",
- "Use split screen with current app on the left",
- ),
- simpleShortcutCategory(
- MultiTasking,
- "Split screen",
- "Switch to app on left or above while using split screen",
- ),
- simpleShortcutCategory(
- MultiTasking,
- "Split screen",
- "Use split screen with current app on the right",
- ),
- simpleShortcutCategory(
- MultiTasking,
- "Split screen",
- "Switch to app on right or below while using split screen",
- ),
- simpleShortcutCategory(System, "System controls", "Show shortcuts"),
- simpleShortcutCategory(System, "System controls", "View recent apps"),
- simpleShortcutCategory(AppCategories, "Applications", "Calculator"),
- simpleShortcutCategory(AppCategories, "Applications", "Calendar"),
- simpleShortcutCategory(AppCategories, "Applications", "Chrome"),
- simpleShortcutCategory(AppCategories, "Applications", "Contacts"),
- simpleShortcutCategory(AppCategories, "Applications", "Gmail"),
- simpleShortcutCategory(AppCategories, "Applications", "Maps"),
- simpleShortcutCategory(AppCategories, "Applications", "Messages"),
- simpleShortcutCategory(MultiTasking, "Recent apps", "Cycle forward through recent apps"),
- )
+ @Test
+ fun pressedKeys_assertCorrectConversion() {
+ testScope.runTest {
+ helper.toggle(deviceId = 123)
+ val pressedKeys by collectLastValue(repo.pressedKeys)
+ repo.updateUserKeyCombination(
+ KeyCombination(modifiers = META_META_ON, keyCode = KEYCODE_SLASH)
+ )
- private val customizableInputGesturesWithSimpleShortcutCombinations =
- listOf(
- simpleInputGestureData(
- keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_ASSISTANT
- ),
- simpleInputGestureData(keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_HOME),
- simpleInputGestureData(
- keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SYSTEM_SETTINGS
- ),
- simpleInputGestureData(keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_LOCK_SCREEN),
- simpleInputGestureData(
- keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL
- ),
- simpleInputGestureData(keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_NOTES),
- simpleInputGestureData(
- keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_TAKE_SCREENSHOT
- ),
- simpleInputGestureData(keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_BACK),
- simpleInputGestureData(
- keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION
- ),
- simpleInputGestureData(
- keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_LEFT
- ),
- simpleInputGestureData(
- keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_LEFT
- ),
- simpleInputGestureData(
- keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_RIGHT
- ),
- simpleInputGestureData(
- keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_RIGHT
- ),
- simpleInputGestureData(
- keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_SHORTCUT_HELPER
- ),
- simpleInputGestureData(keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS),
- simpleInputGestureData(
- keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALCULATOR
- ),
- simpleInputGestureData(
- keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALENDAR
- ),
- simpleInputGestureData(
- keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_BROWSER
- ),
- simpleInputGestureData(
- keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CONTACTS
- ),
- simpleInputGestureData(
- keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_EMAIL
- ),
- simpleInputGestureData(
- keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MAPS
- ),
- simpleInputGestureData(
- keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MESSAGING
- ),
- simpleInputGestureData(
- keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS_SWITCHER
- ),
- )
+ assertThat(pressedKeys)
+ .containsExactly(
+ ShortcutKey.Icon.ResIdIcon(R.drawable.ic_ksh_key_meta),
+ ShortcutKey.Text("/"),
+ )
+ }
+ }
+
+ private val allSupportedModifiers =
+ META_META_ON or
+ META_CTRL_ON or
+ META_FUNCTION_ON or
+ META_SHIFT_ON or
+ META_ALT_ON or
+ META_SYM_ON
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/source/TestShortcuts.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/source/TestShortcuts.kt
index c9c39b3..a1e7ef4 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/source/TestShortcuts.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/source/TestShortcuts.kt
@@ -16,11 +16,20 @@
package com.android.systemui.keyboard.shortcut.data.source
+import android.hardware.input.InputGestureData
+import android.hardware.input.InputGestureData.createKeyTrigger
+import android.hardware.input.KeyGestureEvent
import android.view.KeyEvent
import android.view.KeyboardShortcutGroup
import android.view.KeyboardShortcutInfo
+import com.android.systemui.keyboard.shortcut.shared.model.Shortcut
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategory
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType
+import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.AppCategories
+import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.MultiTasking
+import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.System
+import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCommand
+import com.android.systemui.keyboard.shortcut.shared.model.ShortcutKey
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutSubCategory
import com.android.systemui.keyboard.shortcut.shared.model.shortcut
import com.android.systemui.res.R
@@ -64,6 +73,13 @@
}
}
+ private val goHomeShortcutInfo =
+ KeyboardShortcutInfo(
+ /* label = */ "Go to home screen",
+ /* keycode = */ KeyEvent.KEYCODE_B,
+ /* modifiers = */ KeyEvent.META_CTRL_ON or KeyEvent.META_ALT_ON,
+ )
+
private val standardShortcutInfo1 =
KeyboardShortcutInfo(
/* label = */ "Standard shortcut 1",
@@ -79,6 +95,16 @@
}
}
+ private val customGoHomeShortcut =
+ shortcut("Go to home screen") {
+ command {
+ key("Ctrl")
+ key("Alt")
+ key("A")
+ isCustom(true)
+ }
+ }
+
private val standardShortcutInfo2 =
KeyboardShortcutInfo(
/* label = */ "Standard shortcut 2",
@@ -123,35 +149,38 @@
listOf(
shortcutInfoWithRepeatedLabel,
shortcutInfoWithRepeatedLabelAlternate,
- shortcutInfoWithRepeatedLabelSecondAlternate
- )
+ shortcutInfoWithRepeatedLabelSecondAlternate,
+ ),
)
private val subCategoryWithGroupedRepeatedShortcutLabels =
ShortcutSubCategory(
label = groupWithRepeatedShortcutLabels.label!!.toString(),
- shortcuts = listOf(shortcutWithGroupedRepeatedLabel)
+ shortcuts = listOf(shortcutWithGroupedRepeatedLabel),
)
private val groupWithStandardShortcutInfo =
KeyboardShortcutGroup("Standard group", listOf(standardShortcutInfo1))
+ val groupWithGoHomeShortcutInfo =
+ KeyboardShortcutGroup("System controls", listOf(goHomeShortcutInfo))
+
private val subCategoryWithStandardShortcut =
ShortcutSubCategory(
label = groupWithStandardShortcutInfo.label!!.toString(),
- shortcuts = listOf(standardShortcut1)
+ shortcuts = listOf(standardShortcut1),
)
private val groupWithOnlyUnsupportedModifierShortcut =
KeyboardShortcutGroup(
"Group with unsupported modifiers",
- listOf(shortcutInfoWithUnsupportedModifiers)
+ listOf(shortcutInfoWithUnsupportedModifiers),
)
private val groupWithSupportedAndUnsupportedModifierShortcut =
KeyboardShortcutGroup(
"Group with mix of supported and not supported modifiers",
- listOf(standardShortcutInfo3, shortcutInfoWithUnsupportedModifiers)
+ listOf(standardShortcutInfo3, shortcutInfoWithUnsupportedModifiers),
)
private val switchToNextLanguageShortcut =
@@ -174,19 +203,19 @@
private val subCategoryForInputLanguageSwitchShortcuts =
ShortcutSubCategory(
"Input",
- listOf(switchToNextLanguageShortcut, switchToPreviousLanguageShortcut)
+ listOf(switchToNextLanguageShortcut, switchToPreviousLanguageShortcut),
)
private val subCategoryWithUnsupportedShortcutsRemoved =
ShortcutSubCategory(
groupWithSupportedAndUnsupportedModifierShortcut.label!!.toString(),
- listOf(standardShortcut3)
+ listOf(standardShortcut3),
)
private val standardGroup1 =
KeyboardShortcutGroup(
"Standard group 1",
- listOf(standardShortcutInfo1, standardShortcutInfo2, standardShortcutInfo3)
+ listOf(standardShortcutInfo1, standardShortcutInfo2, standardShortcutInfo3),
)
private val standardPackageName1 = "standard.app.group1"
@@ -194,38 +223,63 @@
private val standardAppGroup1 =
KeyboardShortcutGroup(
"Standard app group 1",
- listOf(standardShortcutInfo1, standardShortcutInfo2, standardShortcutInfo3)
+ listOf(standardShortcutInfo1, standardShortcutInfo2, standardShortcutInfo3),
)
.apply { packageName = standardPackageName1 }
+ private val standardSystemAppSubcategoryWithCustomHomeShortcut =
+ ShortcutSubCategory("System controls", listOf(customGoHomeShortcut))
+
private val standardSubCategory1 =
ShortcutSubCategory(
standardGroup1.label!!.toString(),
- listOf(standardShortcut1, standardShortcut2, standardShortcut3)
+ listOf(standardShortcut1, standardShortcut2, standardShortcut3),
)
private val standardGroup2 =
KeyboardShortcutGroup(
"Standard group 2",
- listOf(standardShortcutInfo3, standardShortcutInfo2, standardShortcutInfo1)
+ listOf(standardShortcutInfo3, standardShortcutInfo2, standardShortcutInfo1),
)
private val standardSubCategory2 =
ShortcutSubCategory(
standardGroup2.label!!.toString(),
- listOf(standardShortcut3, standardShortcut2, standardShortcut1)
+ listOf(standardShortcut3, standardShortcut2, standardShortcut1),
)
private val standardGroup3 =
KeyboardShortcutGroup(
"Standard group 3",
- listOf(standardShortcutInfo2, standardShortcutInfo1)
+ listOf(standardShortcutInfo2, standardShortcutInfo1),
)
private val standardSubCategory3 =
ShortcutSubCategory(
standardGroup3.label!!.toString(),
- listOf(standardShortcut2, standardShortcut1)
+ listOf(standardShortcut2, standardShortcut1),
)
+
+ private val systemSubCategoryWithGoHomeShortcuts =
+ ShortcutSubCategory(
+ label = "System controls",
+ shortcuts =
+ listOf(
+ shortcut("Go to home screen") {
+ command {
+ key("Ctrl")
+ key("Alt")
+ key("B")
+ }
+ command {
+ key("Ctrl")
+ key("Alt")
+ key("A")
+ isCustom(true)
+ }
+ }
+ ),
+ )
+
val imeGroups = listOf(standardGroup1, standardGroup2, standardGroup3)
val imeCategory =
ShortcutCategory(
@@ -235,8 +289,8 @@
subCategoryForInputLanguageSwitchShortcuts,
standardSubCategory1,
standardSubCategory2,
- standardSubCategory3
- )
+ standardSubCategory3,
+ ),
)
val currentAppGroups = listOf(standardAppGroup1)
@@ -245,15 +299,33 @@
val systemGroups = listOf(standardGroup3, standardGroup2, standardGroup1)
val systemCategory =
ShortcutCategory(
- type = ShortcutCategoryType.System,
- subCategories = listOf(standardSubCategory3, standardSubCategory2, standardSubCategory1)
+ type = System,
+ subCategories = listOf(standardSubCategory3, standardSubCategory2, standardSubCategory1),
+ )
+
+ val systemCategoryWithMergedGoHomeShortcut =
+ ShortcutCategory(
+ type = System,
+ subCategories = listOf(systemSubCategoryWithGoHomeShortcuts),
+ )
+
+ val systemCategoryWithCustomHomeShortcut =
+ ShortcutCategory(
+ type = System,
+ subCategories =
+ listOf(
+ standardSubCategory3,
+ standardSubCategory2,
+ standardSubCategory1,
+ standardSystemAppSubcategoryWithCustomHomeShortcut,
+ ),
)
val multitaskingGroups = listOf(standardGroup2, standardGroup1)
val multitaskingCategory =
ShortcutCategory(
- type = ShortcutCategoryType.MultiTasking,
- subCategories = listOf(standardSubCategory2, standardSubCategory1)
+ type = MultiTasking,
+ subCategories = listOf(standardSubCategory2, standardSubCategory1),
)
val groupsWithDuplicateShortcutLabels =
@@ -266,14 +338,14 @@
listOf(
subCategoryForInputLanguageSwitchShortcuts,
subCategoryWithGroupedRepeatedShortcutLabels,
- subCategoryWithStandardShortcut
+ subCategoryWithStandardShortcut,
)
val groupsWithUnsupportedModifier =
listOf(
groupWithStandardShortcutInfo,
groupWithOnlyUnsupportedModifierShortcut,
- groupWithSupportedAndUnsupportedModifierShortcut
+ groupWithSupportedAndUnsupportedModifierShortcut,
)
val subCategoriesWithUnsupportedModifiersRemoved =
@@ -283,8 +355,174 @@
listOf(
subCategoryForInputLanguageSwitchShortcuts,
subCategoryWithStandardShortcut,
- subCategoryWithUnsupportedShortcutsRemoved
+ subCategoryWithUnsupportedShortcutsRemoved,
)
val groupsWithOnlyUnsupportedModifiers = listOf(groupWithOnlyUnsupportedModifierShortcut)
+
+ private fun simpleInputGestureData(
+ keyCode: Int = KeyEvent.KEYCODE_A,
+ modifiers: Int = KeyEvent.META_CTRL_ON or KeyEvent.META_ALT_ON,
+ keyGestureType: Int,
+ ): InputGestureData {
+ val builder = InputGestureData.Builder()
+ builder.setKeyGestureType(keyGestureType)
+ builder.setTrigger(createKeyTrigger(keyCode, modifiers))
+ return builder.build()
+ }
+
+ private fun simpleShortcutCategory(
+ category: ShortcutCategoryType,
+ subcategoryLabel: String,
+ shortcutLabel: String,
+ ): ShortcutCategory {
+ return ShortcutCategory(
+ type = category,
+ subCategories =
+ listOf(
+ ShortcutSubCategory(
+ label = subcategoryLabel,
+ shortcuts = listOf(simpleShortcut(shortcutLabel)),
+ )
+ ),
+ )
+ }
+
+ private fun simpleShortcut(label: String) =
+ Shortcut(
+ label = label,
+ commands =
+ listOf(
+ ShortcutCommand(
+ isCustom = true,
+ keys =
+ listOf(
+ ShortcutKey.Text("Ctrl"),
+ ShortcutKey.Text("Alt"),
+ ShortcutKey.Text("A"),
+ ),
+ )
+ ),
+ )
+
+ val customizableInputGestureWithUnknownKeyGestureType =
+ // These key gesture events are currently not supported by shortcut helper customizer
+ listOf(
+ simpleInputGestureData(
+ keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_GLOBAL_ACTIONS
+ ),
+ simpleInputGestureData(keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_MEDIA_KEY),
+ )
+
+ val expectedShortcutCategoriesWithSimpleShortcutCombination =
+ listOf(
+ simpleShortcutCategory(System, "System apps", "Open assistant"),
+ simpleShortcutCategory(System, "System controls", "Go to home screen"),
+ simpleShortcutCategory(System, "System apps", "Open settings"),
+ simpleShortcutCategory(System, "System controls", "Lock screen"),
+ simpleShortcutCategory(System, "System controls", "View notifications"),
+ simpleShortcutCategory(System, "System apps", "Take a note"),
+ simpleShortcutCategory(System, "System controls", "Take screenshot"),
+ simpleShortcutCategory(System, "System controls", "Go back"),
+ simpleShortcutCategory(
+ MultiTasking,
+ "Split screen",
+ "Switch from split screen to full screen",
+ ),
+ simpleShortcutCategory(
+ MultiTasking,
+ "Split screen",
+ "Use split screen with current app on the left",
+ ),
+ simpleShortcutCategory(
+ MultiTasking,
+ "Split screen",
+ "Switch to app on left or above while using split screen",
+ ),
+ simpleShortcutCategory(
+ MultiTasking,
+ "Split screen",
+ "Use split screen with current app on the right",
+ ),
+ simpleShortcutCategory(
+ MultiTasking,
+ "Split screen",
+ "Switch to app on right or below while using split screen",
+ ),
+ simpleShortcutCategory(System, "System controls", "Show shortcuts"),
+ simpleShortcutCategory(System, "System controls", "View recent apps"),
+ simpleShortcutCategory(AppCategories, "Applications", "Calculator"),
+ simpleShortcutCategory(AppCategories, "Applications", "Calendar"),
+ simpleShortcutCategory(AppCategories, "Applications", "Browser"),
+ simpleShortcutCategory(AppCategories, "Applications", "Contacts"),
+ simpleShortcutCategory(AppCategories, "Applications", "Email"),
+ simpleShortcutCategory(AppCategories, "Applications", "Maps"),
+ simpleShortcutCategory(AppCategories, "Applications", "SMS"),
+ simpleShortcutCategory(MultiTasking, "Recent apps", "Cycle forward through recent apps"),
+ )
+ val customInputGestureTypeHome =
+ simpleInputGestureData(keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_HOME)
+
+ val allCustomizableInputGesturesWithSimpleShortcutCombinations =
+ listOf(
+ simpleInputGestureData(
+ keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_ASSISTANT
+ ),
+ simpleInputGestureData(keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_HOME),
+ simpleInputGestureData(
+ keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SYSTEM_SETTINGS
+ ),
+ simpleInputGestureData(keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_LOCK_SCREEN),
+ simpleInputGestureData(
+ keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL
+ ),
+ simpleInputGestureData(keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_NOTES),
+ simpleInputGestureData(
+ keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_TAKE_SCREENSHOT
+ ),
+ simpleInputGestureData(keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_BACK),
+ simpleInputGestureData(
+ keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION
+ ),
+ simpleInputGestureData(
+ keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_LEFT
+ ),
+ simpleInputGestureData(
+ keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_LEFT
+ ),
+ simpleInputGestureData(
+ keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_RIGHT
+ ),
+ simpleInputGestureData(
+ keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_RIGHT
+ ),
+ simpleInputGestureData(
+ keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_SHORTCUT_HELPER
+ ),
+ simpleInputGestureData(keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS),
+ simpleInputGestureData(
+ keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALCULATOR
+ ),
+ simpleInputGestureData(
+ keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALENDAR
+ ),
+ simpleInputGestureData(
+ keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_BROWSER
+ ),
+ simpleInputGestureData(
+ keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CONTACTS
+ ),
+ simpleInputGestureData(
+ keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_EMAIL
+ ),
+ simpleInputGestureData(
+ keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MAPS
+ ),
+ simpleInputGestureData(
+ keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MESSAGING
+ ),
+ simpleInputGestureData(
+ keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS_SWITCHER
+ ),
+ )
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperCategoriesInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperCategoriesInteractorTest.kt
index 57c8b44..f7c7701 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperCategoriesInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperCategoriesInteractorTest.kt
@@ -16,12 +16,24 @@
package com.android.systemui.keyboard.shortcut.domain.interactor
+import android.content.Context
+import android.content.Context.INPUT_SERVICE
+import android.hardware.input.InputGestureData
+import android.hardware.input.fakeInputManager
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.keyboard.shortcut.data.source.FakeKeyboardShortcutGroupsSource
import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts
+import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.allCustomizableInputGesturesWithSimpleShortcutCombinations
+import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.customInputGestureTypeHome
+import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.groupWithGoHomeShortcutInfo
+import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.systemCategoryWithCustomHomeShortcut
+import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.systemCategoryWithMergedGoHomeShortcut
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategory
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.InputMethodEditor
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.MultiTasking
@@ -34,6 +46,8 @@
import com.android.systemui.keyboard.shortcut.shortcutHelperTestHelper
import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.kosmos.testScope
+import com.android.systemui.settings.FakeUserTracker
+import com.android.systemui.settings.userTracker
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -42,13 +56,18 @@
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.kotlin.anyOrNull
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.whenever
@SmallTest
@RunWith(AndroidJUnit4::class)
class ShortcutHelperCategoriesInteractorTest : SysuiTestCase() {
+ private val mockUserContext: Context = mock()
private val systemShortcutsSource = FakeKeyboardShortcutGroupsSource()
private val multitaskingShortcutsSource = FakeKeyboardShortcutGroupsSource()
+
@OptIn(ExperimentalCoroutinesApi::class)
private val kosmos =
testKosmos().also {
@@ -57,17 +76,23 @@
it.shortcutHelperMultiTaskingShortcutsSource = multitaskingShortcutsSource
it.shortcutHelperAppCategoriesShortcutsSource = FakeKeyboardShortcutGroupsSource()
it.shortcutHelperCurrentAppShortcutsSource = FakeKeyboardShortcutGroupsSource()
+ it.userTracker = FakeUserTracker(onCreateCurrentUserContext = { mockUserContext })
}
+ private val fakeInputManager = kosmos.fakeInputManager
private val testScope = kosmos.testScope
- private val interactor = kosmos.shortcutHelperCategoriesInteractor
+ private lateinit var interactor: ShortcutHelperCategoriesInteractor
private val helper = kosmos.shortcutHelperTestHelper
+ private val inter by lazy { kosmos.shortcutHelperCategoriesInteractor }
@Before
fun setShortcuts() {
+ interactor = kosmos.shortcutHelperCategoriesInteractor
helper.setImeShortcuts(TestShortcuts.imeGroups)
systemShortcutsSource.setGroups(TestShortcuts.systemGroups)
multitaskingShortcutsSource.setGroups(TestShortcuts.multitaskingGroups)
+ whenever(mockUserContext.getSystemService(INPUT_SERVICE))
+ .thenReturn(fakeInputManager.inputManager)
}
@Test
@@ -120,7 +145,7 @@
ShortcutCategory(
type = InputMethodEditor,
subCategories =
- TestShortcuts.imeSubCategoriesWithGroupedDuplicatedShortcutLabels
+ TestShortcuts.imeSubCategoriesWithGroupedDuplicatedShortcutLabels,
),
)
.inOrder()
@@ -140,7 +165,7 @@
ShortcutCategory(
type = System,
subCategories =
- TestShortcuts.subCategoriesWithGroupedDuplicatedShortcutLabels
+ TestShortcuts.subCategoriesWithGroupedDuplicatedShortcutLabels,
),
TestShortcuts.multitaskingCategory,
TestShortcuts.imeCategory,
@@ -163,7 +188,7 @@
ShortcutCategory(
type = MultiTasking,
subCategories =
- TestShortcuts.subCategoriesWithGroupedDuplicatedShortcutLabels
+ TestShortcuts.subCategoriesWithGroupedDuplicatedShortcutLabels,
),
TestShortcuts.imeCategory,
)
@@ -185,7 +210,7 @@
ShortcutCategory(
type = InputMethodEditor,
subCategories =
- TestShortcuts.imeSubCategoriesWithUnsupportedModifiersRemoved
+ TestShortcuts.imeSubCategoriesWithUnsupportedModifiersRemoved,
),
)
.inOrder()
@@ -203,7 +228,7 @@
.containsExactly(
ShortcutCategory(
type = System,
- subCategories = TestShortcuts.subCategoriesWithUnsupportedModifiersRemoved
+ subCategories = TestShortcuts.subCategoriesWithUnsupportedModifiersRemoved,
),
TestShortcuts.multitaskingCategory,
TestShortcuts.imeCategory,
@@ -224,7 +249,7 @@
TestShortcuts.systemCategory,
ShortcutCategory(
type = MultiTasking,
- subCategories = TestShortcuts.subCategoriesWithUnsupportedModifiersRemoved
+ subCategories = TestShortcuts.subCategoriesWithUnsupportedModifiersRemoved,
),
TestShortcuts.imeCategory,
)
@@ -240,10 +265,7 @@
helper.showFromActivity()
assertThat(categories)
- .containsExactly(
- TestShortcuts.multitaskingCategory,
- TestShortcuts.imeCategory,
- )
+ .containsExactly(TestShortcuts.multitaskingCategory, TestShortcuts.imeCategory)
.inOrder()
}
@@ -256,10 +278,63 @@
helper.showFromActivity()
assertThat(categories)
- .containsExactly(
- TestShortcuts.systemCategory,
- TestShortcuts.imeCategory,
- )
+ .containsExactly(TestShortcuts.systemCategory, TestShortcuts.imeCategory)
.inOrder()
}
+
+ @Test
+ @DisableFlags(Flags.FLAG_KEYBOARD_SHORTCUT_HELPER_SHORTCUT_CUSTOMIZER)
+ fun categories_excludesCustomShortcutsWhenFlagIsOff() {
+ testScope.runTest {
+ setCustomInputGestures(allCustomizableInputGesturesWithSimpleShortcutCombinations)
+ helper.showFromActivity()
+ val categories by collectLastValue(interactor.shortcutCategories)
+ assertThat(categories)
+ .containsExactly(
+ TestShortcuts.systemCategory,
+ TestShortcuts.multitaskingCategory,
+ TestShortcuts.imeCategory,
+ )
+ }
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_KEYBOARD_SHORTCUT_HELPER_SHORTCUT_CUSTOMIZER)
+ fun categories_includesCustomShortcutsWhenFlagIsOn() {
+ testScope.runTest {
+ setCustomInputGestures(listOf(customInputGestureTypeHome))
+ helper.showFromActivity()
+ val categories by collectLastValue(interactor.shortcutCategories)
+ assertThat(categories)
+ .containsExactly(
+ systemCategoryWithCustomHomeShortcut,
+ TestShortcuts.multitaskingCategory,
+ TestShortcuts.imeCategory,
+ )
+ }
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_KEYBOARD_SHORTCUT_HELPER_SHORTCUT_CUSTOMIZER)
+ fun categories_correctlyMergesDefaultAndCustomShortcutsOfSameType() {
+ testScope.runTest {
+ setCustomInputGestures(listOf(customInputGestureTypeHome))
+ systemShortcutsSource.setGroups(groupWithGoHomeShortcutInfo)
+ helper.showFromActivity()
+
+ val categories by collectLastValue(interactor.shortcutCategories)
+
+ assertThat(categories)
+ .containsExactly(
+ systemCategoryWithMergedGoHomeShortcut,
+ TestShortcuts.multitaskingCategory,
+ TestShortcuts.imeCategory,
+ )
+ }
+ }
+
+ private fun setCustomInputGestures(customInputGestures: List<InputGestureData>) {
+ whenever(fakeInputManager.inputManager.getCustomInputGestures(/* filter= */ anyOrNull()))
+ .thenReturn(customInputGestures)
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/ui/ShortcutHelperDialogStarterTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/ui/ShortcutHelperDialogStarterTest.kt
index 1580ea5..000024f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/ui/ShortcutHelperDialogStarterTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/ui/ShortcutHelperDialogStarterTest.kt
@@ -16,6 +16,9 @@
package com.android.systemui.keyboard.shortcut.ui
+import android.content.Context
+import android.content.Context.INPUT_SERVICE
+import android.hardware.input.fakeInputManager
import androidx.test.annotation.UiThreadTest
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
@@ -36,6 +39,8 @@
import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.kosmos.testScope
import com.android.systemui.plugins.activityStarter
+import com.android.systemui.settings.FakeUserTracker
+import com.android.systemui.settings.userTracker
import com.android.systemui.statusbar.phone.systemUIDialogFactory
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -44,6 +49,8 @@
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.whenever
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@@ -52,7 +59,7 @@
private val fakeSystemSource = FakeKeyboardShortcutGroupsSource()
private val fakeMultiTaskingSource = FakeKeyboardShortcutGroupsSource()
-
+ private val mockUserContext: Context = mock()
private val kosmos =
Kosmos().also {
it.testCase = this
@@ -62,8 +69,10 @@
it.shortcutHelperAppCategoriesShortcutsSource = FakeKeyboardShortcutGroupsSource()
it.shortcutHelperInputShortcutsSource = FakeKeyboardShortcutGroupsSource()
it.shortcutHelperCurrentAppShortcutsSource = FakeKeyboardShortcutGroupsSource()
+ it.userTracker = FakeUserTracker(onCreateCurrentUserContext = { mockUserContext })
}
+ private val inputManager = kosmos.fakeInputManager.inputManager
private val testScope = kosmos.testScope
private val testHelper = kosmos.shortcutHelperTestHelper
private val dialogFactory = kosmos.systemUIDialogFactory
@@ -85,6 +94,7 @@
fun setUp() {
fakeSystemSource.setGroups(TestShortcuts.systemGroups)
fakeMultiTaskingSource.setGroups(TestShortcuts.multitaskingGroups)
+ whenever(mockUserContext.getSystemService(INPUT_SERVICE)).thenReturn(inputManager)
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/WorkLockActivityControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/WorkLockActivityControllerTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/WorkLockActivityControllerTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/WorkLockActivityControllerTest.java
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt
index 4e429c3..69fb03d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt
@@ -47,7 +47,8 @@
import com.android.systemui.keyguard.data.repository.BiometricType.UNDER_DISPLAY_FINGERPRINT
import com.android.systemui.keyguard.shared.model.DevicePosture
import com.android.systemui.res.R
-import com.android.systemui.statusbar.pipeline.mobile.data.repository.fakeMobileConnectionsRepository
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.fake
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.mobileConnectionsRepository
import com.android.systemui.statusbar.policy.DevicePostureController
import com.android.systemui.testKosmos
import com.android.systemui.user.data.repository.FakeUserRepository
@@ -99,7 +100,7 @@
private lateinit var devicePostureRepository: FakeDevicePostureRepository
private lateinit var facePropertyRepository: FakeFacePropertyRepository
private lateinit var fingerprintPropertyRepository: FakeFingerprintPropertyRepository
- private val mobileConnectionsRepository = kosmos.fakeMobileConnectionsRepository
+ private val mobileConnectionsRepository = kosmos.mobileConnectionsRepository
private lateinit var testDispatcher: TestDispatcher
private lateinit var testScope: TestScope
@@ -142,7 +143,7 @@
1,
SensorStrength.STRONG,
FingerprintSensorType.UDFPS_OPTICAL,
- emptyMap()
+ emptyMap(),
)
verify(lockPatternUtils).registerStrongAuthTracker(strongAuthTracker.capture())
verify(authController, times(2)).addCallback(authControllerCallback.capture())
@@ -247,7 +248,7 @@
private fun deviceIsInPostureThatSupportsFaceAuth() {
overrideResource(
R.integer.config_face_auth_supported_posture,
- DevicePostureController.DEVICE_POSTURE_FLIPPED
+ DevicePostureController.DEVICE_POSTURE_FLIPPED,
)
devicePostureRepository.setCurrentPosture(DevicePosture.FLIPPED)
}
@@ -459,7 +460,7 @@
biometricsAreEnabledBySettings()
doNotDisableKeyguardAuthFeatures()
- mobileConnectionsRepository.isAnySimSecure.value = false
+ mobileConnectionsRepository.fake.isAnySimSecure.value = false
runCurrent()
val isFaceAuthEnabledAndEnrolled by
@@ -467,7 +468,7 @@
assertThat(isFaceAuthEnabledAndEnrolled).isTrue()
- mobileConnectionsRepository.isAnySimSecure.value = true
+ mobileConnectionsRepository.fake.isAnySimSecure.value = true
runCurrent()
assertThat(isFaceAuthEnabledAndEnrolled).isFalse()
@@ -485,13 +486,13 @@
doNotDisableKeyguardAuthFeatures()
faceAuthIsStrongBiometric()
biometricsAreEnabledBySettings()
- mobileConnectionsRepository.isAnySimSecure.value = false
+ mobileConnectionsRepository.fake.isAnySimSecure.value = false
onStrongAuthChanged(STRONG_AUTH_NOT_REQUIRED, PRIMARY_USER_ID)
onNonStrongAuthChanged(false, PRIMARY_USER_ID)
assertThat(isFaceAuthCurrentlyAllowed).isTrue()
- mobileConnectionsRepository.isAnySimSecure.value = true
+ mobileConnectionsRepository.fake.isAnySimSecure.value = true
assertThat(isFaceAuthCurrentlyAllowed).isFalse()
}
@@ -584,7 +585,7 @@
testScope.runTest {
overrideResource(
R.integer.config_face_auth_supported_posture,
- DevicePostureController.DEVICE_POSTURE_UNKNOWN
+ DevicePostureController.DEVICE_POSTURE_UNKNOWN,
)
createBiometricSettingsRepository()
@@ -597,7 +598,7 @@
testScope.runTest {
overrideResource(
R.integer.config_face_auth_supported_posture,
- DevicePostureController.DEVICE_POSTURE_FLIPPED
+ DevicePostureController.DEVICE_POSTURE_FLIPPED,
)
createBiometricSettingsRepository()
@@ -749,7 +750,7 @@
1,
SensorStrength.STRONG,
FingerprintSensorType.UDFPS_OPTICAL,
- emptyMap()
+ emptyMap(),
)
// Non strong auth is not allowed now, FP is marked strong
onStrongAuthChanged(STRONG_AUTH_NOT_REQUIRED, PRIMARY_USER_ID)
@@ -761,7 +762,7 @@
1,
SensorStrength.CONVENIENCE,
FingerprintSensorType.UDFPS_OPTICAL,
- emptyMap()
+ emptyMap(),
)
assertThat(isFingerprintCurrentlyAllowed).isFalse()
@@ -769,7 +770,7 @@
1,
SensorStrength.WEAK,
FingerprintSensorType.UDFPS_OPTICAL,
- emptyMap()
+ emptyMap(),
)
assertThat(isFingerprintCurrentlyAllowed).isFalse()
}
@@ -791,7 +792,7 @@
1,
SensorStrength.STRONG,
FingerprintSensorType.UDFPS_OPTICAL,
- emptyMap()
+ emptyMap(),
)
// Non strong auth is not allowed now, FP is marked strong
onStrongAuthChanged(STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN, PRIMARY_USER_ID)
@@ -830,7 +831,7 @@
UserInfo(
/* id= */ PRIMARY_USER_ID,
/* name= */ "primary user",
- /* flags= */ UserInfo.FLAG_PRIMARY
+ /* flags= */ UserInfo.FLAG_PRIMARY,
)
private const val ANOTHER_USER_ID = 1
@@ -838,7 +839,7 @@
UserInfo(
/* id= */ ANOTHER_USER_ID,
/* name= */ "another user",
- /* flags= */ UserInfo.FLAG_PRIMARY
+ /* flags= */ UserInfo.FLAG_PRIMARY,
)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardSmartspaceRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardSmartspaceRepositoryImplTest.kt
index 5a597fe..f537e32 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardSmartspaceRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardSmartspaceRepositoryImplTest.kt
@@ -50,7 +50,6 @@
fakeSettings.userId = fakeUserTracker.userId
underTest =
KeyguardSmartspaceRepositoryImpl(
- context = context,
secureSettings = fakeSettings,
userTracker = fakeUserTracker,
applicationScope = scope.backgroundScope,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTouchHandlingInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTouchHandlingInteractorTest.kt
index aee72de2..28ac169 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTouchHandlingInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTouchHandlingInteractorTest.kt
@@ -310,7 +310,7 @@
// read during initialization to set up flows. Maybe there is a better way to handle that.
underTest =
KeyguardTouchHandlingInteractor(
- appContext = mContext,
+ context = mContext,
scope = testScope.backgroundScope,
transitionInteractor = kosmos.keyguardTransitionInteractor,
repository = keyguardRepository,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt
index 92764ae..74a0bafda 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt
@@ -17,6 +17,10 @@
package com.android.systemui.keyguard.ui.binder
import android.app.IActivityTaskManager
+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 androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
@@ -25,8 +29,10 @@
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.time.FakeSystemClock
+import com.android.window.flags.Flags
import com.android.wm.shell.keyguard.KeyguardTransitions
import org.junit.Before
+import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.eq
@@ -41,6 +47,9 @@
@RunWith(AndroidJUnit4::class)
@kotlinx.coroutines.ExperimentalCoroutinesApi
class WindowManagerLockscreenVisibilityManagerTest : SysuiTestCase() {
+
+ @get:Rule val checkFlagsRule: CheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule()
+
private lateinit var underTest: WindowManagerLockscreenVisibilityManager
private lateinit var executor: FakeExecutor
@@ -68,32 +77,62 @@
}
@Test
- fun testLockscreenVisible_andAodVisible() {
+ @RequiresFlagsDisabled(Flags.FLAG_ENSURE_KEYGUARD_DOES_TRANSITION_STARTING)
+ fun testLockscreenVisible_andAodVisible_without_keyguard_shell_transitions() {
underTest.setLockscreenShown(true)
- underTest.setAodVisible(true)
-
verify(activityTaskManagerService).setLockScreenShown(true, false)
+ underTest.setAodVisible(true)
verify(activityTaskManagerService).setLockScreenShown(true, true)
+
verifyNoMoreInteractions(activityTaskManagerService)
}
@Test
- fun testGoingAway_whenLockscreenVisible_thenSurfaceMadeVisible() {
+ @RequiresFlagsEnabled(Flags.FLAG_ENSURE_KEYGUARD_DOES_TRANSITION_STARTING)
+ fun testLockscreenVisible_andAodVisible_with_keyguard_shell_transitions() {
underTest.setLockscreenShown(true)
+ verify(keyguardTransitions).startKeyguardTransition(true, false)
underTest.setAodVisible(true)
+ verify(keyguardTransitions).startKeyguardTransition(true, true)
+ verifyNoMoreInteractions(keyguardTransitions)
+ }
+
+ @Test
+ @RequiresFlagsDisabled(Flags.FLAG_ENSURE_KEYGUARD_DOES_TRANSITION_STARTING)
+ fun testGoingAway_whenLockscreenVisible_thenSurfaceMadeVisible_without_keyguard_shell_transitions() {
+ underTest.setLockscreenShown(true)
verify(activityTaskManagerService).setLockScreenShown(true, false)
+ underTest.setAodVisible(true)
verify(activityTaskManagerService).setLockScreenShown(true, true)
+
verifyNoMoreInteractions(activityTaskManagerService)
underTest.setSurfaceBehindVisibility(true)
-
verify(activityTaskManagerService).keyguardGoingAway(anyInt())
+
verifyNoMoreInteractions(activityTaskManagerService)
}
@Test
- fun testSurfaceVisible_whenLockscreenNotShowing_doesNotTriggerGoingAway() {
+ @RequiresFlagsEnabled(Flags.FLAG_ENSURE_KEYGUARD_DOES_TRANSITION_STARTING)
+ fun testGoingAway_whenLockscreenVisible_thenSurfaceMadeVisible_with_keyguard_shell_transitions() {
+ underTest.setLockscreenShown(true)
+ verify(keyguardTransitions).startKeyguardTransition(true, false)
+ underTest.setAodVisible(true)
+ verify(keyguardTransitions).startKeyguardTransition(true, true)
+
+ verifyNoMoreInteractions(keyguardTransitions)
+
+ underTest.setSurfaceBehindVisibility(true)
+ verify(keyguardTransitions).startKeyguardTransition(false, false)
+
+ verifyNoMoreInteractions(keyguardTransitions)
+ }
+
+ @Test
+ @RequiresFlagsDisabled(Flags.FLAG_ENSURE_KEYGUARD_DOES_TRANSITION_STARTING)
+ fun testSurfaceVisible_whenLockscreenNotShowing_doesNotTriggerGoingAway_without_keyguard_shell_transitions() {
underTest.setLockscreenShown(false)
underTest.setAodVisible(false)
@@ -106,7 +145,22 @@
}
@Test
- fun testAodVisible_noLockscreenShownCallYet_doesNotShowLockscreenUntilLater() {
+ @RequiresFlagsEnabled(Flags.FLAG_ENSURE_KEYGUARD_DOES_TRANSITION_STARTING)
+ fun testSurfaceVisible_whenLockscreenNotShowing_doesNotTriggerGoingAway_with_keyguard_shell_transitions() {
+ underTest.setLockscreenShown(false)
+ underTest.setAodVisible(false)
+
+ verify(keyguardTransitions).startKeyguardTransition(false, false)
+ verifyNoMoreInteractions(keyguardTransitions)
+
+ underTest.setSurfaceBehindVisibility(true)
+
+ verifyNoMoreInteractions(keyguardTransitions)
+ }
+
+ @Test
+ @RequiresFlagsDisabled(Flags.FLAG_ENSURE_KEYGUARD_DOES_TRANSITION_STARTING)
+ fun testAodVisible_noLockscreenShownCallYet_doesNotShowLockscreenUntilLater_without_keyguard_shell_transitions() {
underTest.setAodVisible(false)
verifyNoMoreInteractions(activityTaskManagerService)
@@ -116,7 +170,19 @@
}
@Test
- fun setSurfaceBehindVisibility_goesAwayFirst_andIgnoresSecondCall() {
+ @RequiresFlagsEnabled(Flags.FLAG_ENSURE_KEYGUARD_DOES_TRANSITION_STARTING)
+ fun testAodVisible_noLockscreenShownCallYet_doesNotShowLockscreenUntilLater_with_keyguard_shell_transitions() {
+ underTest.setAodVisible(false)
+ verifyNoMoreInteractions(keyguardTransitions)
+
+ underTest.setLockscreenShown(true)
+ verify(keyguardTransitions).startKeyguardTransition(true, false)
+ verifyNoMoreInteractions(activityTaskManagerService)
+ }
+
+ @Test
+ @RequiresFlagsDisabled(Flags.FLAG_ENSURE_KEYGUARD_DOES_TRANSITION_STARTING)
+ fun setSurfaceBehindVisibility_goesAwayFirst_andIgnoresSecondCall_without_keyguard_shell_transitions() {
underTest.setLockscreenShown(true)
underTest.setSurfaceBehindVisibility(true)
verify(activityTaskManagerService).keyguardGoingAway(0)
@@ -126,8 +192,27 @@
}
@Test
- fun setSurfaceBehindVisibility_falseSetsLockscreenVisibility() {
+ @RequiresFlagsEnabled(Flags.FLAG_ENSURE_KEYGUARD_DOES_TRANSITION_STARTING)
+ fun setSurfaceBehindVisibility_goesAwayFirst_andIgnoresSecondCall_with_keyguard_shell_transitions() {
+ underTest.setLockscreenShown(true)
+ underTest.setSurfaceBehindVisibility(true)
+ verify(keyguardTransitions).startKeyguardTransition(false, false)
+
+ underTest.setSurfaceBehindVisibility(true)
+ verifyNoMoreInteractions(keyguardTransitions)
+ }
+
+ @Test
+ @RequiresFlagsDisabled(Flags.FLAG_ENSURE_KEYGUARD_DOES_TRANSITION_STARTING)
+ fun setSurfaceBehindVisibility_falseSetsLockscreenVisibility_without_keyguard_shell_transitions() {
underTest.setSurfaceBehindVisibility(false)
verify(activityTaskManagerService).setLockScreenShown(eq(true), any())
}
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_ENSURE_KEYGUARD_DOES_TRANSITION_STARTING)
+ fun setSurfaceBehindVisibility_falseSetsLockscreenVisibility_with_keyguard_shell_transitions() {
+ underTest.setSurfaceBehindVisibility(false)
+ verify(keyguardTransitions).startKeyguardTransition(eq(true), any())
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSectionTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSectionTest.kt
index d94c97a..c0db95f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSectionTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSectionTest.kt
@@ -34,6 +34,7 @@
import com.android.systemui.keyguard.domain.interactor.KeyguardSmartspaceInteractor
import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardSmartspaceViewModel
+import com.android.systemui.res.R
import com.android.systemui.shared.R as sharedR
import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController
import com.android.systemui.util.mockito.any
@@ -68,6 +69,7 @@
private val clockShouldBeCentered = MutableStateFlow(false)
private val hasCustomWeatherDataDisplay = MutableStateFlow(false)
private val isWeatherVisibleFlow = MutableStateFlow(false)
+ private val isShadeLayoutWide = MutableStateFlow(false)
@Before
fun setup() {
@@ -80,7 +82,7 @@
keyguardSmartspaceInteractor,
lockscreenSmartspaceController,
keyguardUnlockAnimationController,
- blueprintInteractor
+ blueprintInteractor,
)
constraintLayout = ConstraintLayout(mContext)
whenever(lockscreenSmartspaceController.buildAndConnectView(any()))
@@ -93,6 +95,7 @@
whenever(keyguardClockViewModel.clockShouldBeCentered).thenReturn(clockShouldBeCentered)
whenever(keyguardSmartspaceViewModel.isSmartspaceEnabled).thenReturn(true)
whenever(keyguardSmartspaceViewModel.isWeatherVisible).thenReturn(isWeatherVisibleFlow)
+ whenever(keyguardSmartspaceViewModel.isShadeLayoutWide).thenReturn(isShadeLayoutWide)
constraintSet = ConstraintSet()
}
@@ -125,6 +128,26 @@
}
@Test
+ fun testConstraintsWhenShadeLayoutIsNotWide() {
+ underTest.addViews(constraintLayout)
+ underTest.applyConstraints(constraintSet)
+
+ val smartspaceConstraints = constraintSet.getConstraint(smartspaceView.id)
+ assertThat(smartspaceConstraints.layout.endToEnd).isEqualTo(ConstraintSet.PARENT_ID)
+ }
+
+ @Test
+ fun testConstraintsWhenShadeLayoutIsWide() {
+ isShadeLayoutWide.value = true
+
+ underTest.addViews(constraintLayout)
+ underTest.applyConstraints(constraintSet)
+
+ val smartspaceConstraints = constraintSet.getConstraint(smartspaceView.id)
+ assertThat(smartspaceConstraints.layout.endToEnd).isEqualTo(R.id.split_shade_guideline)
+ }
+
+ @Test
fun testConstraintsWhenNotHasCustomWeatherDataDisplay() {
whenever(keyguardSmartspaceViewModel.isDateWeatherDecoupled).thenReturn(true)
underTest.addViews(constraintLayout)
@@ -160,6 +183,7 @@
assertThat(constraintSet.getVisibility(weatherView.id)).isEqualTo(GONE)
assertThat(constraintSet.getVisibility(dateView.id)).isEqualTo(VISIBLE)
}
+
@Test
fun testCustomDateWeatherVisibility() {
hasCustomWeatherDataDisplay.value = true
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModelTest.kt
index b0959e4..d42b538 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModelTest.kt
@@ -27,10 +27,13 @@
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.kosmos.testScope
+import com.android.systemui.res.R
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runTest
+import org.junit.After
+import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -43,6 +46,16 @@
private val underTest: DeviceEntryForegroundViewModel =
kosmos.deviceEntryForegroundIconViewModel
+ @Before
+ fun setup() {
+ context.orCreateTestableResources.addOverride(R.integer.udfps_padding_debounce_duration, 0)
+ }
+
+ @After
+ fun teardown() {
+ context.orCreateTestableResources.removeOverride(R.integer.udfps_padding_debounce_duration)
+ }
+
@Test
fun aodIconColorWhite() =
testScope.runTest {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardSmartspaceViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardSmartspaceViewModelTest.kt
index 0c3fcb3..adce9d6 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardSmartspaceViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardSmartspaceViewModelTest.kt
@@ -26,6 +26,7 @@
import com.android.systemui.keyguard.shared.model.ClockSize
import com.android.systemui.kosmos.testScope
import com.android.systemui.plugins.clocks.ClockController
+import com.android.systemui.shade.data.repository.shadeRepository
import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
@@ -96,4 +97,26 @@
assertThat(isWeatherVisible).isEqualTo(false)
}
+
+ @Test
+ fun isShadeLayoutWide_withConfigTrue_true() =
+ with(kosmos) {
+ testScope.runTest {
+ val isShadeLayoutWide by collectLastValue(underTest.isShadeLayoutWide)
+ shadeRepository.setShadeLayoutWide(true)
+
+ assertThat(isShadeLayoutWide).isTrue()
+ }
+ }
+
+ @Test
+ fun isShadeLayoutWide_withConfigFalse_false() =
+ with(kosmos) {
+ testScope.runTest {
+ val isShadeLayoutWide by collectLastValue(underTest.isShadeLayoutWide)
+ shadeRepository.setShadeLayoutWide(false)
+
+ assertThat(isShadeLayoutWide).isFalse()
+ }
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaTimeoutListenerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/pipeline/MediaTimeoutListenerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaTimeoutListenerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/pipeline/MediaTimeoutListenerTest.kt
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/notetask/FakeNoteTaskBubbleController.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/notetask/FakeNoteTaskBubbleController.kt
index 450aadd..ebc00c3 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/notetask/FakeNoteTaskBubbleController.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/notetask/FakeNoteTaskBubbleController.kt
@@ -20,6 +20,7 @@
import android.content.Intent
import android.graphics.drawable.Icon
import android.os.UserHandle
+import com.android.wm.shell.bubbles.Bubble
import com.android.wm.shell.bubbles.Bubbles
import java.util.Optional
import kotlinx.coroutines.CoroutineDispatcher
@@ -33,14 +34,29 @@
class FakeNoteTaskBubbleController(
unUsed1: Context,
unsUsed2: CoroutineDispatcher,
- private val optionalBubbles: Optional<Bubbles>
+ private val optionalBubbles: Optional<Bubbles>,
) : NoteTaskBubblesController(unUsed1, unsUsed2) {
override suspend fun areBubblesAvailable() = optionalBubbles.isPresent
- override suspend fun showOrHideAppBubble(intent: Intent, userHandle: UserHandle, icon: Icon) {
+ override suspend fun showOrHideAppBubble(
+ intent: Intent,
+ userHandle: UserHandle,
+ icon: Icon,
+ bubbleExpandBehavior: NoteTaskBubbleExpandBehavior,
+ ) {
optionalBubbles.ifPresentOrElse(
- { bubbles -> bubbles.showOrHideAppBubble(intent, userHandle, icon) },
- { throw IllegalAccessException() }
+ { bubbles ->
+ if (
+ bubbleExpandBehavior == NoteTaskBubbleExpandBehavior.KEEP_IF_EXPANDED &&
+ bubbles.isBubbleExpanded(
+ Bubble.getAppBubbleKeyForApp(intent.`package`, userHandle)
+ )
+ ) {
+ return@ifPresentOrElse
+ }
+ bubbles.showOrHideAppBubble(intent, userHandle, icon)
+ },
+ { throw IllegalAccessException() },
)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/notetask/NoteTaskBubblesServiceTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/notetask/NoteTaskBubblesServiceTest.kt
index 9ef6b9c..e55d6ad 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/notetask/NoteTaskBubblesServiceTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/notetask/NoteTaskBubblesServiceTest.kt
@@ -21,9 +21,9 @@
import android.os.UserHandle
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.notetask.NoteTaskBubblesController.NoteTaskBubblesService
+import com.android.systemui.res.R
import com.android.wm.shell.bubbles.Bubbles
import com.google.common.truth.Truth.assertThat
import java.util.Optional
@@ -33,6 +33,9 @@
import org.mockito.Mock
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.any
+import org.mockito.kotlin.never
+import org.mockito.kotlin.whenever
/** atest SystemUITests:NoteTaskBubblesServiceTest */
@SmallTest
@@ -61,12 +64,40 @@
}
@Test
- fun showOrHideAppBubble() {
+ fun showOrHideAppBubble_defaultExpandBehavior_shouldCallBubblesApi() {
val intent = Intent()
val user = UserHandle.SYSTEM
val icon = Icon.createWithResource(context, R.drawable.ic_note_task_shortcut_widget)
+ val bubbleExpandBehavior = NoteTaskBubbleExpandBehavior.DEFAULT
+ whenever(bubbles.isBubbleExpanded(any())).thenReturn(false)
- createServiceBinder().showOrHideAppBubble(intent, user, icon)
+ createServiceBinder().showOrHideAppBubble(intent, user, icon, bubbleExpandBehavior)
+
+ verify(bubbles).showOrHideAppBubble(intent, user, icon)
+ }
+
+ @Test
+ fun showOrHideAppBubble_keepIfExpanded_bubbleShown_shouldNotCallBubblesApi() {
+ val intent = Intent().apply { setPackage("test") }
+ val user = UserHandle.SYSTEM
+ val icon = Icon.createWithResource(context, R.drawable.ic_note_task_shortcut_widget)
+ val bubbleExpandBehavior = NoteTaskBubbleExpandBehavior.KEEP_IF_EXPANDED
+ whenever(bubbles.isBubbleExpanded(any())).thenReturn(true)
+
+ createServiceBinder().showOrHideAppBubble(intent, user, icon, bubbleExpandBehavior)
+
+ verify(bubbles, never()).showOrHideAppBubble(intent, user, icon)
+ }
+
+ @Test
+ fun showOrHideAppBubble_keepIfExpanded_bubbleNotShown_shouldCallBubblesApi() {
+ val intent = Intent().apply { setPackage("test") }
+ val user = UserHandle.SYSTEM
+ val icon = Icon.createWithResource(context, R.drawable.ic_note_task_shortcut_widget)
+ val bubbleExpandBehavior = NoteTaskBubbleExpandBehavior.KEEP_IF_EXPANDED
+ whenever(bubbles.isBubbleExpanded(any())).thenReturn(false)
+
+ createServiceBinder().showOrHideAppBubble(intent, user, icon, bubbleExpandBehavior)
verify(bubbles).showOrHideAppBubble(intent, user, icon)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/notetask/NoteTaskInfoTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/notetask/NoteTaskInfoTest.kt
index 8f4078b..d3578fb 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/notetask/NoteTaskInfoTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/notetask/NoteTaskInfoTest.kt
@@ -19,6 +19,7 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.notetask.NoteTaskEntryPoint.QS_NOTES_TILE
import com.android.systemui.notetask.NoteTaskEntryPoint.WIDGET_PICKER_SHORTCUT_IN_MULTI_WINDOW_MODE
import com.google.common.truth.Truth.assertThat
import org.junit.Test
@@ -44,10 +45,19 @@
}
@Test
- fun launchMode_keyguardUnlocked_launchModeAppBubble() {
+ fun launchMode_keyguardUnlocked_launchModeAppBubble_withDefaultExpandBehavior() {
val underTest = DEFAULT_INFO.copy(isKeyguardLocked = false)
- assertThat(underTest.launchMode).isEqualTo(NoteTaskLaunchMode.AppBubble)
+ assertThat(underTest.launchMode)
+ .isEqualTo(NoteTaskLaunchMode.AppBubble(NoteTaskBubbleExpandBehavior.DEFAULT))
+ }
+
+ @Test
+ fun launchMode_keyguardUnlocked_qsTileEntryPoint_launchModeAppBubble_withKeepIfExpandedExpandBehavior() {
+ val underTest = DEFAULT_INFO.copy(isKeyguardLocked = false, entryPoint = QS_NOTES_TILE)
+
+ assertThat(underTest.launchMode)
+ .isEqualTo(NoteTaskLaunchMode.AppBubble(NoteTaskBubbleExpandBehavior.KEEP_IF_EXPANDED))
}
private companion object {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/power/PowerUITest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/power/PowerUITest.java
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
index 83f95ea..e9633f4 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
@@ -21,9 +21,6 @@
import static com.google.common.truth.Truth.assertThat;
-import static kotlinx.coroutines.flow.FlowKt.asStateFlow;
-import static kotlinx.coroutines.flow.StateFlowKt.MutableStateFlow;
-
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
@@ -39,6 +36,9 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import static kotlinx.coroutines.flow.FlowKt.asStateFlow;
+import static kotlinx.coroutines.flow.StateFlowKt.MutableStateFlow;
+
import android.content.res.Configuration;
import android.content.res.Resources;
import android.platform.test.annotations.DisableFlags;
@@ -70,9 +70,6 @@
import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController;
import com.android.systemui.util.animation.DisappearParameters;
-import kotlinx.coroutines.flow.MutableStateFlow;
-import kotlinx.coroutines.flow.StateFlow;
-
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
@@ -88,6 +85,8 @@
import javax.inject.Provider;
+import kotlinx.coroutines.flow.MutableStateFlow;
+import kotlinx.coroutines.flow.StateFlow;
import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
import platform.test.runner.parameterized.Parameters;
@@ -232,6 +231,7 @@
@After
public void tearDown() {
+ mController.destroy();
disallowTestableLooperAsMainThread();
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/QSPanelControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/QSPanelControllerTest.kt
index 02c5b5a..96f6a62 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/QSPanelControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/QSPanelControllerTest.kt
@@ -1,10 +1,10 @@
package com.android.systemui.qs
import android.content.res.Configuration
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
import android.testing.TestableResources
import android.view.ContextThemeWrapper
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
import com.android.internal.logging.MetricsLogger
import com.android.internal.logging.UiEventLogger
import com.android.systemui.SysuiTestCase
@@ -37,8 +37,8 @@
import org.mockito.Mockito.never
import org.mockito.Mockito.reset
import org.mockito.Mockito.verify
-import org.mockito.MockitoAnnotations
import org.mockito.Mockito.`when` as whenever
+import org.mockito.MockitoAnnotations
@SmallTest
@RunWith(AndroidJUnit4::class)
@@ -81,37 +81,39 @@
setShouldUseSplitShade(false)
whenever(qsPanel.resources).thenReturn(testableResources.resources)
whenever(qsPanel.context)
- .thenReturn( ContextThemeWrapper(context, R.style.Theme_SystemUI_QuickSettings))
+ .thenReturn(ContextThemeWrapper(context, R.style.Theme_SystemUI_QuickSettings))
whenever(qsPanel.getOrCreateTileLayout()).thenReturn(pagedTileLayout)
whenever(statusBarKeyguardViewManager.isPrimaryBouncerInTransit()).thenReturn(false)
whenever(qsPanel.setListening(anyBoolean())).then {
whenever(qsPanel.isListening).thenReturn(it.getArgument(0))
}
- controller = QSPanelController(
- qsPanel,
- tunerService,
- qsHost,
- qsCustomizerController,
- /* usingMediaPlayer= */ usingMediaPlayer,
- mediaHost,
- qsTileRevealControllerFactory,
- dumpManager,
- metricsLogger,
- uiEventLogger,
- qsLogger,
- brightnessControllerFactory,
- brightnessSliderFactory,
- falsingManager,
- statusBarKeyguardViewManager,
- ResourcesSplitShadeStateController(),
- longPressEffectProvider,
- mediaCarouselInteractor,
- )
+ controller =
+ QSPanelController(
+ qsPanel,
+ tunerService,
+ qsHost,
+ qsCustomizerController,
+ /* usingMediaPlayer= */ usingMediaPlayer,
+ mediaHost,
+ qsTileRevealControllerFactory,
+ dumpManager,
+ metricsLogger,
+ uiEventLogger,
+ qsLogger,
+ brightnessControllerFactory,
+ brightnessSliderFactory,
+ falsingManager,
+ statusBarKeyguardViewManager,
+ ResourcesSplitShadeStateController(),
+ longPressEffectProvider,
+ mediaCarouselInteractor,
+ )
}
@After
fun tearDown() {
+ controller.destroy()
reset(mediaHost)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/QSSecurityFooterTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/QSSecurityFooterTest.java
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt
index 369bb22..7880ace 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt
@@ -34,6 +34,7 @@
import com.android.systemui.res.R
import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController
import com.android.systemui.util.leak.RotationUtils
+import javax.inject.Provider
import org.junit.After
import org.junit.Before
import org.junit.Test
@@ -45,9 +46,8 @@
import org.mockito.Mockito.reset
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
-import org.mockito.MockitoAnnotations
-import javax.inject.Provider
import org.mockito.Mockito.`when` as whenever
+import org.mockito.MockitoAnnotations
@SmallTest
@RunWith(AndroidJUnit4::class)
@@ -108,6 +108,7 @@
@After
fun tearDown() {
controller.onViewDetached()
+ controller.destroy()
}
@Test
@@ -184,7 +185,7 @@
dumpManager,
ResourcesSplitShadeStateController(),
longPressEffectProvider,
- mediaCarouselInteractor
+ mediaCarouselInteractor,
) {
private var rotation = RotationUtils.ROTATION_NONE
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelTest.kt
index b72668b..921a8a6 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelTest.kt
@@ -41,8 +41,8 @@
import com.android.systemui.res.R
import com.android.systemui.shade.largeScreenHeaderHelper
import com.android.systemui.statusbar.StatusBarState
-import com.android.systemui.statusbar.disableflags.data.model.DisableFlagsModel
import com.android.systemui.statusbar.disableflags.data.repository.fakeDisableFlagsRepository
+import com.android.systemui.statusbar.disableflags.shared.model.DisableFlagsModel
import com.android.systemui.statusbar.sysuiStatusBarStateController
import com.android.systemui.util.animation.DisappearParameters
import com.google.common.truth.Truth.assertThat
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest_311121830.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest_311121830.kt
similarity index 98%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest_311121830.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest_311121830.kt
index 6fce108..ee2a1d5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest_311121830.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest_311121830.kt
@@ -19,10 +19,10 @@
import android.animation.AnimatorTestRule
import android.content.Context
import android.service.quicksettings.Tile
-import android.testing.AndroidTestingRunner
import android.view.ContextThemeWrapper
import android.view.View
import android.widget.ImageView
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.annotation.UiThreadTest
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
@@ -35,7 +35,7 @@
import org.junit.runner.RunWith
/** Test for regression b/311121830 and b/323125376 */
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@UiThreadTest
@SmallTest
class QSIconViewImplTest_311121830 : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/recordissue/RecordIssueDialogDelegateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/RecordIssueDialogDelegateTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/recordissue/RecordIssueDialogDelegateTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/RecordIssueDialogDelegateTest.kt
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneContainerOcclusionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneContainerOcclusionInteractorTest.kt
index bf97afe..9590816 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneContainerOcclusionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneContainerOcclusionInteractorTest.kt
@@ -27,6 +27,7 @@
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.keyguardInteractor
import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionState
@@ -194,6 +195,24 @@
.isFalse()
}
+ @Test
+ fun invisibleDueToOcclusion_isDreaming_emitsTrue() =
+ testScope.runTest {
+ val invisibleDueToOcclusion by collectLastValue(underTest.invisibleDueToOcclusion)
+
+ // Verify that we start with unoccluded
+ assertWithMessage("Should start unoccluded").that(invisibleDueToOcclusion).isFalse()
+
+ // Start dreaming, which is an occluding activity
+ showOccludingActivity()
+ kosmos.keyguardInteractor.setDreaming(true)
+
+ // Verify not invisible when dreaming
+ assertWithMessage("Should be invisible when dreaming")
+ .that(invisibleDueToOcclusion)
+ .isTrue()
+ }
+
/** Simulates the appearance of a show-when-locked `Activity` in the foreground. */
private fun TestScope.showOccludingActivity() {
keyguardOcclusionInteractor.setWmNotifiedShowWhenLockedActivityOnTop(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
index af0274b..48deee5 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
@@ -73,6 +73,7 @@
import com.android.systemui.keyguard.data.repository.keyguardRepository
import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository
import com.android.systemui.keyguard.dismissCallbackRegistry
+import com.android.systemui.keyguard.domain.interactor.dozeInteractor
import com.android.systemui.keyguard.domain.interactor.keyguardEnabledInteractor
import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
import com.android.systemui.keyguard.domain.interactor.scenetransition.lockscreenSceneTransitionInteractor
@@ -143,6 +144,8 @@
private val kosmos = testKosmos()
private val testScope = kosmos.testScope
private val deviceEntryHapticsInteractor by lazy { kosmos.deviceEntryHapticsInteractor }
+ private val dozeInteractor by lazy { kosmos.dozeInteractor }
+ private val keyguardInteractor by lazy { kosmos.keyguardInteractor }
private val sceneInteractor by lazy { kosmos.sceneInteractor }
private val sceneBackInteractor by lazy { kosmos.sceneBackInteractor }
private val bouncerInteractor by lazy { kosmos.bouncerInteractor }
@@ -373,6 +376,64 @@
}
@Test
+ fun hydrateVisibility_whileDreaming() =
+ testScope.runTest {
+ val isVisible by collectLastValue(sceneInteractor.isVisible)
+
+ // GIVEN the device is dreaming
+ val transitionState =
+ prepareState(isDeviceUnlocked = false, initialSceneKey = Scenes.Dream)
+ underTest.start()
+ assertThat(isVisible).isFalse()
+ }
+
+ @Test
+ fun hydrateVisibility_onCommunalWhileOccluded() =
+ testScope.runTest {
+ val isVisible by collectLastValue(sceneInteractor.isVisible)
+
+ kosmos.keyguardOcclusionInteractor.setWmNotifiedShowWhenLockedActivityOnTop(
+ true,
+ mock(),
+ )
+ prepareState(isDeviceUnlocked = false, initialSceneKey = Scenes.Communal)
+ underTest.start()
+ runCurrent()
+ assertThat(isVisible).isTrue()
+ }
+
+ @Test
+ fun hydrateVisibility_inCommunalTransition() =
+ testScope.runTest {
+ val isVisible by collectLastValue(sceneInteractor.isVisible)
+
+ // GIVEN the device is dreaming
+ val transitionState =
+ prepareState(
+ authenticationMethod = AuthenticationMethodModel.Pin,
+ isDeviceUnlocked = false,
+ initialSceneKey = Scenes.Dream,
+ )
+ underTest.start()
+ assertThat(isVisible).isFalse()
+
+ // WHEN a transition starts to the communal hub
+ sceneInteractor.changeScene(Scenes.Dream, "switching to dream for test")
+ transitionState.value =
+ ObservableTransitionState.Transition(
+ fromScene = Scenes.Dream,
+ toScene = Scenes.Communal,
+ currentScene = flowOf(Scenes.Dream),
+ progress = flowOf(0.5f),
+ isInitiatedByUserInput = true,
+ isUserInputOngoing = flowOf(false),
+ )
+ runCurrent()
+ // THEN scenes are visible
+ assertThat(isVisible).isTrue()
+ }
+
+ @Test
fun startsInLockscreenScene() =
testScope.runTest {
val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
@@ -643,7 +704,7 @@
fun switchToAOD_whenAvailable_whenDeviceSleepsLocked() =
testScope.runTest {
kosmos.lockscreenSceneTransitionInteractor.start()
- val asleepState by collectLastValue(kosmos.keyguardInteractor.asleepKeyguardState)
+ val asleepState by collectLastValue(keyguardInteractor.asleepKeyguardState)
val currentTransitionInfo by
collectLastValue(kosmos.keyguardTransitionRepository.currentTransitionInfoInternal)
val transitionState =
@@ -673,7 +734,7 @@
fun switchToDozing_whenAodUnavailable_whenDeviceSleepsLocked() =
testScope.runTest {
kosmos.lockscreenSceneTransitionInteractor.start()
- val asleepState by collectLastValue(kosmos.keyguardInteractor.asleepKeyguardState)
+ val asleepState by collectLastValue(keyguardInteractor.asleepKeyguardState)
val currentTransitionInfo by
collectLastValue(kosmos.keyguardTransitionRepository.currentTransitionInfoInternal)
val transitionState =
@@ -2360,6 +2421,66 @@
}
@Test
+ fun stayOnLockscreen_whenDozingStarted() =
+ testScope.runTest {
+ val currentScene by collectLastValue(sceneInteractor.currentScene)
+ prepareState()
+ assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
+ underTest.start()
+
+ // Stay on Lockscreen when dozing and dreaming
+ dozeInteractor.setIsDozing(true)
+ keyguardInteractor.setDreaming(true)
+ kosmos.fakeKeyguardRepository.setDreamingWithOverlay(false)
+ runCurrent()
+ assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
+ }
+
+ @Test
+ fun switchFromLockscreenToDream_whenDreamStarted() =
+ testScope.runTest {
+ val currentScene by collectLastValue(sceneInteractor.currentScene)
+ prepareState()
+ assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
+ underTest.start()
+
+ powerInteractor.setAwakeForTest()
+ keyguardInteractor.setDreaming(true)
+ // Move past initial delay with [KeyguardInteractor#isAbleToDream]
+ advanceTimeBy(600L)
+ runCurrent()
+ assertThat(currentScene).isEqualTo(Scenes.Dream)
+ }
+
+ @Test
+ fun switchFromDreamToLockscreen_whenLockedAndDreamStopped() =
+ testScope.runTest {
+ keyguardInteractor.setDreaming(true)
+ val currentScene by collectLastValue(sceneInteractor.currentScene)
+ prepareState(initialSceneKey = Scenes.Dream)
+ assertThat(currentScene).isEqualTo(Scenes.Dream)
+ underTest.start()
+
+ keyguardInteractor.setDreaming(false)
+ runCurrent()
+ assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
+ }
+
+ @Test
+ fun switchFromDreamToGone_whenUnlockedAndDreamStopped() =
+ testScope.runTest {
+ keyguardInteractor.setDreaming(true)
+ val currentScene by collectLastValue(sceneInteractor.currentScene)
+ prepareState(initialSceneKey = Scenes.Dream, isDeviceUnlocked = true)
+ assertThat(currentScene).isEqualTo(Scenes.Dream)
+ underTest.start()
+
+ keyguardInteractor.setDreaming(false)
+ runCurrent()
+ assertThat(currentScene).isEqualTo(Scenes.Gone)
+ }
+
+ @Test
fun replacesLockscreenSceneOnBackStack_whenUnlockdViaAlternateBouncer_fromShade() =
testScope.runTest {
val transitionState =
@@ -2411,6 +2532,64 @@
assertThat(isAlternateBouncerVisible).isFalse()
}
+ @Test
+ fun replacesLockscreenSceneOnBackStack_whenFaceUnlocked_fromShade_noAlternateBouncer() =
+ testScope.runTest {
+ val transitionState =
+ prepareState(
+ isDeviceUnlocked = false,
+ initialSceneKey = Scenes.Lockscreen,
+ authenticationMethod = AuthenticationMethodModel.Pin,
+ )
+ underTest.start()
+
+ val isUnlocked by
+ collectLastValue(
+ kosmos.deviceUnlockedInteractor.deviceUnlockStatus.map { it.isUnlocked }
+ )
+ val currentScene by collectLastValue(sceneInteractor.currentScene)
+ val backStack by collectLastValue(sceneBackInteractor.backStack)
+ val isAlternateBouncerVisible by
+ collectLastValue(kosmos.alternateBouncerInteractor.isVisible)
+ assertThat(isUnlocked).isFalse()
+ assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
+ assertThat(isAlternateBouncerVisible).isFalse()
+
+ // Change to shade.
+ sceneInteractor.changeScene(Scenes.Shade, "")
+ transitionState.value = ObservableTransitionState.Idle(Scenes.Shade)
+ runCurrent()
+ assertThat(isUnlocked).isFalse()
+ assertThat(currentScene).isEqualTo(Scenes.Shade)
+ assertThat(backStack?.asIterable()?.first()).isEqualTo(Scenes.Lockscreen)
+ assertThat(isAlternateBouncerVisible).isFalse()
+
+ // Show the alternate bouncer.
+ kosmos.alternateBouncerInteractor.forceShow()
+ kosmos.sysuiStatusBarStateController.leaveOpen = true // leave shade open
+ runCurrent()
+ assertThat(isUnlocked).isFalse()
+ assertThat(currentScene).isEqualTo(Scenes.Shade)
+ assertThat(backStack?.asIterable()?.first()).isEqualTo(Scenes.Lockscreen)
+ assertThat(isAlternateBouncerVisible).isTrue()
+
+ // Simulate race condition by hiding the alternate bouncer *before* the face unlock:
+ kosmos.alternateBouncerInteractor.hide()
+ runCurrent()
+ assertThat(isUnlocked).isFalse()
+ assertThat(currentScene).isEqualTo(Scenes.Shade)
+ assertThat(backStack?.asIterable()?.first()).isEqualTo(Scenes.Lockscreen)
+ assertThat(isAlternateBouncerVisible).isFalse()
+
+ // Trigger a face unlock.
+ updateFaceAuthStatus(isSuccess = true)
+ runCurrent()
+ assertThat(isUnlocked).isTrue()
+ assertThat(currentScene).isEqualTo(Scenes.Shade)
+ assertThat(backStack?.asIterable()?.first()).isEqualTo(Scenes.Gone)
+ assertThat(isAlternateBouncerVisible).isFalse()
+ }
+
private fun TestScope.emulateSceneTransition(
transitionStateFlow: MutableStateFlow<ObservableTransitionState>,
toScene: SceneKey,
@@ -2647,15 +2826,16 @@
}
private fun updateFaceAuthStatus(isSuccess: Boolean) {
- if (isSuccess) {
- kosmos.fakeDeviceEntryFaceAuthRepository.setAuthenticationStatus(
- SuccessFaceAuthenticationStatus(
- successResult = Mockito.mock(FaceManager.AuthenticationResult::class.java)
- )
- )
- } else {
- kosmos.fakeDeviceEntryFaceAuthRepository.setAuthenticationStatus(
- FailedFaceAuthenticationStatus()
+ with(kosmos.fakeDeviceEntryFaceAuthRepository) {
+ isAuthenticated.value = isSuccess
+ setAuthenticationStatus(
+ if (isSuccess) {
+ SuccessFaceAuthenticationStatus(
+ successResult = Mockito.mock(FaceManager.AuthenticationResult::class.java)
+ )
+ } else {
+ FailedFaceAuthenticationStatus()
+ }
)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionExecutorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/ActionExecutorTest.kt
similarity index 96%
rename from packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionExecutorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/ActionExecutorTest.kt
index a10d81f..1413204 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionExecutorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/ActionExecutorTest.kt
@@ -20,8 +20,8 @@
import android.content.Intent
import android.os.Bundle
import android.os.UserHandle
-import android.testing.AndroidTestingRunner
import android.view.Window
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.util.mockito.argumentCaptor
@@ -39,7 +39,7 @@
import org.mockito.kotlin.verify
import org.mockito.kotlin.verifyBlocking
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
class ActionExecutorTest : SysuiTestCase() {
private val scheduler = TestCoroutineScheduler()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotDetectionControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/ScreenshotDetectionControllerTest.kt
similarity index 98%
rename from packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotDetectionControllerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/ScreenshotDetectionControllerTest.kt
index c5070286..84b7d10 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotDetectionControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/ScreenshotDetectionControllerTest.kt
@@ -21,10 +21,10 @@
import android.content.pm.PackageManager
import android.content.pm.PackageManager.MATCH_ANY_USER
import android.content.pm.PackageManager.MATCH_DISABLED_COMPONENTS
-import android.testing.AndroidTestingRunner
import android.view.Display
import android.view.IWindowManager
import android.view.WindowManager
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.argThat
@@ -43,7 +43,7 @@
import org.mockito.MockitoAnnotations
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class ScreenshotDetectionControllerTest {
@Mock lateinit var windowManager: IWindowManager
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotServiceTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/TakeScreenshotServiceTest.kt
similarity index 98%
rename from packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotServiceTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/TakeScreenshotServiceTest.kt
index 77b5c91..d2eca0d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotServiceTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/TakeScreenshotServiceTest.kt
@@ -23,9 +23,9 @@
import android.net.Uri
import android.os.UserHandle
import android.os.UserManager
-import android.testing.AndroidTestingRunner
import android.view.WindowManager.ScreenshotSource.SCREENSHOT_KEY_OTHER
import android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN
+import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.internal.logging.testing.UiEventLoggerFake
import com.android.internal.util.ScreenshotRequest
import com.android.systemui.SysuiTestCase
@@ -49,7 +49,7 @@
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class TakeScreenshotServiceTest : SysuiTestCase() {
private val userManager = mock<UserManager> { on { isUserUnlocked } doReturn (true) }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/policy/PolicyRequestProcessorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/policy/PolicyRequestProcessorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/screenshot/policy/PolicyRequestProcessorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/policy/PolicyRequestProcessorTest.kt
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
index 01c17bd..94a19c8 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
@@ -148,7 +148,6 @@
import com.android.systemui.statusbar.StatusBarStateControllerImpl;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.VibratorHelper;
-import com.android.systemui.statusbar.disableflags.data.repository.FakeDisableFlagsRepository;
import com.android.systemui.statusbar.notification.ConversationNotificationManager;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.HeadsUpTouchHelper;
@@ -428,7 +427,7 @@
mShadeInteractor = new ShadeInteractorImpl(
mTestScope.getBackgroundScope(),
mKosmos.getDeviceProvisioningInteractor(),
- new FakeDisableFlagsRepository(),
+ mKosmos.getDisableFlagsInteractor(),
mDozeParameters,
mFakeKeyguardRepository,
mKeyguardTransitionInteractor,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/QuickSettingsControllerImplBaseTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/QuickSettingsControllerImplBaseTest.java
index 443595d..ef132d5 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/QuickSettingsControllerImplBaseTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/QuickSettingsControllerImplBaseTest.java
@@ -149,7 +149,7 @@
@Mock protected LargeScreenHeaderHelper mLargeScreenHeaderHelper;
protected FakeDisableFlagsRepository mDisableFlagsRepository =
- new FakeDisableFlagsRepository();
+ mKosmos.getFakeDisableFlagsRepository();
protected FakeKeyguardRepository mKeyguardRepository = new FakeKeyguardRepository();
protected FakeShadeRepository mShadeRepository = new FakeShadeRepository();
@@ -185,7 +185,7 @@
mShadeInteractor = new ShadeInteractorImpl(
mTestScope.getBackgroundScope(),
mKosmos.getDeviceProvisioningInteractor(),
- mDisableFlagsRepository,
+ mKosmos.getDisableFlagsInteractor(),
mDozeParameters,
mKeyguardRepository,
keyguardTransitionInteractor,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/QuickSettingsControllerImplWithCoroutinesTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/QuickSettingsControllerImplWithCoroutinesTest.kt
index 46961d4..ee3f801 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/QuickSettingsControllerImplWithCoroutinesTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/QuickSettingsControllerImplWithCoroutinesTest.kt
@@ -19,7 +19,7 @@
import android.app.StatusBarManager
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.statusbar.disableflags.data.model.DisableFlagsModel
+import com.android.systemui.statusbar.disableflags.shared.model.DisableFlagsModel
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.cancelChildren
@@ -61,7 +61,7 @@
mDisableFlagsRepository.disableFlags.value =
DisableFlagsModel(
StatusBarManager.DISABLE_NONE,
- StatusBarManager.DISABLE2_QUICK_SETTINGS
+ StatusBarManager.DISABLE2_QUICK_SETTINGS,
)
runCurrent()
@@ -76,7 +76,7 @@
mDisableFlagsRepository.disableFlags.value =
DisableFlagsModel(
StatusBarManager.DISABLE_NONE,
- StatusBarManager.DISABLE2_NOTIFICATION_SHADE
+ StatusBarManager.DISABLE2_NOTIFICATION_SHADE,
)
runCurrent()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/carrier/ShadeCarrierGroupControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/carrier/ShadeCarrierGroupControllerTest.java
index 9a8df33..cd55bb2 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/carrier/ShadeCarrierGroupControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/carrier/ShadeCarrierGroupControllerTest.java
@@ -38,11 +38,11 @@
import android.content.Intent;
import android.os.Handler;
import android.provider.Settings;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.View;
import android.widget.TextView;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.keyguard.CarrierTextManager;
@@ -76,7 +76,7 @@
import java.util.Arrays;
import java.util.List;
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@TestableLooper.RunWithLooper
@SmallTest
public class ShadeCarrierGroupControllerTest extends LeakCheckedTest {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadePositionRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepositoryTest.kt
similarity index 95%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadePositionRepositoryTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepositoryTest.kt
index a9a5cac..4e7839e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadePositionRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepositoryTest.kt
@@ -34,13 +34,13 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
-class ShadePositionRepositoryTest : SysuiTestCase() {
+class ShadeDisplaysRepositoryTest : SysuiTestCase() {
private val kosmos = testKosmos()
private val testScope = kosmos.testScope
private val commandRegistry = kosmos.commandRegistry
private val pw = PrintWriter(StringWriter())
- private val underTest = ShadePositionRepositoryImpl(commandRegistry)
+ private val underTest = ShadeDisplaysRepositoryImpl(commandRegistry)
@Before
fun setUp() {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeDisplaysInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeDisplaysInteractorTest.kt
new file mode 100644
index 0000000..8ef1e56
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeDisplaysInteractorTest.kt
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.shade.domain.interactor
+
+import android.content.Context
+import android.content.res.Configuration
+import android.content.res.Resources
+import android.view.Display
+import android.view.WindowManager
+import android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.display.data.repository.FakeDisplayWindowPropertiesRepository
+import com.android.systemui.display.shared.model.DisplayWindowProperties
+import com.android.systemui.scene.ui.view.WindowRootView
+import com.android.systemui.shade.data.repository.FakeShadeDisplayRepository
+import com.android.systemui.statusbar.phone.ConfigurationForwarder
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.advanceUntilIdle
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.verify
+import org.mockito.kotlin.any
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.verifyNoMoreInteractions
+import org.mockito.kotlin.whenever
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class ShadeDisplaysInteractorTest : SysuiTestCase() {
+
+ private val shadeRootview = mock<WindowRootView>()
+ private val positionRepository = FakeShadeDisplayRepository()
+ private val defaultContext = mock<Context>()
+ private val secondaryContext = mock<Context>()
+ private val contextStore = FakeDisplayWindowPropertiesRepository()
+ private val testScope = TestScope(UnconfinedTestDispatcher())
+ private val configurationForwarder = mock<ConfigurationForwarder>()
+ private val defaultWm = mock<WindowManager>()
+ private val secondaryWm = mock<WindowManager>()
+ private val resources = mock<Resources>()
+ private val configuration = mock<Configuration>()
+ private val display = mock<Display>()
+
+ private val interactor =
+ ShadeDisplaysInteractor(
+ shadeRootview,
+ positionRepository,
+ defaultContext,
+ contextStore,
+ testScope,
+ configurationForwarder,
+ testScope.coroutineContext,
+ )
+
+ @Before
+ fun setup() {
+ whenever(shadeRootview.display).thenReturn(display)
+ whenever(display.displayId).thenReturn(0)
+
+ whenever(resources.configuration).thenReturn(configuration)
+ whenever(resources.configuration).thenReturn(configuration)
+
+ whenever(defaultContext.displayId).thenReturn(0)
+ whenever(defaultContext.getSystemService(any())).thenReturn(defaultWm)
+ whenever(defaultContext.resources).thenReturn(resources)
+ contextStore.insert(
+ DisplayWindowProperties(
+ displayId = 0,
+ windowType = TYPE_NOTIFICATION_SHADE,
+ context = defaultContext,
+ windowManager = defaultWm,
+ layoutInflater = mock(),
+ )
+ )
+
+ whenever(secondaryContext.displayId).thenReturn(1)
+ whenever(secondaryContext.getSystemService(any())).thenReturn(secondaryWm)
+ whenever(secondaryContext.resources).thenReturn(resources)
+ contextStore.insert(
+ DisplayWindowProperties(
+ displayId = 1,
+ windowType = TYPE_NOTIFICATION_SHADE,
+ context = secondaryContext,
+ windowManager = secondaryWm,
+ layoutInflater = mock(),
+ )
+ )
+ }
+
+ @Test
+ fun start_shadeInCorrectPosition_notAddedOrRemoved() {
+ whenever(display.displayId).thenReturn(0)
+ positionRepository.setDisplayId(0)
+ interactor.start()
+ testScope.advanceUntilIdle()
+
+ verifyNoMoreInteractions(defaultWm)
+ verifyNoMoreInteractions(secondaryWm)
+ }
+
+ @Test
+ fun start_shadeInWrongPosition_changes() {
+ whenever(display.displayId).thenReturn(0)
+ positionRepository.setDisplayId(1)
+ interactor.start()
+ testScope.advanceUntilIdle()
+
+ verify(defaultWm).removeView(eq(shadeRootview))
+ verify(secondaryWm).addView(eq(shadeRootview), any())
+ }
+
+ @Test
+ fun start_shadePositionChanges_removedThenAdded() {
+ whenever(display.displayId).thenReturn(0)
+ positionRepository.setDisplayId(0)
+ interactor.start()
+ testScope.advanceUntilIdle()
+
+ positionRepository.setDisplayId(1)
+ testScope.advanceUntilIdle()
+
+ verify(defaultWm).removeView(eq(shadeRootview))
+ verify(secondaryWm).addView(eq(shadeRootview), any())
+ }
+
+ @Test
+ fun start_shadePositionChanges_newConfigPropagated() {
+ whenever(display.displayId).thenReturn(0)
+ positionRepository.setDisplayId(0)
+ interactor.start()
+ testScope.advanceUntilIdle()
+
+ positionRepository.setDisplayId(1)
+ testScope.advanceUntilIdle()
+
+ verify(configurationForwarder).onConfigurationChanged(eq(configuration))
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImplTest.kt
index 19ac0cf..da652c4 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImplTest.kt
@@ -37,8 +37,8 @@
import com.android.systemui.power.shared.model.WakeSleepReason
import com.android.systemui.power.shared.model.WakefulnessState
import com.android.systemui.shade.shadeTestUtil
-import com.android.systemui.statusbar.disableflags.data.model.DisableFlagsModel
import com.android.systemui.statusbar.disableflags.data.repository.fakeDisableFlagsRepository
+import com.android.systemui.statusbar.disableflags.shared.model.DisableFlagsModel
import com.android.systemui.statusbar.phone.dozeParameters
import com.android.systemui.statusbar.policy.data.repository.fakeDeviceProvisioningRepository
import com.android.systemui.statusbar.policy.data.repository.fakeUserSetupRepository
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/CommandQueueTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/CommandQueueTest.java
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java
index fb7252b..60a1855 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java
@@ -16,8 +16,6 @@
package com.android.systemui.statusbar;
-import static android.app.Notification.CATEGORY_CALL;
-
import static com.google.common.truth.Truth.assertThat;
import static junit.framework.Assert.assertEquals;
@@ -199,19 +197,6 @@
}
@Test
- public void testContentDescForNotification_noNotifContent() {
- Notification n = new Notification.Builder(mContext, "test")
- .setSmallIcon(0)
- .setContentTitle("hello")
- .setCategory(CATEGORY_CALL)
- .build();
- assertThat(NotificationContentDescription.contentDescForNotification(mContext, n)
- .toString()).startsWith("com.android.systemui.tests notification");
- assertThat(NotificationContentDescription.contentDescForNotification(mContext, n)
- .toString()).doesNotContain("hello");
- }
-
- @Test
@EnableFlags({Flags.FLAG_MODES_UI, Flags.FLAG_MODES_UI_ICONS})
public void setIcon_withPreloaded_usesPreloaded() {
Icon mockIcon = mock(Icon.class);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/CallbackHandlerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/connectivity/CallbackHandlerTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/CallbackHandlerTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/connectivity/CallbackHandlerTest.java
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/disableflags/data/repository/DisableFlagsRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/disableflags/data/repository/DisableFlagsRepositoryTest.kt
index 907c684..cd07821 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/disableflags/data/repository/DisableFlagsRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/disableflags/data/repository/DisableFlagsRepositoryTest.kt
@@ -32,7 +32,7 @@
import com.android.systemui.res.R
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.disableflags.DisableFlagsLogger
-import com.android.systemui.statusbar.disableflags.data.model.DisableFlagsModel
+import com.android.systemui.statusbar.disableflags.shared.model.DisableFlagsModel
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler
import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/PropertyAnimatorTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/PropertyAnimatorTest.java
similarity index 98%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/PropertyAnimatorTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/PropertyAnimatorTest.java
index c8ef663..e974c2e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/PropertyAnimatorTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/PropertyAnimatorTest.java
@@ -30,13 +30,13 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
-import android.testing.AndroidTestingRunner;
import android.util.FloatProperty;
import android.util.Property;
import android.view.View;
import android.view.animation.Interpolator;
import androidx.test.annotation.UiThreadTest;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.app.animation.Interpolators;
@@ -51,7 +51,7 @@
import org.junit.runner.RunWith;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@UiThreadTest
public class PropertyAnimatorTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
similarity index 99%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
index e21a005..4ef9792 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
@@ -68,12 +68,12 @@
import android.service.notification.NotificationListenerService.Ranking;
import android.service.notification.NotificationListenerService.RankingMap;
import android.service.notification.StatusBarNotification;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.util.ArrayMap;
import android.util.ArraySet;
import androidx.annotation.NonNull;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.internal.statusbar.IStatusBarService;
@@ -118,7 +118,7 @@
import java.util.Map;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@TestableLooper.RunWithLooper
public class NotifCollectionTest extends SysuiTestCase {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt
index 9613f76..2c488e3 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt
@@ -28,7 +28,6 @@
import com.android.systemui.statusbar.NotificationRemoteInputManager
import com.android.systemui.statusbar.chips.notification.domain.interactor.statusBarNotificationChipsInteractor
import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
-import com.android.systemui.statusbar.notification.HeadsUpManagerPhone
import com.android.systemui.statusbar.notification.NotifPipelineFlags
import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder
import com.android.systemui.statusbar.notification.collection.NotifPipeline
@@ -52,6 +51,7 @@
import com.android.systemui.statusbar.notification.interruption.VisualInterruptionDecisionProvider
import com.android.systemui.statusbar.notification.row.NotifBindPipeline.BindCallback
import com.android.systemui.statusbar.phone.NotificationGroupTestHelper
+import com.android.systemui.statusbar.policy.BaseHeadsUpManager
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener
import com.android.systemui.testKosmos
import com.android.systemui.util.concurrency.FakeExecutor
@@ -101,7 +101,7 @@
private val notifPipeline: NotifPipeline = mock()
private val logger = HeadsUpCoordinatorLogger(logcatLogBuffer(), verbose = true)
- private val headsUpManager: HeadsUpManagerPhone = mock()
+ private val headsUpManager: BaseHeadsUpManager = mock()
private val headsUpViewBinder: HeadsUpViewBinder = mock()
private val visualInterruptionDecisionProvider: VisualInterruptionDecisionProvider = mock()
private val remoteInputManager: NotificationRemoteInputManager = mock()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationAlertsInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationAlertsInteractorTest.kt
index 79ff4be..7eac7e8 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationAlertsInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationAlertsInteractorTest.kt
@@ -21,8 +21,8 @@
import com.android.systemui.SysUITestModule
import com.android.systemui.SysuiTestCase
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.statusbar.disableflags.data.model.DisableFlagsModel
import com.android.systemui.statusbar.disableflags.data.repository.FakeDisableFlagsRepository
+import com.android.systemui.statusbar.disableflags.shared.model.DisableFlagsModel
import com.google.common.truth.Truth.assertThat
import dagger.BindsInstance
import dagger.Component
@@ -51,10 +51,7 @@
fun disableFlags_notifAlertsNotDisabled_notifAlertsEnabledTrue() =
with(testComponent) {
disableFlags.disableFlags.value =
- DisableFlagsModel(
- StatusBarManager.DISABLE_NONE,
- StatusBarManager.DISABLE2_NONE,
- )
+ DisableFlagsModel(StatusBarManager.DISABLE_NONE, StatusBarManager.DISABLE2_NONE)
assertThat(underTest.areNotificationAlertsEnabled()).isTrue()
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifLayoutInflaterFactoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotifLayoutInflaterFactoryTest.kt
similarity index 98%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifLayoutInflaterFactoryTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotifLayoutInflaterFactoryTest.kt
index fd41921..371e1c5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifLayoutInflaterFactoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotifLayoutInflaterFactoryTest.kt
@@ -16,13 +16,13 @@
package com.android.systemui.statusbar.notification.row
import android.content.Context
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
import android.util.AttributeSet
import android.view.View
import android.widget.Button
import android.widget.FrameLayout
import android.widget.LinearLayout
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_CONTRACTED
@@ -38,7 +38,7 @@
/** Tests for [NotifLayoutInflaterFactory] */
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@RunWithLooper
class NotifLayoutInflaterFactoryTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
similarity index 98%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
index 3669e3d..b8745b3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
@@ -21,12 +21,12 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -47,7 +47,7 @@
import org.mockito.junit.MockitoRule;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
public class NotificationSectionsManagerTest extends SysuiTestCase {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt
index f6d439a..5a77f3d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt
@@ -17,7 +17,6 @@
package com.android.systemui.statusbar.pipeline.mobile.domain.interactor
import android.os.ParcelUuid
-import android.telephony.SubscriptionManager
import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID
import android.telephony.SubscriptionManager.PROFILE_CLASS_PROVISIONING
import android.telephony.SubscriptionManager.PROFILE_CLASS_UNSET
@@ -25,91 +24,78 @@
import androidx.test.filters.SmallTest
import com.android.settingslib.mobile.MobileMappings
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.log.table.logcatTableLogBuffer
+import com.android.systemui.flags.fake
+import com.android.systemui.flags.featureFlagsClassic
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.kosmos.collectLastValue
+import com.android.systemui.kosmos.runCurrent
+import com.android.systemui.kosmos.runTest
+import com.android.systemui.kosmos.testScope
import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionRepository
-import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository
-import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.fake
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.mobileConnectionsRepository
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.mobileConnectionsRepositoryLogbufferName
import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlot
-import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
+import com.android.systemui.statusbar.pipeline.shared.data.repository.connectivityRepository
+import com.android.systemui.statusbar.pipeline.shared.data.repository.fake
import com.android.systemui.statusbar.policy.data.repository.FakeUserSetupRepository
import com.android.systemui.testKosmos
import com.android.systemui.util.CarrierConfigTracker
-import com.android.systemui.util.mockito.mock
-import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import java.util.UUID
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.StandardTestDispatcher
-import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.advanceTimeBy
-import kotlinx.coroutines.test.runCurrent
-import kotlinx.coroutines.test.runTest
-import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.Mock
-import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.whenever
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(AndroidJUnit4::class)
class MobileIconsInteractorTest : SysuiTestCase() {
- private val kosmos = testKosmos()
-
- private lateinit var underTest: MobileIconsInteractor
- private lateinit var connectivityRepository: FakeConnectivityRepository
- private lateinit var connectionsRepository: FakeMobileConnectionsRepository
- private val userSetupRepository = FakeUserSetupRepository()
- private val mobileMappingsProxy = FakeMobileMappingsProxy()
- private val flags =
- FakeFeatureFlagsClassic().apply {
- set(Flags.FILTER_PROVISIONING_NETWORK_SUBSCRIPTIONS, true)
+ private val kosmos by lazy {
+ testKosmos().apply {
+ mobileConnectionsRepositoryLogbufferName = "MobileIconsInteractorTest"
+ mobileConnectionsRepository.fake.run {
+ setMobileConnectionRepositoryMap(
+ mapOf(
+ SUB_1_ID to CONNECTION_1,
+ SUB_2_ID to CONNECTION_2,
+ SUB_3_ID to CONNECTION_3,
+ SUB_4_ID to CONNECTION_4,
+ )
+ )
+ setActiveMobileDataSubscriptionId(SUB_1_ID)
+ }
+ featureFlagsClassic.fake.set(Flags.FILTER_PROVISIONING_NETWORK_SUBSCRIPTIONS, true)
}
+ }
- private val testDispatcher = StandardTestDispatcher()
- private val testScope = TestScope(testDispatcher)
+ // shortcut rename
+ private val Kosmos.connectionsRepository by Fixture { mobileConnectionsRepository.fake }
- private val tableLogBuffer = logcatTableLogBuffer(kosmos, "MobileIconsInteractorTest")
+ private val Kosmos.carrierConfigTracker by Fixture { mock<CarrierConfigTracker>() }
- @Mock private lateinit var carrierConfigTracker: CarrierConfigTracker
-
- @Before
- fun setUp() {
- MockitoAnnotations.initMocks(this)
-
- connectivityRepository = FakeConnectivityRepository()
-
- connectionsRepository = FakeMobileConnectionsRepository(mobileMappingsProxy, tableLogBuffer)
- connectionsRepository.setMobileConnectionRepositoryMap(
- mapOf(
- SUB_1_ID to CONNECTION_1,
- SUB_2_ID to CONNECTION_2,
- SUB_3_ID to CONNECTION_3,
- SUB_4_ID to CONNECTION_4,
- )
+ private val Kosmos.underTest by Fixture {
+ MobileIconsInteractorImpl(
+ mobileConnectionsRepository,
+ carrierConfigTracker,
+ tableLogger = mock(),
+ connectivityRepository,
+ FakeUserSetupRepository(),
+ testScope.backgroundScope,
+ context,
+ featureFlagsClassic,
)
- connectionsRepository.setActiveMobileDataSubscriptionId(SUB_1_ID)
-
- underTest =
- MobileIconsInteractorImpl(
- connectionsRepository,
- carrierConfigTracker,
- tableLogger = mock(),
- connectivityRepository,
- userSetupRepository,
- testScope.backgroundScope,
- context,
- flags,
- )
}
@Test
fun filteredSubscriptions_default() =
- testScope.runTest {
+ kosmos.runTest {
val latest by collectLastValue(underTest.filteredSubscriptions)
assertThat(latest).isEqualTo(listOf<SubscriptionModel>())
@@ -118,7 +104,7 @@
// Based on the logic from the old pipeline, we'll never filter subs when there are more than 2
@Test
fun filteredSubscriptions_moreThanTwo_doesNotFilter() =
- testScope.runTest {
+ kosmos.runTest {
connectionsRepository.setSubscriptions(listOf(SUB_1, SUB_3_OPP, SUB_4_OPP))
connectionsRepository.setActiveMobileDataSubscriptionId(SUB_4_ID)
@@ -129,7 +115,7 @@
@Test
fun filteredSubscriptions_nonOpportunistic_updatesWithMultipleSubs() =
- testScope.runTest {
+ kosmos.runTest {
connectionsRepository.setSubscriptions(listOf(SUB_1, SUB_2))
val latest by collectLastValue(underTest.filteredSubscriptions)
@@ -139,7 +125,7 @@
@Test
fun filteredSubscriptions_opportunistic_differentGroups_doesNotFilter() =
- testScope.runTest {
+ kosmos.runTest {
connectionsRepository.setSubscriptions(listOf(SUB_3_OPP, SUB_4_OPP))
connectionsRepository.setActiveMobileDataSubscriptionId(SUB_3_ID)
@@ -150,7 +136,7 @@
@Test
fun filteredSubscriptions_opportunistic_nonGrouped_doesNotFilter() =
- testScope.runTest {
+ kosmos.runTest {
val (sub1, sub2) =
createSubscriptionPair(
subscriptionIds = Pair(SUB_1_ID, SUB_2_ID),
@@ -167,7 +153,7 @@
@Test
fun filteredSubscriptions_opportunistic_grouped_configFalse_showsActive_3() =
- testScope.runTest {
+ kosmos.runTest {
val (sub3, sub4) =
createSubscriptionPair(
subscriptionIds = Pair(SUB_3_ID, SUB_4_ID),
@@ -187,7 +173,7 @@
@Test
fun filteredSubscriptions_opportunistic_grouped_configFalse_showsActive_4() =
- testScope.runTest {
+ kosmos.runTest {
val (sub3, sub4) =
createSubscriptionPair(
subscriptionIds = Pair(SUB_3_ID, SUB_4_ID),
@@ -207,7 +193,7 @@
@Test
fun filteredSubscriptions_oneOpportunistic_grouped_configTrue_showsPrimary_active_1() =
- testScope.runTest {
+ kosmos.runTest {
val (sub1, sub3) =
createSubscriptionPair(
subscriptionIds = Pair(SUB_1_ID, SUB_3_ID),
@@ -228,7 +214,7 @@
@Test
fun filteredSubscriptions_oneOpportunistic_grouped_configTrue_showsPrimary_nonActive_1() =
- testScope.runTest {
+ kosmos.runTest {
val (sub1, sub3) =
createSubscriptionPair(
subscriptionIds = Pair(SUB_1_ID, SUB_3_ID),
@@ -249,7 +235,7 @@
@Test
fun filteredSubscriptions_vcnSubId_agreesWithActiveSubId_usesActiveAkaVcnSub() =
- testScope.runTest {
+ kosmos.runTest {
val (sub1, sub3) =
createSubscriptionPair(
subscriptionIds = Pair(SUB_1_ID, SUB_3_ID),
@@ -258,7 +244,7 @@
)
connectionsRepository.setSubscriptions(listOf(sub1, sub3))
connectionsRepository.setActiveMobileDataSubscriptionId(SUB_3_ID)
- connectivityRepository.vcnSubId.value = SUB_3_ID
+ kosmos.connectivityRepository.fake.vcnSubId.value = SUB_3_ID
whenever(carrierConfigTracker.alwaysShowPrimarySignalBarInOpportunisticNetworkDefault)
.thenReturn(false)
@@ -269,7 +255,7 @@
@Test
fun filteredSubscriptions_vcnSubId_disagreesWithActiveSubId_usesVcnSub() =
- testScope.runTest {
+ kosmos.runTest {
val (sub1, sub3) =
createSubscriptionPair(
subscriptionIds = Pair(SUB_1_ID, SUB_3_ID),
@@ -278,7 +264,7 @@
)
connectionsRepository.setSubscriptions(listOf(sub1, sub3))
connectionsRepository.setActiveMobileDataSubscriptionId(SUB_3_ID)
- connectivityRepository.vcnSubId.value = SUB_1_ID
+ kosmos.connectivityRepository.fake.vcnSubId.value = SUB_1_ID
whenever(carrierConfigTracker.alwaysShowPrimarySignalBarInOpportunisticNetworkDefault)
.thenReturn(false)
@@ -289,9 +275,9 @@
@Test
fun filteredSubscriptions_doesNotFilterProvisioningWhenFlagIsFalse() =
- testScope.runTest {
+ kosmos.runTest {
// GIVEN the flag is false
- flags.set(Flags.FILTER_PROVISIONING_NETWORK_SUBSCRIPTIONS, false)
+ featureFlagsClassic.fake.set(Flags.FILTER_PROVISIONING_NETWORK_SUBSCRIPTIONS, false)
// GIVEN 1 sub that is in PROFILE_CLASS_PROVISIONING
val sub1 =
@@ -313,7 +299,7 @@
@Test
fun filteredSubscriptions_filtersOutProvisioningSubs() =
- testScope.runTest {
+ kosmos.runTest {
val sub1 =
SubscriptionModel(
subscriptionId = SUB_1_ID,
@@ -326,7 +312,7 @@
subscriptionId = SUB_2_ID,
isOpportunistic = false,
carrierName = "Carrier 2",
- profileClass = SubscriptionManager.PROFILE_CLASS_PROVISIONING,
+ profileClass = PROFILE_CLASS_PROVISIONING,
)
connectionsRepository.setSubscriptions(listOf(sub1, sub2))
@@ -339,7 +325,7 @@
/** Note: I'm not sure if this will ever be the case, but we can test it at least */
@Test
fun filteredSubscriptions_filtersOutProvisioningSubsBeforeOpportunistic() =
- testScope.runTest {
+ kosmos.runTest {
// This is a contrived test case, where the active subId is the one that would
// also be filtered by opportunistic filtering.
@@ -376,7 +362,7 @@
@Test
fun filteredSubscriptions_groupedPairAndNonProvisioned_groupedFilteringStillHappens() =
- testScope.runTest {
+ kosmos.runTest {
// Grouped filtering only happens when the list of subs is length 2. In this case
// we'll show that filtering of provisioning subs happens before, and thus grouped
// filtering happens even though the unfiltered list is length 3
@@ -406,7 +392,7 @@
@Test
fun filteredSubscriptions_subNotExclusivelyNonTerrestrial_hasSub() =
- testScope.runTest {
+ kosmos.runTest {
val notExclusivelyNonTerrestrialSub =
SubscriptionModel(
isExclusivelyNonTerrestrial = false,
@@ -424,7 +410,7 @@
@Test
fun filteredSubscriptions_subExclusivelyNonTerrestrial_doesNotHaveSub() =
- testScope.runTest {
+ kosmos.runTest {
val exclusivelyNonTerrestrialSub =
SubscriptionModel(
isExclusivelyNonTerrestrial = true,
@@ -442,7 +428,7 @@
@Test
fun filteredSubscription_mixOfExclusivelyNonTerrestrialAndOther_hasOtherSubsOnly() =
- testScope.runTest {
+ kosmos.runTest {
val exclusivelyNonTerrestrialSub =
SubscriptionModel(
isExclusivelyNonTerrestrial = true,
@@ -476,7 +462,7 @@
@Test
fun filteredSubscriptions_exclusivelyNonTerrestrialSub_andOpportunistic_bothFiltersHappen() =
- testScope.runTest {
+ kosmos.runTest {
// Exclusively non-terrestrial sub
val exclusivelyNonTerrestrialSub =
SubscriptionModel(
@@ -507,7 +493,7 @@
@Test
fun activeDataConnection_turnedOn() =
- testScope.runTest {
+ kosmos.runTest {
CONNECTION_1.setDataEnabled(true)
val latest by collectLastValue(underTest.activeDataConnectionHasDataEnabled)
@@ -517,7 +503,7 @@
@Test
fun activeDataConnection_turnedOff() =
- testScope.runTest {
+ kosmos.runTest {
CONNECTION_1.setDataEnabled(true)
val latest by collectLastValue(underTest.activeDataConnectionHasDataEnabled)
@@ -528,7 +514,7 @@
@Test
fun activeDataConnection_invalidSubId() =
- testScope.runTest {
+ kosmos.runTest {
val latest by collectLastValue(underTest.activeDataConnectionHasDataEnabled)
connectionsRepository.setActiveMobileDataSubscriptionId(INVALID_SUBSCRIPTION_ID)
@@ -539,7 +525,7 @@
@Test
fun failedConnection_default_validated_notFailed() =
- testScope.runTest {
+ kosmos.runTest {
val latest by collectLastValue(underTest.isDefaultConnectionFailed)
connectionsRepository.mobileIsDefault.value = true
@@ -550,7 +536,7 @@
@Test
fun failedConnection_notDefault_notValidated_notFailed() =
- testScope.runTest {
+ kosmos.runTest {
val latest by collectLastValue(underTest.isDefaultConnectionFailed)
connectionsRepository.mobileIsDefault.value = false
@@ -561,7 +547,7 @@
@Test
fun failedConnection_default_notValidated_failed() =
- testScope.runTest {
+ kosmos.runTest {
val latest by collectLastValue(underTest.isDefaultConnectionFailed)
connectionsRepository.mobileIsDefault.value = true
@@ -572,7 +558,7 @@
@Test
fun failedConnection_carrierMergedDefault_notValidated_failed() =
- testScope.runTest {
+ kosmos.runTest {
val latest by collectLastValue(underTest.isDefaultConnectionFailed)
connectionsRepository.hasCarrierMergedConnection.value = true
@@ -584,7 +570,7 @@
/** Regression test for b/275076959. */
@Test
fun failedConnection_dataSwitchInSameGroup_notFailed() =
- testScope.runTest {
+ kosmos.runTest {
val latest by collectLastValue(underTest.isDefaultConnectionFailed)
connectionsRepository.mobileIsDefault.value = true
@@ -602,7 +588,7 @@
@Test
fun failedConnection_dataSwitchNotInSameGroup_isFailed() =
- testScope.runTest {
+ kosmos.runTest {
val latest by collectLastValue(underTest.isDefaultConnectionFailed)
connectionsRepository.mobileIsDefault.value = true
@@ -618,7 +604,7 @@
@Test
fun alwaysShowDataRatIcon_configHasTrue() =
- testScope.runTest {
+ kosmos.runTest {
val latest by collectLastValue(underTest.alwaysShowDataRatIcon)
val config = MobileMappings.Config()
@@ -630,7 +616,7 @@
@Test
fun alwaysShowDataRatIcon_configHasFalse() =
- testScope.runTest {
+ kosmos.runTest {
val latest by collectLastValue(underTest.alwaysShowDataRatIcon)
val config = MobileMappings.Config()
@@ -642,7 +628,7 @@
@Test
fun alwaysUseCdmaLevel_configHasTrue() =
- testScope.runTest {
+ kosmos.runTest {
val latest by collectLastValue(underTest.alwaysUseCdmaLevel)
val config = MobileMappings.Config()
@@ -654,7 +640,7 @@
@Test
fun alwaysUseCdmaLevel_configHasFalse() =
- testScope.runTest {
+ kosmos.runTest {
val latest by collectLastValue(underTest.alwaysUseCdmaLevel)
val config = MobileMappings.Config()
@@ -666,7 +652,7 @@
@Test
fun isSingleCarrier_zeroSubscriptions_false() =
- testScope.runTest {
+ kosmos.runTest {
val latest by collectLastValue(underTest.isSingleCarrier)
connectionsRepository.setSubscriptions(emptyList())
@@ -676,7 +662,7 @@
@Test
fun isSingleCarrier_oneSubscription_true() =
- testScope.runTest {
+ kosmos.runTest {
val latest by collectLastValue(underTest.isSingleCarrier)
connectionsRepository.setSubscriptions(listOf(SUB_1))
@@ -686,7 +672,7 @@
@Test
fun isSingleCarrier_twoSubscriptions_false() =
- testScope.runTest {
+ kosmos.runTest {
val latest by collectLastValue(underTest.isSingleCarrier)
connectionsRepository.setSubscriptions(listOf(SUB_1, SUB_2))
@@ -696,7 +682,7 @@
@Test
fun isSingleCarrier_updates() =
- testScope.runTest {
+ kosmos.runTest {
val latest by collectLastValue(underTest.isSingleCarrier)
connectionsRepository.setSubscriptions(listOf(SUB_1))
@@ -708,7 +694,7 @@
@Test
fun mobileIsDefault_mobileFalseAndCarrierMergedFalse_false() =
- testScope.runTest {
+ kosmos.runTest {
val latest by collectLastValue(underTest.mobileIsDefault)
connectionsRepository.mobileIsDefault.value = false
@@ -719,7 +705,7 @@
@Test
fun mobileIsDefault_mobileTrueAndCarrierMergedFalse_true() =
- testScope.runTest {
+ kosmos.runTest {
val latest by collectLastValue(underTest.mobileIsDefault)
connectionsRepository.mobileIsDefault.value = true
@@ -731,7 +717,7 @@
/** Regression test for b/272586234. */
@Test
fun mobileIsDefault_mobileFalseAndCarrierMergedTrue_true() =
- testScope.runTest {
+ kosmos.runTest {
val latest by collectLastValue(underTest.mobileIsDefault)
connectionsRepository.mobileIsDefault.value = false
@@ -742,7 +728,7 @@
@Test
fun mobileIsDefault_updatesWhenRepoUpdates() =
- testScope.runTest {
+ kosmos.runTest {
val latest by collectLastValue(underTest.mobileIsDefault)
connectionsRepository.mobileIsDefault.value = true
@@ -760,7 +746,7 @@
@Test
fun dataSwitch_inSameGroup_validatedMatchesPreviousValue_expiresAfter2s() =
- testScope.runTest {
+ kosmos.runTest {
val latest by collectLastValue(underTest.isDefaultConnectionFailed)
connectionsRepository.mobileIsDefault.value = true
@@ -774,17 +760,17 @@
// After 1s, the force validation bit is still present, so the connection is not marked
// as failed
- advanceTimeBy(1000)
+ testScope.advanceTimeBy(1000)
assertThat(latest).isFalse()
// After 2s, the force validation expires so the connection updates to failed
- advanceTimeBy(1001)
+ testScope.advanceTimeBy(1001)
assertThat(latest).isTrue()
}
@Test
fun dataSwitch_inSameGroup_notValidated_immediatelyMarkedAsFailed() =
- testScope.runTest {
+ kosmos.runTest {
val latest by collectLastValue(underTest.isDefaultConnectionFailed)
connectionsRepository.mobileIsDefault.value = true
@@ -798,7 +784,7 @@
@Test
fun dataSwitch_loseValidation_thenSwitchHappens_clearsForcedBit() =
- testScope.runTest {
+ kosmos.runTest {
val latest by collectLastValue(underTest.isDefaultConnectionFailed)
// GIVEN the network starts validated
@@ -819,17 +805,17 @@
// THEN the forced validation bit is still used...
assertThat(latest).isFalse()
- advanceTimeBy(1000)
+ testScope.advanceTimeBy(1000)
assertThat(latest).isFalse()
// ... but expires after 2s
- advanceTimeBy(1001)
+ testScope.advanceTimeBy(1001)
assertThat(latest).isTrue()
}
@Test
fun dataSwitch_whileAlreadyForcingValidation_resetsClock() =
- testScope.runTest {
+ kosmos.runTest {
val latest by collectLastValue(underTest.isDefaultConnectionFailed)
connectionsRepository.mobileIsDefault.value = true
connectionsRepository.defaultConnectionIsValidated.value = true
@@ -837,7 +823,7 @@
connectionsRepository.activeSubChangedInGroupEvent.emit(Unit)
- advanceTimeBy(1000)
+ testScope.advanceTimeBy(1000)
// WHEN another change in same group event happens
connectionsRepository.activeSubChangedInGroupEvent.emit(Unit)
@@ -847,37 +833,37 @@
// THEN the forced validation remains for exactly 2 more seconds from now
// 1.500s from second event
- advanceTimeBy(1500)
+ testScope.advanceTimeBy(1500)
assertThat(latest).isFalse()
// 2.001s from the second event
- advanceTimeBy(501)
+ testScope.advanceTimeBy(501)
assertThat(latest).isTrue()
}
@Test
fun isForceHidden_repoHasMobileHidden_true() =
- testScope.runTest {
+ kosmos.runTest {
val latest by collectLastValue(underTest.isForceHidden)
- connectivityRepository.setForceHiddenIcons(setOf(ConnectivitySlot.MOBILE))
+ kosmos.connectivityRepository.fake.setForceHiddenIcons(setOf(ConnectivitySlot.MOBILE))
assertThat(latest).isTrue()
}
@Test
fun isForceHidden_repoDoesNotHaveMobileHidden_false() =
- testScope.runTest {
+ kosmos.runTest {
val latest by collectLastValue(underTest.isForceHidden)
- connectivityRepository.setForceHiddenIcons(setOf(ConnectivitySlot.WIFI))
+ kosmos.connectivityRepository.fake.setForceHiddenIcons(setOf(ConnectivitySlot.WIFI))
assertThat(latest).isFalse()
}
@Test
fun iconInteractor_cachedPerSubId() =
- testScope.runTest {
+ kosmos.runTest {
val interactor1 = underTest.getMobileConnectionInteractorForSubId(SUB_1_ID)
val interactor2 = underTest.getMobileConnectionInteractorForSubId(SUB_1_ID)
@@ -887,7 +873,7 @@
@Test
fun deviceBasedEmergencyMode_emergencyCallsOnly_followsDeviceServiceStateFromRepo() =
- testScope.runTest {
+ kosmos.runTest {
val latest by collectLastValue(underTest.isDeviceInEmergencyCallsOnlyMode)
connectionsRepository.isDeviceEmergencyCallCapable.value = true
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/domain/interactor/CollapsedStatusBarInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/domain/interactor/CollapsedStatusBarInteractorTest.kt
index 46f822a..db24d4b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/domain/interactor/CollapsedStatusBarInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/domain/interactor/CollapsedStatusBarInteractorTest.kt
@@ -21,18 +21,18 @@
import android.app.StatusBarManager.DISABLE_NONE
import android.app.StatusBarManager.DISABLE_NOTIFICATION_ICONS
import android.app.StatusBarManager.DISABLE_SYSTEM_INFO
-import androidx.test.ext.junit.runners.AndroidJUnit4;
+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.kosmos.testScope
-import com.android.systemui.statusbar.disableflags.data.model.DisableFlagsModel
import com.android.systemui.statusbar.disableflags.data.repository.fakeDisableFlagsRepository
+import com.android.systemui.statusbar.disableflags.shared.model.DisableFlagsModel
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlin.test.Test
import kotlinx.coroutines.test.runTest
-import org.junit.runner.RunWith;
+import org.junit.runner.RunWith
@SmallTest
@RunWith(AndroidJUnit4::class)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelImplTest.kt
index c4d2569..b9cdd06 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelImplTest.kt
@@ -58,8 +58,8 @@
import com.android.systemui.statusbar.data.model.StatusBarMode
import com.android.systemui.statusbar.data.repository.FakeStatusBarModeRepository.Companion.DISPLAY_ID
import com.android.systemui.statusbar.data.repository.fakeStatusBarModeRepository
-import com.android.systemui.statusbar.disableflags.data.model.DisableFlagsModel
import com.android.systemui.statusbar.disableflags.data.repository.fakeDisableFlagsRepository
+import com.android.systemui.statusbar.disableflags.shared.model.DisableFlagsModel
import com.android.systemui.statusbar.events.data.repository.systemStatusEventAnimationRepository
import com.android.systemui.statusbar.events.shared.model.SystemEventAnimationState.AnimatingIn
import com.android.systemui.statusbar.events.shared.model.SystemEventAnimationState.AnimatingOut
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt
similarity index 94%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt
index d823bf5..4eef308 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt
@@ -27,10 +27,14 @@
import android.platform.test.annotations.EnableFlags
import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID
import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.Flags.FLAG_MULTIUSER_WIFI_PICKER_TRACKER_SUPPORT
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.table.logcatTableLogBuffer
import com.android.systemui.statusbar.connectivity.WifiPickerTrackerFactory
@@ -40,9 +44,7 @@
import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiScanEntry
import com.android.systemui.testKosmos
import com.android.systemui.user.data.repository.fakeUserRepository
-import com.android.systemui.user.data.repository.userRepository
import com.android.systemui.util.concurrency.FakeExecutor
-import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.time.fakeSystemClock
import com.android.wifitrackerlib.HotspotNetworkEntry
import com.android.wifitrackerlib.HotspotNetworkEntry.DeviceType
@@ -53,31 +55,23 @@
import com.android.wifitrackerlib.WifiEntry.WIFI_LEVEL_UNREACHABLE
import com.android.wifitrackerlib.WifiPickerTracker
import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-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.Test
+import org.junit.runner.RunWith
import org.mockito.kotlin.any
-import org.mockito.kotlin.capture
+import org.mockito.kotlin.argumentCaptor
import org.mockito.kotlin.mock
-import org.mockito.kotlin.times
+import org.mockito.kotlin.never
+import org.mockito.kotlin.reset
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
-/**
- * Note: Most of these tests are duplicates of [WifiRepositoryImplTest] tests.
- *
- * Any new tests added here may also need to be added to [WifiRepositoryImplTest].
- */
-@Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
-@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
+@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
-class WifiRepositoryImplTest() : SysuiTestCase() {
- private val kosmos = testKosmos()
+class WifiRepositoryImplTest : SysuiTestCase() {
+ private val kosmos = testKosmos().useUnconfinedTestDispatcher()
private val userRepository = kosmos.fakeUserRepository
// Using lazy means that the class will only be constructed once it's fetched. Because the
@@ -108,13 +102,13 @@
private val callbackCaptor = argumentCaptor<WifiPickerTracker.WifiPickerTrackerCallback>()
- private val dispatcher = StandardTestDispatcher()
- private val testScope = TestScope(dispatcher)
+ private val dispatcher = kosmos.testDispatcher
+ private val testScope = kosmos.testScope
@Before
fun setUp() {
userRepository.setUserInfos(listOf(PRIMARY_USER, ANOTHER_USER))
- whenever(wifiPickerTrackerFactory.create(any(), any(), capture(callbackCaptor), any()))
+ whenever(wifiPickerTrackerFactory.create(any(), any(), callbackCaptor.capture(), any()))
.thenReturn(wifiPickerTracker)
}
@@ -122,7 +116,6 @@
fun wifiPickerTrackerCreation_scansDisabled() =
testScope.runTest {
collectLastValue(underTest.wifiNetwork)
- testScope.runCurrent()
verify(wifiPickerTracker).disableScanning()
}
@@ -1001,7 +994,6 @@
mock<WifiEntry>().apply { whenever(this.isPrimaryNetwork).thenReturn(false) }
whenever(wifiPickerTracker.connectedWifiEntry).thenReturn(wifiEntry)
getCallback().onWifiEntriesChanged()
- testScope.runCurrent()
assertThat(underTest.isWifiConnectedWithValidSsid()).isFalse()
}
@@ -1017,7 +1009,6 @@
}
whenever(wifiPickerTracker.connectedWifiEntry).thenReturn(wifiEntry)
getCallback().onWifiEntriesChanged()
- testScope.runCurrent()
assertThat(underTest.isWifiConnectedWithValidSsid()).isFalse()
}
@@ -1034,7 +1025,6 @@
}
whenever(wifiPickerTracker.connectedWifiEntry).thenReturn(wifiEntry)
getCallback().onWifiEntriesChanged()
- testScope.runCurrent()
assertThat(underTest.isWifiConnectedWithValidSsid()).isFalse()
}
@@ -1051,7 +1041,6 @@
}
whenever(wifiPickerTracker.connectedWifiEntry).thenReturn(wifiEntry)
getCallback().onWifiEntriesChanged()
- testScope.runCurrent()
assertThat(underTest.isWifiConnectedWithValidSsid()).isFalse()
}
@@ -1068,7 +1057,6 @@
}
whenever(wifiPickerTracker.connectedWifiEntry).thenReturn(wifiEntry)
getCallback().onWifiEntriesChanged()
- testScope.runCurrent()
assertThat(underTest.isWifiConnectedWithValidSsid()).isFalse()
}
@@ -1085,7 +1073,6 @@
}
whenever(wifiPickerTracker.connectedWifiEntry).thenReturn(wifiEntry)
getCallback().onWifiEntriesChanged()
- testScope.runCurrent()
assertThat(underTest.isWifiConnectedWithValidSsid()).isTrue()
}
@@ -1103,14 +1090,12 @@
}
whenever(wifiPickerTracker.connectedWifiEntry).thenReturn(wifiEntry)
getCallback().onWifiEntriesChanged()
- testScope.runCurrent()
assertThat(underTest.isWifiConnectedWithValidSsid()).isTrue()
// WHEN the network is lost
whenever(wifiPickerTracker.connectedWifiEntry).thenReturn(null)
getCallback().onWifiEntriesChanged()
- testScope.runCurrent()
// THEN the isWifiConnected updates
assertThat(underTest.isWifiConnectedWithValidSsid()).isFalse()
@@ -1216,9 +1201,6 @@
assertThat(latest).isEmpty()
}
- // TODO(b/371586248): This test currently require currentUserContext to be public for testing,
- // this needs to
- // be updated to capture the argument instead so currentUserContext can be private.
@Test
@EnableFlags(FLAG_MULTIUSER_WIFI_PICKER_TRACKER_SUPPORT)
fun oneUserVerifyCreatingWifiPickerTracker_multiuserFlagEnabled() =
@@ -1230,16 +1212,16 @@
)
userRepository.setSelectedUserInfo(PRIMARY_USER)
- runCurrent()
- val currentUserContext by collectLastValue(underTest.selectedUserContext)
- assertThat(currentUserContext).isEqualTo(primaryUserMockContext)
- verify(wifiPickerTrackerFactory).create(any(), any(), any(), any())
+ collectLastValue(underTest.wifiNetwork)
+
+ val contextCaptor = argumentCaptor<Context>()
+ verify(wifiPickerTrackerFactory).create(contextCaptor.capture(), any(), any(), any())
+ // If the flag is on, verify that we use the context from #createContextAsUser and we
+ // do NOT use the top-level context
+ assertThat(contextCaptor.firstValue).isEqualTo(primaryUserMockContext)
}
- // TODO(b/371586248): This test currently require currentUserContext to be public for testing,
- // this needs to
- // be updated to capture the argument instead so currentUserContext can be private.
@Test
@EnableFlags(FLAG_MULTIUSER_WIFI_PICKER_TRACKER_SUPPORT)
fun changeUserVerifyCreatingWifiPickerTracker_multiuserEnabled() =
@@ -1249,32 +1231,30 @@
UserHandle.of(PRIMARY_USER_ID),
primaryUserMockContext,
)
-
- runCurrent()
userRepository.setSelectedUserInfo(PRIMARY_USER)
- runCurrent()
- val currentUserContext by collectLastValue(underTest.selectedUserContext)
- assertThat(currentUserContext).isEqualTo(primaryUserMockContext)
+ collectLastValue(underTest.wifiNetwork)
+ val contextCaptor = argumentCaptor<Context>()
+ verify(wifiPickerTrackerFactory).create(contextCaptor.capture(), any(), any(), any())
+ assertThat(contextCaptor.firstValue).isEqualTo(primaryUserMockContext)
+
+ reset(wifiPickerTrackerFactory)
+
+ // WHEN we switch to a different user
val otherUserMockContext = mock<Context>()
mContext.prepareCreateContextAsUser(
UserHandle.of(ANOTHER_USER_ID),
otherUserMockContext,
)
-
- runCurrent()
userRepository.setSelectedUserInfo(ANOTHER_USER)
- runCurrent()
- val otherUserContext by collectLastValue(underTest.selectedUserContext)
- assertThat(otherUserContext).isEqualTo(otherUserMockContext)
- verify(wifiPickerTrackerFactory, times(2)).create(any(), any(), any(), any())
+ // THEN we use the different user's context to create WifiPickerTracker
+ val newCaptor = argumentCaptor<Context>()
+ verify(wifiPickerTrackerFactory).create(newCaptor.capture(), any(), any(), any())
+ assertThat(newCaptor.firstValue).isEqualTo(otherUserMockContext)
}
- // TODO(b/371586248): This test currently require currentUserContext to be public for testing,
- // this needs to
- // be updated to capture the argument instead so currentUserContext can be private.
@Test
@DisableFlags(FLAG_MULTIUSER_WIFI_PICKER_TRACKER_SUPPORT)
fun changeUserVerifyCreatingWifiPickerTracker_multiuserDisabled() =
@@ -1285,36 +1265,39 @@
primaryUserMockContext,
)
- runCurrent()
userRepository.setSelectedUserInfo(PRIMARY_USER)
- runCurrent()
- val currentUserContext by collectLastValue(underTest.selectedUserContext)
- assertThat(currentUserContext).isEqualTo(primaryUserMockContext)
+ collectLastValue(underTest.wifiNetwork)
+ val contextCaptor = argumentCaptor<Context>()
+ verify(wifiPickerTrackerFactory).create(contextCaptor.capture(), any(), any(), any())
+ // If the flag is off, verify that we do NOT use the context from #createContextAsUser
+ // and we instead use the top-level context
+ assertThat(contextCaptor.firstValue).isNotEqualTo(primaryUserMockContext)
+ assertThat(contextCaptor.firstValue).isEqualTo(mContext)
+
+ reset(wifiPickerTrackerFactory)
+
+ // WHEN we switch to a different user
val otherUserMockContext = mock<Context>()
mContext.prepareCreateContextAsUser(
UserHandle.of(ANOTHER_USER_ID),
otherUserMockContext,
)
-
- runCurrent()
userRepository.setSelectedUserInfo(ANOTHER_USER)
- runCurrent()
- verify(wifiPickerTrackerFactory, times(1)).create(any(), any(), any(), any())
+ // THEN we do NOT re-create WifiPickerTracker because the multiuser flag is off
+ verify(wifiPickerTrackerFactory, never()).create(any(), any(), any(), any())
}
private fun getCallback(): WifiPickerTracker.WifiPickerTrackerCallback {
- testScope.runCurrent()
- return callbackCaptor.value
+ return callbackCaptor.firstValue
}
private fun getTrafficStateCallback(): WifiManager.TrafficStateCallback {
- testScope.runCurrent()
val callbackCaptor = argumentCaptor<WifiManager.TrafficStateCallback>()
verify(wifiManager).registerTrafficStateCallback(any(), callbackCaptor.capture())
- return callbackCaptor.value!!
+ return callbackCaptor.firstValue
}
private fun createHotspotWithType(@DeviceType type: Int): HotspotNetworkEntry {
@@ -1325,10 +1308,9 @@
}
private fun getScanResultsCallback(): WifiManager.ScanResultsCallback {
- testScope.runCurrent()
val callbackCaptor = argumentCaptor<WifiManager.ScanResultsCallback>()
verify(wifiManager).registerScanResultsCallback(any(), callbackCaptor.capture())
- return callbackCaptor.value!!
+ return callbackCaptor.firstValue
}
private companion object {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/AvalancheControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/AvalancheControllerTest.kt
index 9a862fc..c5eed73 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/AvalancheControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/AvalancheControllerTest.kt
@@ -24,11 +24,19 @@
import com.android.internal.logging.testing.UiEventLoggerFake
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
+import com.android.systemui.kosmos.testScope
import com.android.systemui.log.logcatLogBuffer
+import com.android.systemui.plugins.statusbar.statusBarStateController
+import com.android.systemui.shade.domain.interactor.shadeInteractor
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
+import com.android.systemui.statusbar.notification.collection.provider.visualStabilityProvider
+import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManagerImpl
import com.android.systemui.statusbar.notification.shared.NotificationThrottleHun
+import com.android.systemui.statusbar.phone.keyguardBypassController
import com.android.systemui.statusbar.policy.HeadsUpManagerTestUtil.createFullScreenIntentEntry
+import com.android.systemui.testKosmos
import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.kotlin.JavaAdapter
import com.android.systemui.util.settings.FakeGlobalSettings
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
@@ -48,6 +56,7 @@
@RunWith(AndroidJUnit4::class)
@EnableFlags(NotificationThrottleHun.FLAG_NAME)
class AvalancheControllerTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
// For creating mocks
@get:Rule var rule: MockitoRule = MockitoJUnit.rule()
@@ -61,7 +70,6 @@
@Mock private val mAccessibilityMgr: AccessibilityManagerWrapper? = null
private val mUiEventLoggerFake = UiEventLoggerFake()
@Mock private lateinit var mHeadsUpManagerLogger: HeadsUpManagerLogger
-
@Mock private lateinit var mBgHandler: Handler
private val mLogger = Mockito.spy(HeadsUpManagerLogger(logcatLogBuffer()))
@@ -76,26 +84,33 @@
Mockito.`when`(
mAccessibilityMgr!!.getRecommendedTimeoutMillis(
ArgumentMatchers.anyInt(),
- ArgumentMatchers.anyInt()
+ ArgumentMatchers.anyInt(),
)
)
.then { i: InvocationOnMock -> i.getArgument(0) }
// Initialize AvalancheController and TestableHeadsUpManager during setUp instead of
// declaration, where mocks are null
- mAvalancheController = AvalancheController(dumpManager, mUiEventLoggerFake,
- mHeadsUpManagerLogger, mBgHandler)
+ mAvalancheController =
+ AvalancheController(dumpManager, mUiEventLoggerFake, mHeadsUpManagerLogger, mBgHandler)
testableHeadsUpManager =
TestableHeadsUpManager(
mContext,
mLogger,
+ kosmos.statusBarStateController,
+ kosmos.keyguardBypassController,
+ GroupMembershipManagerImpl(),
+ kosmos.visualStabilityProvider,
+ kosmos.configurationController,
mExecutor,
mGlobalSettings,
mSystemClock,
mAccessibilityMgr,
mUiEventLoggerFake,
- mAvalancheController
+ JavaAdapter(kosmos.testScope),
+ kosmos.shadeInteractor,
+ mAvalancheController,
)
}
@@ -270,7 +285,6 @@
assertThat(mAvalancheController.headsUpEntryShowing).isEqualTo(nextEntry)
}
-
@Test
fun testDelete_deleteSecondToLastEntry_showingEntryKeyBecomesPreviousHunKey() {
mAvalancheController.previousHunKey = ""
@@ -305,7 +319,7 @@
mAvalancheController.delete(showingEntry, runnableMock!!, "testLabel")
// Next entry is shown
- assertThat(mAvalancheController.previousHunKey).isEqualTo("");
+ assertThat(mAvalancheController.previousHunKey).isEqualTo("")
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java
index 89aa670..abb3e6e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java
@@ -35,6 +35,8 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import static kotlinx.coroutines.flow.StateFlowKt.MutableStateFlow;
+
import android.app.Notification;
import android.app.PendingIntent;
import android.app.Person;
@@ -49,15 +51,21 @@
import com.android.internal.logging.testing.UiEventLoggerFake;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.kosmos.KosmosJavaAdapter;
import com.android.systemui.res.R;
+import com.android.systemui.shade.domain.interactor.ShadeInteractor;
+import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
+import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManagerImpl;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.shared.NotificationThrottleHun;
import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.kotlin.JavaAdapter;
import com.android.systemui.util.settings.FakeGlobalSettings;
import com.android.systemui.util.time.FakeSystemClock;
+import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -73,7 +81,10 @@
@SmallTest
@TestableLooper.RunWithLooper
@RunWith(ParameterizedAndroidJunit4.class)
+// TODO(b/378142453): Merge this with BaseHeadsUpManagerTest.
public class BaseHeadsUpManagerTest extends SysuiTestCase {
+ protected KosmosJavaAdapter mKosmos = new KosmosJavaAdapter(this);
+
@Rule
public MockitoRule rule = MockitoJUnit.rule();
@@ -85,6 +96,7 @@
private final HeadsUpManagerLogger mLogger = spy(new HeadsUpManagerLogger(logcatLogBuffer()));
@Mock private Handler mBgHandler;
@Mock private DumpManager dumpManager;
+ @Mock private ShadeInteractor mShadeInteractor;
private AvalancheController mAvalancheController;
@Mock private AccessibilityManagerWrapper mAccessibilityMgr;
@@ -108,8 +120,22 @@
}
private BaseHeadsUpManager createHeadsUpManager() {
- return new TestableHeadsUpManager(mContext, mLogger, mExecutor, mGlobalSettings,
- mSystemClock, mAccessibilityMgr, mUiEventLoggerFake, mAvalancheController);
+ return new TestableHeadsUpManager(
+ mContext,
+ mLogger,
+ mKosmos.getStatusBarStateController(),
+ mKosmos.getKeyguardBypassController(),
+ new GroupMembershipManagerImpl(),
+ mKosmos.getVisualStabilityProvider(),
+ mKosmos.getConfigurationController(),
+ mExecutor,
+ mGlobalSettings,
+ mSystemClock,
+ mAccessibilityMgr,
+ mUiEventLoggerFake,
+ new JavaAdapter(mKosmos.getTestScope()),
+ mShadeInteractor,
+ mAvalancheController);
}
private NotificationEntry createStickyEntry(int id) {
@@ -152,6 +178,8 @@
super.SysuiSetup();
mAvalancheController = new AvalancheController(dumpManager, mUiEventLoggerFake, mLogger,
mBgHandler);
+ when(mShadeInteractor.isAnyExpanded()).thenReturn(MutableStateFlow(true));
+ when(mKosmos.getKeyguardBypassController().getBypassEnabled()).thenReturn(false);
}
@Test
@@ -298,46 +326,6 @@
verify(mLogger, times(1)).logNotificationActuallyRemoved(eq(notifEntry));
}
- @Test
- public void testShouldHeadsUpBecomePinned_hasFSI_notUnpinned_true() {
- final BaseHeadsUpManager hum = createHeadsUpManager();
- final NotificationEntry notifEntry =
- HeadsUpManagerTestUtil.createFullScreenIntentEntry(/* id = */ 0, mContext);
-
- // Add notifEntry to ANM mAlertEntries map and make it NOT unpinned
- hum.showNotification(notifEntry);
-
- final BaseHeadsUpManager.HeadsUpEntry headsUpEntry = hum.getHeadsUpEntry(
- notifEntry.getKey());
- headsUpEntry.mWasUnpinned = false;
-
- assertTrue(hum.shouldHeadsUpBecomePinned(notifEntry));
- }
-
- @Test
- public void testShouldHeadsUpBecomePinned_wasUnpinned_false() {
- final BaseHeadsUpManager hum = createHeadsUpManager();
- final NotificationEntry notifEntry =
- HeadsUpManagerTestUtil.createFullScreenIntentEntry(/* id = */ 0, mContext);
-
- // Add notifEntry to ANM mAlertEntries map and make it unpinned
- hum.showNotification(notifEntry);
-
- final BaseHeadsUpManager.HeadsUpEntry headsUpEntry = hum.getHeadsUpEntry(
- notifEntry.getKey());
- headsUpEntry.mWasUnpinned = true;
-
- assertFalse(hum.shouldHeadsUpBecomePinned(notifEntry));
- }
-
- @Test
- public void testShouldHeadsUpBecomePinned_noFSI_false() {
- final BaseHeadsUpManager hum = createHeadsUpManager();
- final NotificationEntry entry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0, mContext);
-
- assertFalse(hum.shouldHeadsUpBecomePinned(entry));
- }
-
@Test
public void testShowNotification_autoDismissesIncludingTouchAcceptanceDelay() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryStateNotifierTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BatteryStateNotifierTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryStateNotifierTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BatteryStateNotifierTest.kt
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/HeadsUpManagerPhoneTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/HeadsUpManagerPhoneTest.kt
index 1915e8e..8ebdbaa 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/HeadsUpManagerPhoneTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/HeadsUpManagerPhoneTest.kt
@@ -15,7 +15,6 @@
*/
package com.android.systemui.statusbar.policy
-import android.content.Context
import android.os.Handler
import android.platform.test.annotations.EnableFlags
import android.platform.test.flag.junit.FlagsParameterization
@@ -26,27 +25,19 @@
import com.android.systemui.flags.andSceneContainer
import com.android.systemui.kosmos.testScope
import com.android.systemui.log.logcatLogBuffer
-import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.res.R
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.FakeStatusBarStateController
import com.android.systemui.statusbar.NotificationShadeWindowController
import com.android.systemui.statusbar.StatusBarState
-import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.provider.VisualStabilityProvider
import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager
-import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.shared.NotificationThrottleHun
import com.android.systemui.statusbar.phone.ConfigurationControllerImpl
-import com.android.systemui.statusbar.notification.HeadsUpManagerPhone
import com.android.systemui.statusbar.phone.KeyguardBypassController
import com.android.systemui.testKosmos
-import com.android.systemui.util.concurrency.DelayableExecutor
import com.android.systemui.util.concurrency.mockExecutorHandler
import com.android.systemui.util.kotlin.JavaAdapter
-import com.android.systemui.util.mockito.mock
-import com.android.systemui.util.settings.GlobalSettings
-import com.android.systemui.util.time.SystemClock
import com.google.common.truth.Truth.assertThat
import junit.framework.Assert
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -78,7 +69,7 @@
@Mock private lateinit var mVSProvider: VisualStabilityProvider
- @Mock private lateinit var mStatusBarStateController: StatusBarStateController
+ val statusBarStateController = FakeStatusBarStateController()
@Mock private lateinit var mBypassController: KeyguardBypassController
@@ -97,61 +88,16 @@
@Mock private lateinit var mBgHandler: Handler
- private class TestableHeadsUpManagerPhone(
- context: Context,
- headsUpManagerLogger: HeadsUpManagerLogger,
- groupManager: GroupMembershipManager,
- visualStabilityProvider: VisualStabilityProvider,
- statusBarStateController: StatusBarStateController,
- keyguardBypassController: KeyguardBypassController,
- configurationController: ConfigurationController,
- globalSettings: GlobalSettings,
- systemClock: SystemClock,
- executor: DelayableExecutor,
- accessibilityManagerWrapper: AccessibilityManagerWrapper,
- uiEventLogger: UiEventLogger,
- javaAdapter: JavaAdapter,
- shadeInteractor: ShadeInteractor,
- avalancheController: AvalancheController
- ) :
- HeadsUpManagerPhone(
- context,
- headsUpManagerLogger,
- statusBarStateController,
- keyguardBypassController,
- groupManager,
- visualStabilityProvider,
- configurationController,
- mockExecutorHandler(executor),
- globalSettings,
- systemClock,
- executor,
- accessibilityManagerWrapper,
- uiEventLogger,
- javaAdapter,
- shadeInteractor,
- avalancheController
- ) {
- init {
- mMinimumDisplayTime = TEST_MINIMUM_DISPLAY_TIME
- mAutoDismissTime = TEST_AUTO_DISMISS_TIME
- }
-
- /** Wrapper for [BaseHeadsUpManager.shouldHeadsUpBecomePinned] for testing */
- fun shouldHeadsUpBecomePinnedWrapper(entry: NotificationEntry): Boolean {
- return shouldHeadsUpBecomePinned(entry)
- }
- }
-
- private fun createHeadsUpManagerPhone(): HeadsUpManagerPhone {
- return TestableHeadsUpManagerPhone(
+ private fun createHeadsUpManagerPhone(): BaseHeadsUpManager {
+ return BaseHeadsUpManager(
mContext,
mHeadsUpManagerLogger,
+ statusBarStateController,
+ mBypassController,
mGroupManager,
mVSProvider,
- mStatusBarStateController,
- mBypassController,
mConfigurationController,
+ mockExecutorHandler(mExecutor),
mGlobalSettings,
mSystemClock,
mExecutor,
@@ -159,20 +105,22 @@
mUiEventLogger,
mJavaAdapter,
mShadeInteractor,
- mAvalancheController
+ mAvalancheController,
)
}
@Before
fun setUp() {
whenever(mShadeInteractor.isAnyExpanded).thenReturn(MutableStateFlow(false))
+ whenever(mShadeInteractor.isQsExpanded).thenReturn(MutableStateFlow(false))
+ whenever(mBypassController.bypassEnabled).thenReturn(false)
whenever(mVSProvider.isReorderingAllowed).thenReturn(true)
val accessibilityMgr =
mDependency.injectMockDependency(AccessibilityManagerWrapper::class.java)
whenever(
accessibilityMgr.getRecommendedTimeoutMillis(
ArgumentMatchers.anyInt(),
- ArgumentMatchers.anyInt()
+ ArgumentMatchers.anyInt(),
)
)
.thenReturn(TEST_AUTO_DISMISS_TIME)
@@ -205,7 +153,7 @@
hmp.removeNotification(
entry.key,
/* releaseImmediately= */ false,
- /* reason= */ "swipe out"
+ /* reason= */ "swipe out",
)
Assert.assertTrue(removedImmediately)
Assert.assertFalse(hmp.isHeadsUpEntry(entry.key))
@@ -245,6 +193,7 @@
mSystemClock.advanceTime((TEST_AUTO_DISMISS_TIME + hmp.mExtensionTime / 2).toLong())
Assert.assertTrue(hmp.isHeadsUpEntry(entry.key))
}
+
@Test
@EnableFlags(NotificationThrottleHun.FLAG_NAME)
fun testShowNotification_removeWhenReorderingAllowedTrue() {
@@ -253,7 +202,7 @@
val notifEntry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
hmp.showNotification(notifEntry)
- assertThat(hmp.mEntriesToRemoveWhenReorderingAllowed.contains(notifEntry)).isTrue();
+ assertThat(hmp.mEntriesToRemoveWhenReorderingAllowed.contains(notifEntry)).isTrue()
}
@Test
@@ -264,7 +213,7 @@
val notifEntry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
hmp.showNotification(notifEntry)
- assertThat(notifEntry.isSeenInShade).isTrue();
+ assertThat(notifEntry.isSeenInShade).isTrue()
}
@Test
@@ -275,197 +224,136 @@
val notifEntry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
hmp.showNotification(notifEntry)
- assertThat(notifEntry.isSeenInShade).isFalse();
+ assertThat(notifEntry.isSeenInShade).isFalse()
}
@Test
+ fun testShouldHeadsUpBecomePinned_noFSI_false() =
+ testScope.runTest {
+ val hum = createHeadsUpManagerPhone()
+ statusBarStateController.setState(StatusBarState.KEYGUARD)
+
+ val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
+
+ Assert.assertFalse(hum.shouldHeadsUpBecomePinned(entry))
+ }
+
+ @Test
+ fun testShouldHeadsUpBecomePinned_hasFSI_notUnpinned_true() =
+ testScope.runTest {
+ val hum = createHeadsUpManagerPhone()
+ statusBarStateController.setState(StatusBarState.KEYGUARD)
+
+ val notifEntry =
+ HeadsUpManagerTestUtil.createFullScreenIntentEntry(/* id= */ 0, mContext)
+
+ // Add notifEntry to ANM mAlertEntries map and make it NOT unpinned
+ hum.showNotification(notifEntry)
+
+ val headsUpEntry = hum.getHeadsUpEntry(notifEntry.key)
+ headsUpEntry!!.mWasUnpinned = false
+
+ Assert.assertTrue(hum.shouldHeadsUpBecomePinned(notifEntry))
+ }
+
+ @Test
+ fun testShouldHeadsUpBecomePinned_wasUnpinned_false() =
+ testScope.runTest {
+ val hum = createHeadsUpManagerPhone()
+ statusBarStateController.setState(StatusBarState.KEYGUARD)
+
+ val notifEntry =
+ HeadsUpManagerTestUtil.createFullScreenIntentEntry(/* id= */ 0, mContext)
+
+ // Add notifEntry to ANM mAlertEntries map and make it unpinned
+ hum.showNotification(notifEntry)
+
+ val headsUpEntry = hum.getHeadsUpEntry(notifEntry.key)
+ headsUpEntry!!.mWasUnpinned = true
+
+ Assert.assertFalse(hum.shouldHeadsUpBecomePinned(notifEntry))
+ }
+
+ @Test
fun shouldHeadsUpBecomePinned_shadeNotExpanded_true() =
testScope.runTest {
// GIVEN
- val statusBarStateController = FakeStatusBarStateController()
whenever(mShadeInteractor.isAnyFullyExpanded).thenReturn(MutableStateFlow(false))
- val hmp =
- TestableHeadsUpManagerPhone(
- mContext,
- mHeadsUpManagerLogger,
- mGroupManager,
- mVSProvider,
- statusBarStateController,
- mBypassController,
- mConfigurationController,
- mGlobalSettings,
- mSystemClock,
- mExecutor,
- mAccessibilityManagerWrapper,
- mUiEventLogger,
- mJavaAdapter,
- mShadeInteractor,
- mAvalancheController
- )
+ val hmp = createHeadsUpManagerPhone()
val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
statusBarStateController.setState(StatusBarState.SHADE)
runCurrent()
// THEN
- Assert.assertTrue(hmp.shouldHeadsUpBecomePinnedWrapper(entry))
+ Assert.assertTrue(hmp.shouldHeadsUpBecomePinned(entry))
}
@Test
fun shouldHeadsUpBecomePinned_shadeLocked_false() =
testScope.runTest {
// GIVEN
- val statusBarStateController = FakeStatusBarStateController()
- val hmp =
- TestableHeadsUpManagerPhone(
- mContext,
- mHeadsUpManagerLogger,
- mGroupManager,
- mVSProvider,
- statusBarStateController,
- mBypassController,
- mConfigurationController,
- mGlobalSettings,
- mSystemClock,
- mExecutor,
- mAccessibilityManagerWrapper,
- mUiEventLogger,
- mJavaAdapter,
- mShadeInteractor,
- mAvalancheController
- )
+ val hmp = createHeadsUpManagerPhone()
val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
statusBarStateController.setState(StatusBarState.SHADE_LOCKED)
runCurrent()
// THEN
- Assert.assertFalse(hmp.shouldHeadsUpBecomePinnedWrapper(entry))
+ Assert.assertFalse(hmp.shouldHeadsUpBecomePinned(entry))
}
@Test
fun shouldHeadsUpBecomePinned_shadeUnknown_false() =
testScope.runTest {
// GIVEN
- val statusBarStateController = FakeStatusBarStateController()
- val hmp =
- TestableHeadsUpManagerPhone(
- mContext,
- mHeadsUpManagerLogger,
- mGroupManager,
- mVSProvider,
- statusBarStateController,
- mBypassController,
- mConfigurationController,
- mGlobalSettings,
- mSystemClock,
- mExecutor,
- mAccessibilityManagerWrapper,
- mUiEventLogger,
- mJavaAdapter,
- mShadeInteractor,
- mAvalancheController
- )
+ val hmp = createHeadsUpManagerPhone()
val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
statusBarStateController.setState(1207)
runCurrent()
// THEN
- Assert.assertFalse(hmp.shouldHeadsUpBecomePinnedWrapper(entry))
+ Assert.assertFalse(hmp.shouldHeadsUpBecomePinned(entry))
}
@Test
fun shouldHeadsUpBecomePinned_keyguardWithBypassOn_true() =
testScope.runTest {
// GIVEN
- val statusBarStateController = FakeStatusBarStateController()
whenever(mBypassController.bypassEnabled).thenReturn(true)
- val hmp =
- TestableHeadsUpManagerPhone(
- mContext,
- mHeadsUpManagerLogger,
- mGroupManager,
- mVSProvider,
- statusBarStateController,
- mBypassController,
- mConfigurationController,
- mGlobalSettings,
- mSystemClock,
- mExecutor,
- mAccessibilityManagerWrapper,
- mUiEventLogger,
- mJavaAdapter,
- mShadeInteractor,
- mAvalancheController
- )
+ val hmp = createHeadsUpManagerPhone()
val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
statusBarStateController.setState(StatusBarState.KEYGUARD)
runCurrent()
// THEN
- Assert.assertTrue(hmp.shouldHeadsUpBecomePinnedWrapper(entry))
+ Assert.assertTrue(hmp.shouldHeadsUpBecomePinned(entry))
}
@Test
fun shouldHeadsUpBecomePinned_keyguardWithBypassOff_false() =
testScope.runTest {
// GIVEN
- val statusBarStateController = FakeStatusBarStateController()
whenever(mBypassController.bypassEnabled).thenReturn(false)
- val hmp =
- TestableHeadsUpManagerPhone(
- mContext,
- mHeadsUpManagerLogger,
- mGroupManager,
- mVSProvider,
- statusBarStateController,
- mBypassController,
- mConfigurationController,
- mGlobalSettings,
- mSystemClock,
- mExecutor,
- mAccessibilityManagerWrapper,
- mUiEventLogger,
- mJavaAdapter,
- mShadeInteractor,
- mAvalancheController
- )
+ val hmp = createHeadsUpManagerPhone()
val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
statusBarStateController.setState(StatusBarState.KEYGUARD)
runCurrent()
// THEN
- Assert.assertFalse(hmp.shouldHeadsUpBecomePinnedWrapper(entry))
+ Assert.assertFalse(hmp.shouldHeadsUpBecomePinned(entry))
}
@Test
fun shouldHeadsUpBecomePinned_shadeExpanded_false() =
testScope.runTest {
// GIVEN
- val statusBarStateController = FakeStatusBarStateController()
whenever(mShadeInteractor.isAnyExpanded).thenReturn(MutableStateFlow(true))
- val hmp =
- TestableHeadsUpManagerPhone(
- mContext,
- mHeadsUpManagerLogger,
- mGroupManager,
- mVSProvider,
- statusBarStateController,
- mBypassController,
- mConfigurationController,
- mGlobalSettings,
- mSystemClock,
- mExecutor,
- mAccessibilityManagerWrapper,
- mUiEventLogger,
- mJavaAdapter,
- mShadeInteractor,
- mAvalancheController
- )
+ val hmp = createHeadsUpManagerPhone()
val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
statusBarStateController.setState(StatusBarState.SHADE)
runCurrent()
// THEN
- Assert.assertFalse(hmp.shouldHeadsUpBecomePinnedWrapper(entry))
+ Assert.assertFalse(hmp.shouldHeadsUpBecomePinned(entry))
}
companion object {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/RotationLockControllerImplTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/RotationLockControllerImplTest.java
index 3f33d2f..8593f6a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/RotationLockControllerImplTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/RotationLockControllerImplTest.java
@@ -21,9 +21,9 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.internal.view.RotationPolicy;
@@ -37,7 +37,7 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@TestableLooper.RunWithLooper
@SmallTest
public class RotationLockControllerImplTest extends SysuiTestCase {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/TestableHeadsUpManager.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/TestableHeadsUpManager.java
index 3efabd7..59987f4 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/TestableHeadsUpManager.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/TestableHeadsUpManager.java
@@ -16,7 +16,6 @@
package com.android.systemui.statusbar.policy;
-import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_CONTRACTED;
import static com.android.systemui.util.concurrency.MockExecutorHandlerKt.mockExecutorHandler;
import static org.mockito.Mockito.spy;
@@ -28,8 +27,14 @@
import androidx.annotation.Nullable;
import com.android.internal.logging.UiEventLogger;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.shade.domain.interactor.ShadeInteractor;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.provider.VisualStabilityProvider;
+import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
+import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.util.concurrency.DelayableExecutor;
+import com.android.systemui.util.kotlin.JavaAdapter;
import com.android.systemui.util.settings.GlobalSettings;
import com.android.systemui.util.time.SystemClock;
@@ -37,16 +42,39 @@
private HeadsUpEntry mLastCreatedEntry;
- TestableHeadsUpManager(Context context,
+ TestableHeadsUpManager(
+ Context context,
HeadsUpManagerLogger logger,
+ StatusBarStateController statusBarStateController,
+ KeyguardBypassController bypassController,
+ GroupMembershipManager groupMembershipManager,
+ VisualStabilityProvider visualStabilityProvider,
+ ConfigurationController configurationController,
DelayableExecutor executor,
GlobalSettings globalSettings,
SystemClock systemClock,
AccessibilityManagerWrapper accessibilityManagerWrapper,
UiEventLogger uiEventLogger,
+ JavaAdapter javaAdapter,
+ ShadeInteractor shadeInteractor,
AvalancheController avalancheController) {
- super(context, logger, mockExecutorHandler(executor), globalSettings, systemClock,
- executor, accessibilityManagerWrapper, uiEventLogger, avalancheController);
+ super(
+ context,
+ logger,
+ statusBarStateController,
+ bypassController,
+ groupMembershipManager,
+ visualStabilityProvider,
+ configurationController,
+ mockExecutorHandler(executor),
+ globalSettings,
+ systemClock,
+ executor,
+ accessibilityManagerWrapper,
+ uiEventLogger,
+ javaAdapter,
+ shadeInteractor,
+ avalancheController);
mTouchAcceptanceDelay = BaseHeadsUpManagerTest.TEST_TOUCH_ACCEPTANCE_TIME;
mMinimumDisplayTime = BaseHeadsUpManagerTest.TEST_MINIMUM_DISPLAY_TIME;
@@ -61,11 +89,6 @@
return mLastCreatedEntry;
}
- @Override
- public int getContentFlag() {
- return FLAG_CONTENT_VIEW_CONTRACTED;
- }
-
// The following are only implemented by HeadsUpManagerPhone. If you need them, use that.
@Override
public void addHeadsUpPhoneListener(@NonNull OnHeadsUpPhoneListenerChange listener) {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/ui/dialog/viewmodel/ModesDialogViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/ui/dialog/viewmodel/ModesDialogViewModelTest.kt
index 0e32c95..e686ede 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/ui/dialog/viewmodel/ModesDialogViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/ui/dialog/viewmodel/ModesDialogViewModelTest.kt
@@ -24,6 +24,7 @@
import android.provider.Settings
import android.service.notification.SystemZenRules
import android.service.notification.ZenModeConfig
+import android.service.notification.ZenModeConfig.ScheduleInfo
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.settingslib.notification.modes.TestModeBuilder
@@ -65,6 +66,7 @@
private lateinit var underTest: ModesDialogViewModel
private lateinit var timeScheduleMode: ZenMode
+ private lateinit var timeScheduleInfo: ScheduleInfo
@Before
fun setUp() {
@@ -78,18 +80,18 @@
kosmos.mockModesDialogEventLogger,
)
- val scheduleInfo = ZenModeConfig.ScheduleInfo()
- scheduleInfo.days = intArrayOf(Calendar.MONDAY, Calendar.TUESDAY, Calendar.WEDNESDAY)
- scheduleInfo.startHour = 11
- scheduleInfo.endHour = 15
+ timeScheduleInfo = ZenModeConfig.ScheduleInfo()
+ timeScheduleInfo.days = intArrayOf(Calendar.MONDAY, Calendar.TUESDAY, Calendar.WEDNESDAY)
+ timeScheduleInfo.startHour = 11
+ timeScheduleInfo.endHour = 15
timeScheduleMode =
TestModeBuilder()
.setPackage(SystemZenRules.PACKAGE_ANDROID)
.setType(AutomaticZenRule.TYPE_SCHEDULE_TIME)
.setManualInvocationAllowed(true)
- .setConditionId(ZenModeConfig.toScheduleConditionId(scheduleInfo))
+ .setConditionId(ZenModeConfig.toScheduleConditionId(timeScheduleInfo))
.setTriggerDescription(
- SystemZenRules.getTriggerDescriptionForScheduleTime(mContext, scheduleInfo)
+ SystemZenRules.getTriggerDescriptionForScheduleTime(mContext, timeScheduleInfo)
)
.build()
}
@@ -358,7 +360,7 @@
assertThat(tiles!![3].subtext).isEqualTo("Off")
assertThat(tiles!![4].subtext).isEqualTo("On")
assertThat(tiles!![5].subtext).isEqualTo("Not set")
- assertThat(tiles!![6].subtext).isEqualTo("Mon - Wed, 11:00 AM - 3:00 PM")
+ assertThat(tiles!![6].subtext).isEqualTo(timeScheduleMode.triggerDescription)
}
@Test
@@ -437,7 +439,8 @@
with(tiles?.elementAt(6)!!) {
assertThat(this.stateDescription).isEqualTo("Off")
assertThat(this.subtextDescription)
- .isEqualTo("Monday to Wednesday, 11:00 AM - 3:00 PM")
+ .isEqualTo(SystemZenRules.getDaysOfWeekFull(context, timeScheduleInfo)
+ + ", " + SystemZenRules.getTimeSummary(context, timeScheduleInfo))
}
// All tiles have the same long click info
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/window/MultiDisplayStatusBarWindowControllerStoreTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/window/MultiDisplayStatusBarWindowControllerStoreTest.kt
new file mode 100644
index 0000000..7a9d017
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/window/MultiDisplayStatusBarWindowControllerStoreTest.kt
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.window
+
+import android.platform.test.annotations.EnableFlags
+import android.view.Display.DEFAULT_DISPLAY
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.display.data.repository.displayRepository
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
+import com.android.systemui.testKosmos
+import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.never
+import org.mockito.kotlin.verify
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@EnableFlags(StatusBarConnectedDisplays.FLAG_NAME)
+class MultiDisplayStatusBarWindowControllerStoreTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
+ private val fakeDisplayRepository = kosmos.displayRepository
+ private val testScope = kosmos.testScope
+
+ private val underTest by lazy { kosmos.multiDisplayStatusBarWindowControllerStore }
+
+ @Before
+ fun start() {
+ underTest.start()
+ }
+
+ @Before fun addDisplays() = runBlocking { fakeDisplayRepository.addDisplay(DEFAULT_DISPLAY) }
+
+ @Test
+ fun beforeDisplayRemoved_doesNotStopInstances() =
+ testScope.runTest {
+ val instance = underTest.forDisplay(DEFAULT_DISPLAY)
+
+ verify(instance, never()).stop()
+ }
+
+ @Test
+ fun displayRemoved_stopsInstance() =
+ testScope.runTest {
+ val instance = underTest.forDisplay(DEFAULT_DISPLAY)
+
+ fakeDisplayRepository.removeDisplay(DEFAULT_DISPLAY)
+
+ verify(instance).stop()
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/window/StatusBarWindowControllerImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/window/StatusBarWindowControllerImplTest.kt
index 6e66287..61c7193 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/window/StatusBarWindowControllerImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/window/StatusBarWindowControllerImplTest.kt
@@ -17,12 +17,17 @@
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
+import android.view.fakeWindowManager
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.concurrency.fakeExecutor
+import com.android.systemui.fragments.fragmentService
import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
+import com.android.systemui.statusbar.core.StatusBarRootModernization
import com.android.systemui.statusbar.policy.statusBarConfigurationController
import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.any
@@ -37,6 +42,9 @@
testKosmos().also { it.statusBarWindowViewInflater = it.fakeStatusBarWindowViewInflater }
private val underTest = kosmos.statusBarWindowControllerImpl
+ private val fakeExecutor = kosmos.fakeExecutor
+ private val fakeWindowManager = kosmos.fakeWindowManager
+ private val mockFragmentService = kosmos.fragmentService
private val fakeStatusBarWindowViewInflater = kosmos.fakeStatusBarWindowViewInflater
private val statusBarConfigurationController = kosmos.statusBarConfigurationController
@@ -59,4 +67,64 @@
verify(mockWindowView, never()).setStatusBarConfigurationController(any())
}
+
+ @Test
+ @EnableFlags(StatusBarRootModernization.FLAG_NAME, StatusBarConnectedDisplays.FLAG_NAME)
+ fun stop_statusBarModernizationFlagEnabled_doesNotRemoveFragment() {
+ val windowView = fakeStatusBarWindowViewInflater.inflatedMockViews.first()
+
+ underTest.stop()
+ fakeExecutor.runAllReady()
+
+ verify(mockFragmentService, never()).removeAndDestroy(windowView)
+ }
+
+ @Test
+ @DisableFlags(StatusBarRootModernization.FLAG_NAME)
+ @EnableFlags(StatusBarConnectedDisplays.FLAG_NAME)
+ fun stop_statusBarModernizationFlagDisabled_removesFragment() {
+ val windowView = fakeStatusBarWindowViewInflater.inflatedMockViews.first()
+
+ underTest.stop()
+ fakeExecutor.runAllReady()
+
+ verify(mockFragmentService).removeAndDestroy(windowView)
+ }
+
+ @Test
+ @DisableFlags(StatusBarRootModernization.FLAG_NAME)
+ @EnableFlags(StatusBarConnectedDisplays.FLAG_NAME)
+ fun stop_statusBarModernizationFlagDisabled_removesFragmentOnExecutor() {
+ val windowView = fakeStatusBarWindowViewInflater.inflatedMockViews.first()
+
+ underTest.stop()
+
+ verify(mockFragmentService, never()).removeAndDestroy(windowView)
+ fakeExecutor.runAllReady()
+ verify(mockFragmentService).removeAndDestroy(windowView)
+ }
+
+ @Test
+ @EnableFlags(StatusBarConnectedDisplays.FLAG_NAME)
+ fun stop_removesWindowViewFromWindowManager() {
+ underTest.attach()
+ underTest.stop()
+
+ assertThat(fakeWindowManager.addedViews).isEmpty()
+ }
+
+ @Test(expected = IllegalStateException::class)
+ @DisableFlags(StatusBarConnectedDisplays.FLAG_NAME)
+ fun stop_connectedDisplaysFlagDisabled_crashes() {
+ underTest.stop()
+ }
+
+ @Test
+ fun attach_windowViewAddedToWindowManager() {
+ val windowView = fakeStatusBarWindowViewInflater.inflatedMockViews.first()
+
+ underTest.attach()
+
+ assertThat(fakeWindowManager.addedViews.keys).containsExactly(windowView)
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusUsiPowerStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/stylus/StylusUsiPowerStartableTest.kt
similarity index 97%
rename from packages/SystemUI/tests/src/com/android/systemui/stylus/StylusUsiPowerStartableTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/stylus/StylusUsiPowerStartableTest.kt
index 9592b28..798380a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusUsiPowerStartableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/stylus/StylusUsiPowerStartableTest.kt
@@ -18,8 +18,8 @@
import android.hardware.BatteryState
import android.hardware.input.InputManager
-import android.testing.AndroidTestingRunner
import android.view.InputDevice
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.flags.FeatureFlags
@@ -35,7 +35,7 @@
import org.mockito.Mockito.verifyNoMoreInteractions
import org.mockito.MockitoAnnotations
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
class StylusUsiPowerStartableTest : SysuiTestCase() {
@Mock lateinit var inputManager: InputManager
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/TestableAlertDialogTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/TestableAlertDialogTest.kt
similarity index 98%
rename from packages/SystemUI/tests/src/com/android/systemui/util/TestableAlertDialogTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/util/TestableAlertDialogTest.kt
index 01dd60a..4351d28 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/TestableAlertDialogTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/TestableAlertDialogTest.kt
@@ -21,8 +21,8 @@
import android.content.DialogInterface.BUTTON_NEGATIVE
import android.content.DialogInterface.BUTTON_NEUTRAL
import android.content.DialogInterface.BUTTON_POSITIVE
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.util.mockito.any
@@ -36,7 +36,7 @@
import org.mockito.Mockito.verify
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper
class TestableAlertDialogTest : SysuiTestCase() {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/util/settings/SettingsProxyTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/settings/SettingsProxyTest.kt
index 2e6d0fc..e25bf13 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/util/settings/SettingsProxyTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/settings/SettingsProxyTest.kt
@@ -27,7 +27,7 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope
@@ -57,7 +57,7 @@
@Before
fun setUp() {
testScope = TestScope(testDispatcher)
- mSettings = FakeSettingsProxy(testDispatcher)
+ mSettings = FakeSettingsProxy(testScope)
mContentObserver = object : ContentObserver(Handler(Looper.getMainLooper())) {}
}
@@ -92,7 +92,7 @@
mSettings.registerContentObserverSync(
TEST_SETTING,
notifyForDescendants = true,
- mContentObserver
+ mContentObserver,
)
verify(mSettings.getContentResolver())
.registerContentObserver(eq(TEST_SETTING_URI), eq(true), eq(mContentObserver))
@@ -104,7 +104,7 @@
mSettings.registerContentObserver(
TEST_SETTING,
notifyForDescendants = true,
- mContentObserver
+ mContentObserver,
)
verify(mSettings.getContentResolver())
.registerContentObserver(eq(TEST_SETTING_URI), eq(true), eq(mContentObserver))
@@ -116,7 +116,7 @@
mSettings.registerContentObserverAsync(
TEST_SETTING,
notifyForDescendants = true,
- mContentObserver
+ mContentObserver,
)
testScope.advanceUntilIdle()
verify(mSettings.getContentResolver())
@@ -154,7 +154,7 @@
mSettings.registerContentObserverSync(
TEST_SETTING_URI,
notifyForDescendants = true,
- mContentObserver
+ mContentObserver,
)
verify(mSettings.getContentResolver())
.registerContentObserver(eq(TEST_SETTING_URI), eq(true), eq(mContentObserver))
@@ -166,7 +166,7 @@
mSettings.registerContentObserver(
TEST_SETTING_URI,
notifyForDescendants = true,
- mContentObserver
+ mContentObserver,
)
verify(mSettings.getContentResolver())
.registerContentObserver(eq(TEST_SETTING_URI), eq(true), eq(mContentObserver))
@@ -178,7 +178,7 @@
mSettings.registerContentObserverAsync(
TEST_SETTING_URI,
notifyForDescendants = true,
- mContentObserver
+ mContentObserver,
)
testScope.advanceUntilIdle()
verify(mSettings.getContentResolver())
@@ -202,7 +202,7 @@
TEST_SETTING_URI,
false,
mContentObserver,
- it
+ it,
)
}
}
@@ -382,15 +382,15 @@
assertThat(mSettings.getFloat(TEST_SETTING, 2.5F)).isEqualTo(2.5F)
}
- private class FakeSettingsProxy(val testDispatcher: CoroutineDispatcher) : SettingsProxy {
+ private class FakeSettingsProxy(val testScope: CoroutineScope) : SettingsProxy {
private val mContentResolver = mock(ContentResolver::class.java)
private val settingToValueMap: MutableMap<String, String?> = mutableMapOf()
override fun getContentResolver() = mContentResolver
- override val backgroundDispatcher: CoroutineDispatcher
- get() = testDispatcher
+ override val settingsScope: CoroutineScope
+ get() = testScope
override fun getUriFor(name: String) =
Uri.parse(StringBuilder().append("content://settings/").append(name).toString())
@@ -408,7 +408,7 @@
name: String,
value: String?,
tag: String?,
- makeDefault: Boolean
+ makeDefault: Boolean,
): Boolean {
settingToValueMap[name] = value
return true
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/util/settings/UserSettingsProxyTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/settings/UserSettingsProxyTest.kt
index 00b8cd0..5787f7d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/util/settings/UserSettingsProxyTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/settings/UserSettingsProxyTest.kt
@@ -28,7 +28,7 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.launch
import kotlinx.coroutines.test.StandardTestDispatcher
@@ -36,7 +36,6 @@
import kotlinx.coroutines.test.advanceUntilIdle
import kotlinx.coroutines.test.runTest
import org.junit.Assert.assertThrows
-import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.mock
@@ -51,14 +50,9 @@
private var userId = MAIN_USER_ID
private val testDispatcher = StandardTestDispatcher()
- private var mSettings: UserSettingsProxy = FakeUserSettingsProxy({ userId }, testDispatcher)
+ private val testScope = TestScope(testDispatcher)
+ private var mSettings: UserSettingsProxy = FakeUserSettingsProxy({ userId }, testScope)
private var mContentObserver = object : ContentObserver(Handler(Looper.getMainLooper())) {}
- private lateinit var testScope: TestScope
-
- @Before
- fun setUp() {
- testScope = TestScope(testDispatcher)
- }
@Test
fun registerContentObserverForUser_inputString_success() =
@@ -69,7 +63,7 @@
eq(TEST_SETTING_URI),
eq(false),
eq(mContentObserver),
- eq(MAIN_USER_ID)
+ eq(MAIN_USER_ID),
)
}
@@ -82,7 +76,7 @@
eq(TEST_SETTING_URI),
eq(false),
eq(mContentObserver),
- eq(MAIN_USER_ID)
+ eq(MAIN_USER_ID),
)
}
@@ -96,7 +90,7 @@
eq(TEST_SETTING_URI),
eq(false),
eq(mContentObserver),
- eq(MAIN_USER_ID)
+ eq(MAIN_USER_ID),
)
}
@@ -107,14 +101,14 @@
TEST_SETTING,
notifyForDescendants = true,
mContentObserver,
- userId
+ userId,
)
verify(mSettings.getContentResolver())
.registerContentObserver(
eq(TEST_SETTING_URI),
eq(true),
eq(mContentObserver),
- eq(MAIN_USER_ID)
+ eq(MAIN_USER_ID),
)
}
@@ -125,16 +119,14 @@
TEST_SETTING,
notifyForDescendants = true,
mContentObserver,
- userId
+ userId,
)
verify(mSettings.getContentResolver())
.registerContentObserver(
eq(TEST_SETTING_URI),
- eq(
- true,
- ),
+ eq(true),
eq(mContentObserver),
- eq(MAIN_USER_ID)
+ eq(MAIN_USER_ID),
)
}
@@ -145,7 +137,7 @@
TEST_SETTING,
notifyForDescendants = true,
mContentObserver,
- userId
+ userId,
)
testScope.advanceUntilIdle()
verify(mSettings.getContentResolver())
@@ -153,7 +145,7 @@
eq(TEST_SETTING_URI),
eq(true),
eq(mContentObserver),
- eq(MAIN_USER_ID)
+ eq(MAIN_USER_ID),
)
}
@@ -166,7 +158,7 @@
eq(TEST_SETTING_URI),
eq(false),
eq(mContentObserver),
- eq(MAIN_USER_ID)
+ eq(MAIN_USER_ID),
)
}
@@ -179,7 +171,7 @@
eq(TEST_SETTING_URI),
eq(false),
eq(mContentObserver),
- eq(MAIN_USER_ID)
+ eq(MAIN_USER_ID),
)
}
@@ -189,7 +181,7 @@
mSettings.registerContentObserverForUserAsync(
TEST_SETTING_URI,
mContentObserver,
- userId
+ userId,
)
testScope.advanceUntilIdle()
@@ -198,7 +190,7 @@
eq(TEST_SETTING_URI),
eq(false),
eq(mContentObserver),
- eq(MAIN_USER_ID)
+ eq(MAIN_USER_ID),
)
}
@@ -213,7 +205,7 @@
TEST_SETTING_URI,
mContentObserver,
userId,
- runnable
+ runnable,
)
testScope.advanceUntilIdle()
assertThat(callbackCalled).isTrue()
@@ -226,14 +218,14 @@
TEST_SETTING_URI,
notifyForDescendants = true,
mContentObserver,
- userId
+ userId,
)
verify(mSettings.getContentResolver())
.registerContentObserver(
eq(TEST_SETTING_URI),
eq(true),
eq(mContentObserver),
- eq(MAIN_USER_ID)
+ eq(MAIN_USER_ID),
)
}
@@ -244,14 +236,14 @@
TEST_SETTING_URI,
notifyForDescendants = true,
mContentObserver,
- userId
+ userId,
)
verify(mSettings.getContentResolver())
.registerContentObserver(
eq(TEST_SETTING_URI),
eq(true),
eq(mContentObserver),
- eq(MAIN_USER_ID)
+ eq(MAIN_USER_ID),
)
}
@@ -262,7 +254,7 @@
TEST_SETTING_URI,
notifyForDescendants = true,
mContentObserver,
- userId
+ userId,
)
testScope.advanceUntilIdle()
verify(mSettings.getContentResolver())
@@ -270,7 +262,7 @@
eq(TEST_SETTING_URI),
eq(true),
eq(mContentObserver),
- eq(MAIN_USER_ID)
+ eq(MAIN_USER_ID),
)
}
@@ -283,7 +275,7 @@
eq(TEST_SETTING_URI),
eq(false),
eq(mContentObserver),
- eq(0)
+ eq(0),
)
}
@@ -296,7 +288,7 @@
eq(TEST_SETTING_URI),
eq(false),
eq(mContentObserver),
- eq(0)
+ eq(0),
)
}
@@ -309,7 +301,7 @@
eq(TEST_SETTING_URI),
eq(false),
eq(mContentObserver),
- eq(0)
+ eq(0),
)
}
}
@@ -320,14 +312,14 @@
mSettings.registerContentObserverSync(
TEST_SETTING_URI,
notifyForDescendants = true,
- mContentObserver
+ mContentObserver,
)
verify(mSettings.getContentResolver())
.registerContentObserver(
eq(TEST_SETTING_URI),
eq(true),
eq(mContentObserver),
- eq(0)
+ eq(0),
)
}
@@ -340,7 +332,7 @@
eq(TEST_SETTING_URI),
eq(false),
eq(mContentObserver),
- eq(0)
+ eq(0),
)
}
@@ -354,7 +346,7 @@
eq(TEST_SETTING_URI),
eq(false),
eq(mContentObserver),
- eq(0)
+ eq(0),
)
}
}
@@ -557,7 +549,7 @@
*/
private class FakeUserSettingsProxy(
override val currentUserProvider: SettingsProxy.CurrentUserIdProvider,
- val testDispatcher: CoroutineDispatcher
+ val testScope: CoroutineScope,
) : UserSettingsProxy {
private val mContentResolver = mock(ContentResolver::class.java)
@@ -569,8 +561,8 @@
override fun getUriFor(name: String) =
Uri.parse(StringBuilder().append(URI_PREFIX).append(name).toString())
- override val backgroundDispatcher: CoroutineDispatcher
- get() = testDispatcher
+ override val settingsScope: CoroutineScope
+ get() = testScope
override fun getStringForUser(name: String, userHandle: Int) =
userIdToSettingsValueMap[userHandle]?.get(name) ?: ""
@@ -578,7 +570,7 @@
override fun putString(
name: String,
value: String?,
- overrideableByRestore: Boolean
+ overrideableByRestore: Boolean,
): Boolean {
userIdToSettingsValueMap[DEFAULT_USER_ID]?.put(name, value)
return true
@@ -588,7 +580,7 @@
name: String,
value: String?,
tag: String?,
- makeDefault: Boolean
+ makeDefault: Boolean,
): Boolean {
putStringForUser(name, value, DEFAULT_USER_ID)
return true
@@ -605,7 +597,7 @@
tag: String?,
makeDefault: Boolean,
userHandle: Int,
- overrideableByRestore: Boolean
+ overrideableByRestore: Boolean,
): Boolean {
userIdToSettingsValueMap[userHandle]?.set(name, value)
return true
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/wakelock/KeepAwakeAnimationListenerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/wakelock/KeepAwakeAnimationListenerTest.java
similarity index 96%
rename from packages/SystemUI/tests/src/com/android/systemui/util/wakelock/KeepAwakeAnimationListenerTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/util/wakelock/KeepAwakeAnimationListenerTest.java
index 1ff9548..c6bfb35 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/wakelock/KeepAwakeAnimationListenerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/wakelock/KeepAwakeAnimationListenerTest.java
@@ -21,9 +21,9 @@
import static org.mockito.Mockito.verify;
import android.animation.Animator;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -36,7 +36,7 @@
@SmallTest
@TestableLooper.RunWithLooper
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
public class KeepAwakeAnimationListenerTest extends SysuiTestCase {
@Mock WakeLock mWakeLock;
KeepAwakeAnimationListener mKeepAwakeAnimationListener;
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java
index 1914867..75f3386 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java
@@ -23,6 +23,7 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -39,14 +40,13 @@
import android.media.session.MediaSession;
import android.os.Handler;
import android.os.Process;
-import android.platform.test.annotations.EnableFlags;
import android.testing.TestableLooper;
import android.view.accessibility.AccessibilityManager;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
-import com.android.settingslib.flags.Flags;
+import com.android.keyguard.TestScopeProvider;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.SysuiTestCaseExtKt;
import com.android.systemui.broadcast.BroadcastDispatcher;
@@ -64,6 +64,10 @@
import com.android.systemui.util.kotlin.JavaAdapter;
import com.android.systemui.util.time.FakeSystemClock;
import com.android.systemui.volume.domain.interactor.AudioSharingInteractor;
+import com.android.systemui.volume.domain.interactor.FakeAudioSharingInteractor;
+import com.android.systemui.volume.shared.VolumeLogger;
+
+import kotlinx.coroutines.test.TestScope;
import org.junit.Before;
import org.junit.Test;
@@ -94,6 +98,9 @@
private RingerModeLiveData mRingerModeInternalLiveData;
private final FakeThreadFactory mThreadFactory = new FakeThreadFactory(
new FakeExecutor(new FakeSystemClock()));
+ private final TestScope mTestScope = TestScopeProvider.getTestScope();
+ private final JavaAdapter mJavaAdapter = new JavaAdapter(mTestScope);
+ private FakeAudioSharingInteractor mFakeAudioSharingInteractor;
@Mock
private AudioManager mAudioManager;
@Mock
@@ -117,9 +124,7 @@
@Mock
private DumpManager mDumpManager;
@Mock
- private AudioSharingInteractor mAudioSharingInteractor;
- @Mock
- private JavaAdapter mJavaAdapter;
+ private VolumeLogger mVolumeLogger;
@Before
@@ -136,6 +141,8 @@
mCallback = mock(VolumeDialogControllerImpl.C.class);
mThreadFactory.setLooper(TestableLooper.get(this).getLooper());
+ mFakeAudioSharingInteractor = spy(new FakeAudioSharingInteractor());
+ mFakeAudioSharingInteractor.setAudioSharingVolumeBarAvailable(true);
mVolumeController =
new TestableVolumeDialogControllerImpl(
mContext,
@@ -155,8 +162,9 @@
mUserTracker,
mDumpManager,
mCallback,
- mAudioSharingInteractor,
- mJavaAdapter);
+ mFakeAudioSharingInteractor,
+ mJavaAdapter,
+ mVolumeLogger);
mVolumeController.setEnableDialogs(true, true);
}
@@ -305,13 +313,13 @@
}
@Test
- @EnableFlags(Flags.FLAG_VOLUME_DIALOG_AUDIO_SHARING_FIX)
public void testSetStreamVolume_setSecondaryDeviceVolume() {
mVolumeController.setStreamVolume(
VolumeDialogControllerImpl.DYNAMIC_STREAM_BROADCAST, /* level= */ 100);
Objects.requireNonNull(TestableLooper.get(this)).processAllMessages();
+ mTestScope.getTestScheduler().advanceUntilIdle();
- verify(mAudioSharingInteractor).setStreamVolume(100);
+ verify(mFakeAudioSharingInteractor).setStreamVolume(100);
}
static class TestableVolumeDialogControllerImpl extends VolumeDialogControllerImpl {
@@ -336,7 +344,8 @@
DumpManager dumpManager,
C callback,
AudioSharingInteractor audioSharingInteractor,
- JavaAdapter javaAdapter) {
+ JavaAdapter javaAdapter,
+ VolumeLogger volumeLogger) {
super(
context,
broadcastDispatcher,
@@ -355,7 +364,8 @@
userTracker,
dumpManager,
audioSharingInteractor,
- javaAdapter);
+ javaAdapter,
+ volumeLogger);
mCallbacks = callback;
ArgumentCaptor<WakefulnessLifecycle.Observer> observerCaptor =
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/VolumeDialogControllerImplTestKt.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/VolumeDialogControllerImplTestKt.kt
index 76b7b8f..a9c352d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/VolumeDialogControllerImplTestKt.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/VolumeDialogControllerImplTestKt.kt
@@ -49,9 +49,10 @@
import com.android.systemui.util.RingerModeLiveData
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.concurrency.FakeThreadFactory
+import com.android.systemui.util.kotlin.JavaAdapter
import com.android.systemui.util.time.fakeSystemClock
import com.android.systemui.volume.data.repository.audioRepository
-import com.android.systemui.volume.domain.interactor.audioSharingInteractor
+import com.android.systemui.volume.domain.interactor.FakeAudioSharingInteractor
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
@@ -76,6 +77,9 @@
private val kosmos: Kosmos = testKosmos()
private val audioManager: AudioManager = mock {}
private val callbacks: VolumeDialogController.Callbacks = mock {}
+ private val javaAdapter: JavaAdapter = JavaAdapter(kosmos.testScope)
+ private val fakeAudioSharingInteractor: FakeAudioSharingInteractor =
+ FakeAudioSharingInteractor()
private lateinit var threadFactory: FakeThreadFactory
private lateinit var underTest: VolumeDialogControllerImpl
@@ -87,6 +91,8 @@
threadFactory =
FakeThreadFactory(FakeExecutor(fakeSystemClock)).apply { setLooper(looper) }
broadcastDispatcherContext = testableContext
+ fakeAudioSharingInteractor.setAudioSharingVolumeBarAvailable(true)
+
underTest =
VolumeDialogControllerImpl(
applicationContext,
@@ -108,7 +114,8 @@
activityManager,
mock { on { userContext }.thenReturn(applicationContext) },
dumpManager,
- audioSharingInteractor,
+ fakeAudioSharingInteractor,
+ javaAdapter,
mock {},
)
.apply {
@@ -129,6 +136,7 @@
},
)
testableLooper.processAllMessages()
+ testScheduler.advanceUntilIdle()
verify(callbacks) { 1 * { onStateChanged(any()) } }
}
@@ -163,6 +171,7 @@
emitVolumeChange(AudioManager.STREAM_SYSTEM, AudioManager.FLAG_SHOW_UI)
runCurrent()
TestableLooper.get(this@VolumeDialogControllerImplTestKt).processAllMessages()
+ testScheduler.advanceUntilIdle()
verify(callbacks) { 1 * { onShowRequested(any(), any(), any()) } }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSlidersInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSlidersInteractorTest.kt
index 7c5a487..3f995c6 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSlidersInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSlidersInteractorTest.kt
@@ -155,6 +155,30 @@
}
}
+ @Test
+ fun activeStreamChanges_showBoth() {
+ with(kosmos) {
+ testScope.runTest {
+ runCurrent()
+ fakeVolumeDialogController.updateState {
+ activeStream = AudioManager.STREAM_SYSTEM
+ states.put(AudioManager.STREAM_MUSIC, buildStreamState())
+ states.put(AudioManager.STREAM_SYSTEM, buildStreamState())
+ }
+ val slidersModel by collectLastValue(underTest.sliders)
+ runCurrent()
+
+ fakeVolumeDialogController.updateState { activeStream = AudioManager.STREAM_MUSIC }
+ runCurrent()
+
+ assertThat(slidersModel!!.slider)
+ .isEqualTo(VolumeDialogSliderType.Stream(AudioManager.STREAM_MUSIC))
+ assertThat(slidersModel!!.floatingSliders)
+ .containsExactly(VolumeDialogSliderType.Stream(AudioManager.STREAM_SYSTEM))
+ }
+ }
+ }
+
private fun buildStreamState(
build: VolumeDialogController.StreamState.() -> Unit = {}
): VolumeDialogController.StreamState {
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockProviderPlugin.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockProviderPlugin.kt
index 89da465..fb9e96c 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockProviderPlugin.kt
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockProviderPlugin.kt
@@ -139,7 +139,9 @@
/** Message buffer for large clock rendering */
val largeClockMessageBuffer: MessageBuffer,
-)
+) {
+ constructor(buffer: MessageBuffer) : this(buffer, buffer, buffer) {}
+}
data class AodClockBurnInModel(val scale: Float, val translationX: Float, val translationY: Float)
diff --git a/packages/SystemUI/pods/com/android/systemui/util/settings/GlobalSettingsImpl.java b/packages/SystemUI/pods/com/android/systemui/util/settings/GlobalSettingsImpl.java
index d68501f..6b90952 100644
--- a/packages/SystemUI/pods/com/android/systemui/util/settings/GlobalSettingsImpl.java
+++ b/packages/SystemUI/pods/com/android/systemui/util/settings/GlobalSettingsImpl.java
@@ -25,7 +25,7 @@
import com.android.systemui.util.settings.SettingsSingleThreadBackground;
-import kotlinx.coroutines.CoroutineDispatcher;
+import kotlinx.coroutines.CoroutineScope;
import javax.inject.Inject;
@@ -33,13 +33,13 @@
@SuppressLint("StaticSettingsProvider")
class GlobalSettingsImpl implements GlobalSettings {
private final ContentResolver mContentResolver;
- private final CoroutineDispatcher mBgDispatcher;
+ private final CoroutineScope mSettingsScope;
@Inject
GlobalSettingsImpl(ContentResolver contentResolver,
- @SettingsSingleThreadBackground CoroutineDispatcher bgDispatcher) {
+ @SettingsSingleThreadBackground CoroutineScope settingsScope) {
mContentResolver = contentResolver;
- mBgDispatcher = bgDispatcher;
+ mSettingsScope = settingsScope;
}
@NonNull
@@ -56,8 +56,8 @@
@NonNull
@Override
- public CoroutineDispatcher getBackgroundDispatcher() {
- return mBgDispatcher;
+ public CoroutineScope getSettingsScope() {
+ return mSettingsScope;
}
@Override
diff --git a/packages/SystemUI/pods/com/android/systemui/util/settings/SecureSettingsImpl.java b/packages/SystemUI/pods/com/android/systemui/util/settings/SecureSettingsImpl.java
index 211a6f4..ae89a5f 100644
--- a/packages/SystemUI/pods/com/android/systemui/util/settings/SecureSettingsImpl.java
+++ b/packages/SystemUI/pods/com/android/systemui/util/settings/SecureSettingsImpl.java
@@ -23,23 +23,23 @@
import com.android.systemui.util.settings.SettingsSingleThreadBackground;
-import kotlinx.coroutines.CoroutineDispatcher;
+import kotlinx.coroutines.CoroutineScope;
import javax.inject.Inject;
class SecureSettingsImpl implements SecureSettings {
private final ContentResolver mContentResolver;
private final CurrentUserIdProvider mCurrentUserProvider;
- private final CoroutineDispatcher mBgDispatcher;
+ private final CoroutineScope mSettingsScope;
@Inject
SecureSettingsImpl(
ContentResolver contentResolver,
CurrentUserIdProvider currentUserProvider,
- @SettingsSingleThreadBackground CoroutineDispatcher bgDispatcher) {
+ @SettingsSingleThreadBackground CoroutineScope settingsScope) {
mContentResolver = contentResolver;
mCurrentUserProvider = currentUserProvider;
- mBgDispatcher = bgDispatcher;
+ mSettingsScope = settingsScope;
}
@NonNull
@@ -62,8 +62,8 @@
@NonNull
@Override
- public CoroutineDispatcher getBackgroundDispatcher() {
- return mBgDispatcher;
+ public CoroutineScope getSettingsScope() {
+ return mSettingsScope;
}
@Override
diff --git a/packages/SystemUI/pods/com/android/systemui/util/settings/SettingsProxy.kt b/packages/SystemUI/pods/com/android/systemui/util/settings/SettingsProxy.kt
index 5d0b0d5..154d3cc 100644
--- a/packages/SystemUI/pods/com/android/systemui/util/settings/SettingsProxy.kt
+++ b/packages/SystemUI/pods/com/android/systemui/util/settings/SettingsProxy.kt
@@ -23,10 +23,12 @@
import androidx.annotation.AnyThread
import androidx.annotation.WorkerThread
import com.android.app.tracing.TraceUtils.trace
-import com.android.systemui.coroutines.newTracingContext
+import com.android.app.tracing.coroutines.launchTraced as launch
+import com.android.app.tracing.coroutines.nameCoroutine
+import kotlin.coroutines.CoroutineContext
+import kotlin.coroutines.EmptyCoroutineContext
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
-import com.android.app.tracing.coroutines.launchTraced as launch
import kotlinx.coroutines.withContext
/**
@@ -47,11 +49,14 @@
/** Returns the [ContentResolver] this instance was constructed with. */
fun getContentResolver(): ContentResolver
- /**
- * Returns the background [CoroutineDispatcher] that the async APIs will use for a specific
- * implementation.
- */
- val backgroundDispatcher: CoroutineDispatcher
+ /** Returns the [CoroutineScope] that the async APIs will use. */
+ val settingsScope: CoroutineScope
+
+ @OptIn(ExperimentalStdlibApi::class)
+ fun settingsDispatcherContext(name: String): CoroutineContext {
+ return (settingsScope.coroutineContext[CoroutineDispatcher] ?: EmptyCoroutineContext) +
+ nameCoroutine(name)
+ }
/**
* Construct the content URI for a particular name/value pair, useful for monitoring changes
@@ -82,7 +87,7 @@
* wish to synchronize execution.
*/
suspend fun registerContentObserver(name: String, settingsObserver: ContentObserver) {
- withContext(backgroundDispatcher) {
+ withContext(settingsDispatcherContext("registerContentObserver-A")) {
registerContentObserverSync(getUriFor(name), settingsObserver)
}
}
@@ -94,7 +99,7 @@
*/
@AnyThread
fun registerContentObserverAsync(name: String, settingsObserver: ContentObserver) =
- CoroutineScope(backgroundDispatcher + newTracingContext("SettingsProxy-A")).launch {
+ settingsScope.launch("registerContentObserverAsync-A") {
registerContentObserverSync(getUriFor(name), settingsObserver)
}
@@ -111,7 +116,7 @@
settingsObserver: ContentObserver,
@WorkerThread registered: Runnable,
) =
- CoroutineScope(backgroundDispatcher + newTracingContext("SettingsProxy-B")).launch {
+ settingsScope.launch("registerContentObserverAsync-B") {
registerContentObserverSync(getUriFor(name), settingsObserver)
registered.run()
}
@@ -134,7 +139,9 @@
* wish to synchronize execution.
*/
suspend fun registerContentObserver(uri: Uri, settingsObserver: ContentObserver) {
- withContext(backgroundDispatcher) { registerContentObserverSync(uri, settingsObserver) }
+ withContext(settingsDispatcherContext("registerContentObserver-B")) {
+ registerContentObserverSync(uri, settingsObserver)
+ }
}
/**
@@ -144,7 +151,7 @@
*/
@AnyThread
fun registerContentObserverAsync(uri: Uri, settingsObserver: ContentObserver) =
- CoroutineScope(backgroundDispatcher + newTracingContext("SettingsProxy-C")).launch {
+ settingsScope.launch("registerContentObserverAsync-C") {
registerContentObserverSync(uri, settingsObserver)
}
@@ -161,7 +168,7 @@
settingsObserver: ContentObserver,
@WorkerThread registered: Runnable,
) =
- CoroutineScope(backgroundDispatcher + newTracingContext("SettingsProxy-D")).launch {
+ settingsScope.launch("registerContentObserverAsync-D") {
registerContentObserverSync(uri, settingsObserver)
registered.run()
}
@@ -188,9 +195,9 @@
suspend fun registerContentObserver(
name: String,
notifyForDescendants: Boolean,
- settingsObserver: ContentObserver
+ settingsObserver: ContentObserver,
) {
- withContext(backgroundDispatcher) {
+ withContext(settingsDispatcherContext("registerContentObserver-C")) {
registerContentObserverSync(getUriFor(name), notifyForDescendants, settingsObserver)
}
}
@@ -206,7 +213,7 @@
notifyForDescendants: Boolean,
settingsObserver: ContentObserver,
) =
- CoroutineScope(backgroundDispatcher + newTracingContext("SettingsProxy-E")).launch {
+ settingsScope.launch("registerContentObserverAsync-E") {
registerContentObserverSync(getUriFor(name), notifyForDescendants, settingsObserver)
}
@@ -224,7 +231,7 @@
settingsObserver: ContentObserver,
@WorkerThread registered: Runnable,
) =
- CoroutineScope(backgroundDispatcher + newTracingContext("SettingsProxy-F")).launch {
+ settingsScope.launch("registerContentObserverAsync-F") {
registerContentObserverSync(getUriFor(name), notifyForDescendants, settingsObserver)
registered.run()
}
@@ -239,7 +246,7 @@
fun registerContentObserverSync(
uri: Uri,
notifyForDescendants: Boolean,
- settingsObserver: ContentObserver
+ settingsObserver: ContentObserver,
) {
trace({ "SP#registerObserver#[$uri]" }) {
getContentResolver()
@@ -257,9 +264,9 @@
suspend fun registerContentObserver(
uri: Uri,
notifyForDescendants: Boolean,
- settingsObserver: ContentObserver
+ settingsObserver: ContentObserver,
) {
- withContext(backgroundDispatcher) {
+ withContext(settingsDispatcherContext("registerContentObserver-D")) {
registerContentObserverSync(uri, notifyForDescendants, settingsObserver)
}
}
@@ -275,7 +282,7 @@
notifyForDescendants: Boolean,
settingsObserver: ContentObserver,
) =
- CoroutineScope(backgroundDispatcher + newTracingContext("SettingsProxy-G")).launch {
+ settingsScope.launch("registerContentObserverAsync-G") {
registerContentObserverSync(uri, notifyForDescendants, settingsObserver)
}
@@ -293,7 +300,7 @@
settingsObserver: ContentObserver,
@WorkerThread registered: Runnable,
) =
- CoroutineScope(backgroundDispatcher + newTracingContext("SettingsProxy-H")).launch {
+ settingsScope.launch("registerContentObserverAsync-H") {
registerContentObserverSync(uri, notifyForDescendants, settingsObserver)
registered.run()
}
@@ -319,7 +326,9 @@
* async block if they wish to synchronize execution.
*/
suspend fun unregisterContentObserver(settingsObserver: ContentObserver) {
- withContext(backgroundDispatcher) { unregisterContentObserverSync(settingsObserver) }
+ withContext(settingsDispatcherContext("unregisterContentObserver")) {
+ unregisterContentObserverSync(settingsObserver)
+ }
}
/**
@@ -330,7 +339,7 @@
*/
@AnyThread
fun unregisterContentObserverAsync(settingsObserver: ContentObserver) =
- CoroutineScope(backgroundDispatcher + newTracingContext("SettingsProxy-I")).launch {
+ settingsScope.launch("unregisterContentObserverAsync") {
unregisterContentObserver(settingsObserver)
}
diff --git a/packages/SystemUI/pods/com/android/systemui/util/settings/SystemSettingsImpl.java b/packages/SystemUI/pods/com/android/systemui/util/settings/SystemSettingsImpl.java
index 1b3f74e..65d1c27 100644
--- a/packages/SystemUI/pods/com/android/systemui/util/settings/SystemSettingsImpl.java
+++ b/packages/SystemUI/pods/com/android/systemui/util/settings/SystemSettingsImpl.java
@@ -23,22 +23,22 @@
import com.android.systemui.util.settings.SettingsSingleThreadBackground;
-import kotlinx.coroutines.CoroutineDispatcher;
+import kotlinx.coroutines.CoroutineScope;
import javax.inject.Inject;
class SystemSettingsImpl implements SystemSettings {
private final ContentResolver mContentResolver;
private final CurrentUserIdProvider mCurrentUserProvider;
- private final CoroutineDispatcher mBgCoroutineDispatcher;
+ private final CoroutineScope mSettingsScope;
@Inject
SystemSettingsImpl(ContentResolver contentResolver,
CurrentUserIdProvider currentUserProvider,
- @SettingsSingleThreadBackground CoroutineDispatcher bgDispatcher) {
+ @SettingsSingleThreadBackground CoroutineScope settingsScope) {
mContentResolver = contentResolver;
mCurrentUserProvider = currentUserProvider;
- mBgCoroutineDispatcher = bgDispatcher;
+ mSettingsScope = settingsScope;
}
@NonNull
@@ -61,8 +61,8 @@
@NonNull
@Override
- public CoroutineDispatcher getBackgroundDispatcher() {
- return mBgCoroutineDispatcher;
+ public CoroutineScope getSettingsScope() {
+ return mSettingsScope;
}
@Override
diff --git a/packages/SystemUI/pods/com/android/systemui/util/settings/UserSettingsProxy.kt b/packages/SystemUI/pods/com/android/systemui/util/settings/UserSettingsProxy.kt
index 4b03df6..1a55170 100644
--- a/packages/SystemUI/pods/com/android/systemui/util/settings/UserSettingsProxy.kt
+++ b/packages/SystemUI/pods/com/android/systemui/util/settings/UserSettingsProxy.kt
@@ -23,13 +23,11 @@
import android.os.UserHandle
import android.provider.Settings.SettingNotFoundException
import com.android.app.tracing.TraceUtils.trace
-import com.android.systemui.coroutines.newTracingContext
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.util.settings.SettingsProxy.Companion.parseFloat
import com.android.systemui.util.settings.SettingsProxy.Companion.parseFloatOrThrow
import com.android.systemui.util.settings.SettingsProxy.Companion.parseLongOrThrow
import com.android.systemui.util.settings.SettingsProxy.Companion.parseLongOrUseDefault
-import kotlinx.coroutines.CoroutineScope
-import com.android.app.tracing.coroutines.launchTraced as launch
import kotlinx.coroutines.withContext
/**
@@ -73,13 +71,13 @@
}
override suspend fun registerContentObserver(uri: Uri, settingsObserver: ContentObserver) {
- withContext(backgroundDispatcher) {
+ withContext(settingsDispatcherContext("registerContentObserver-A")) {
registerContentObserverForUserSync(uri, settingsObserver, userId)
}
}
override fun registerContentObserverAsync(uri: Uri, settingsObserver: ContentObserver) =
- CoroutineScope(backgroundDispatcher + newTracingContext("UserSettingsProxy-A")).launch {
+ settingsScope.launch("registerContentObserverAsync-A") {
registerContentObserverForUserSync(uri, settingsObserver, userId)
}
@@ -96,9 +94,9 @@
override suspend fun registerContentObserver(
uri: Uri,
notifyForDescendants: Boolean,
- settingsObserver: ContentObserver
+ settingsObserver: ContentObserver,
) {
- withContext(backgroundDispatcher) {
+ withContext(settingsDispatcherContext("registerContentObserver-B")) {
registerContentObserverForUserSync(uri, notifyForDescendants, settingsObserver, userId)
}
}
@@ -113,7 +111,7 @@
notifyForDescendants: Boolean,
settingsObserver: ContentObserver,
) =
- CoroutineScope(backgroundDispatcher + newTracingContext("UserSettingsProxy-B")).launch {
+ settingsScope.launch("registerContentObserverAsync-B") {
registerContentObserverForUserSync(uri, notifyForDescendants, settingsObserver, userId)
}
@@ -126,7 +124,7 @@
fun registerContentObserverForUserSync(
name: String,
settingsObserver: ContentObserver,
- userHandle: Int
+ userHandle: Int,
) {
registerContentObserverForUserSync(getUriFor(name), settingsObserver, userHandle)
}
@@ -141,9 +139,9 @@
suspend fun registerContentObserverForUser(
name: String,
settingsObserver: ContentObserver,
- userHandle: Int
+ userHandle: Int,
) {
- withContext(backgroundDispatcher) {
+ withContext(settingsDispatcherContext("registerContentObserverForUser-A")) {
registerContentObserverForUserSync(name, settingsObserver, userHandle)
}
}
@@ -158,7 +156,7 @@
settingsObserver: ContentObserver,
userHandle: Int,
) =
- CoroutineScope(backgroundDispatcher + newTracingContext("UserSettingsProxy-C")).launch {
+ settingsScope.launch("registerContentObserverForUserAsync-A") {
registerContentObserverForUserSync(getUriFor(name), settingsObserver, userHandle)
}
@@ -167,7 +165,7 @@
fun registerContentObserverForUserSync(
uri: Uri,
settingsObserver: ContentObserver,
- userHandle: Int
+ userHandle: Int,
) {
registerContentObserverForUserSync(uri, false, settingsObserver, userHandle)
}
@@ -182,9 +180,9 @@
suspend fun registerContentObserverForUser(
uri: Uri,
settingsObserver: ContentObserver,
- userHandle: Int
+ userHandle: Int,
) {
- withContext(backgroundDispatcher) {
+ withContext(settingsDispatcherContext("registerContentObserverForUser-B")) {
registerContentObserverForUserSync(uri, settingsObserver, userHandle)
}
}
@@ -199,7 +197,7 @@
settingsObserver: ContentObserver,
userHandle: Int,
) =
- CoroutineScope(backgroundDispatcher + newTracingContext("UserSettingsProxy-D")).launch {
+ settingsScope.launch("registerContentObserverForUserAsync-B") {
registerContentObserverForUserSync(uri, settingsObserver, userHandle)
}
@@ -216,7 +214,7 @@
userHandle: Int,
@WorkerThread registered: Runnable,
) =
- CoroutineScope(backgroundDispatcher + newTracingContext("UserSettingsProxy-E")).launch {
+ settingsScope.launch("registerContentObserverForUserAsync-C") {
registerContentObserverForUserSync(uri, settingsObserver, userHandle)
registered.run()
}
@@ -231,13 +229,13 @@
name: String,
notifyForDescendants: Boolean,
settingsObserver: ContentObserver,
- userHandle: Int
+ userHandle: Int,
) {
registerContentObserverForUserSync(
getUriFor(name),
notifyForDescendants,
settingsObserver,
- userHandle
+ userHandle,
)
}
@@ -252,14 +250,14 @@
name: String,
notifyForDescendants: Boolean,
settingsObserver: ContentObserver,
- userHandle: Int
+ userHandle: Int,
) {
- withContext(backgroundDispatcher) {
+ withContext(settingsDispatcherContext("registerContentObserverForUser-C")) {
registerContentObserverForUserSync(
name,
notifyForDescendants,
settingsObserver,
- userHandle
+ userHandle,
)
}
}
@@ -275,7 +273,7 @@
settingsObserver: ContentObserver,
userHandle: Int,
) {
- CoroutineScope(backgroundDispatcher + newTracingContext("UserSettingsProxy-F")).launch {
+ settingsScope.launch("registerContentObserverForUserAsync-D") {
registerContentObserverForUserSync(
getUriFor(name),
notifyForDescendants,
@@ -291,7 +289,7 @@
uri: Uri,
notifyForDescendants: Boolean,
settingsObserver: ContentObserver,
- userHandle: Int
+ userHandle: Int,
) {
trace({ "USP#registerObserver#[$uri]" }) {
getContentResolver()
@@ -299,7 +297,7 @@
uri,
notifyForDescendants,
settingsObserver,
- getRealUserHandle(userHandle)
+ getRealUserHandle(userHandle),
)
Unit
}
@@ -316,14 +314,14 @@
uri: Uri,
notifyForDescendants: Boolean,
settingsObserver: ContentObserver,
- userHandle: Int
+ userHandle: Int,
) {
- withContext(backgroundDispatcher) {
+ withContext(settingsDispatcherContext("registerContentObserverForUser-D")) {
registerContentObserverForUserSync(
uri,
notifyForDescendants,
settingsObserver,
- getRealUserHandle(userHandle)
+ getRealUserHandle(userHandle),
)
}
}
@@ -339,7 +337,7 @@
settingsObserver: ContentObserver,
userHandle: Int,
) =
- CoroutineScope(backgroundDispatcher + newTracingContext("UserSettingsProxy-G")).launch {
+ settingsScope.launch("registerContentObserverForUserAsync-E") {
registerContentObserverForUserSync(
uri,
notifyForDescendants,
@@ -385,7 +383,7 @@
tag: String?,
makeDefault: Boolean,
@UserIdInt userHandle: Int,
- overrideableByRestore: Boolean
+ overrideableByRestore: Boolean,
): Boolean
override fun getInt(name: String, default: Int): Int {
diff --git a/packages/SystemUI/res/drawable/volume_background_top.xml b/packages/SystemUI/res/drawable/volume_background_top.xml
index 75849be..132572a 100644
--- a/packages/SystemUI/res/drawable/volume_background_top.xml
+++ b/packages/SystemUI/res/drawable/volume_background_top.xml
@@ -17,7 +17,6 @@
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
<item>
<shape>
- <size android:width="@dimen/volume_dialog_width" />
<solid android:color="?androidprv:attr/materialColorSurface" />
<corners android:topLeftRadius="@dimen/volume_dialog_background_corner_radius"
android:topRightRadius="@dimen/volume_dialog_background_corner_radius"/>
diff --git a/packages/SystemUI/res/drawable/volume_dialog_spacer.xml b/packages/SystemUI/res/drawable/volume_dialog_spacer.xml
deleted file mode 100644
index 3c60784..0000000
--- a/packages/SystemUI/res/drawable/volume_dialog_spacer.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?><!--
- Copyright (C) 2024 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
- android:shape="rectangle">
- <size
- android:width="@dimen/volume_dialog_spacing"
- android:height="@dimen/volume_dialog_spacing" />
- <solid android:color="@color/transparent" />
-</shape>
diff --git a/packages/SystemUI/res/layout/activity_rear_display_front_screen_on.xml b/packages/SystemUI/res/layout/activity_rear_display_front_screen_on.xml
new file mode 100644
index 0000000..a8d4d2e
--- /dev/null
+++ b/packages/SystemUI/res/layout/activity_rear_display_front_screen_on.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2024 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:gravity="center"
+ android:paddingStart="@dimen/dialog_side_padding"
+ android:paddingEnd="@dimen/dialog_side_padding"
+ android:paddingTop="@dimen/dialog_top_padding"
+ android:paddingBottom="@dimen/dialog_bottom_padding">
+
+ <androidx.cardview.widget.CardView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ app:cardElevation="0dp"
+ app:cardCornerRadius="28dp"
+ app:cardBackgroundColor="@color/rear_display_overlay_animation_background_color">
+
+ <com.android.systemui.reardisplay.RearDisplayEducationLottieViewWrapper
+ android:id="@+id/rear_display_folded_animation"
+ android:importantForAccessibility="no"
+ android:layout_width="@dimen/rear_display_animation_width_opened"
+ android:layout_height="@dimen/rear_display_animation_height_opened"
+ android:layout_gravity="center"
+ android:contentDescription="@string/rear_display_accessibility_unfolded_animation"
+ android:scaleType="fitXY"
+ app:lottie_rawRes="@raw/rear_display_turnaround"
+ app:lottie_autoPlay="true"
+ app:lottie_repeatMode="reverse"/>
+ </androidx.cardview.widget.CardView>
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/rear_display_unfolded_front_screen_on"
+ android:textAppearance="@style/TextAppearance.Dialog.Title"
+ android:lineSpacingExtra="2sp"
+ android:translationY="-1.24sp"
+ android:gravity="center_horizontal" />
+
+ <!-- Buttons -->
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:layout_marginTop="36dp">
+ <Space
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"/>
+ <TextView
+ android:id="@+id/button_cancel"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="0"
+ android:layout_gravity="start"
+ android:text="@string/cancel"
+ style="@style/Widget.Dialog.Button.BorderButton" />
+ </LinearLayout>
+
+</LinearLayout>
diff --git a/packages/SystemUI/res/layout/hybrid_conversation_notification.xml b/packages/SystemUI/res/layout/hybrid_conversation_notification.xml
index a313833..4b2f45f 100644
--- a/packages/SystemUI/res/layout/hybrid_conversation_notification.xml
+++ b/packages/SystemUI/res/layout/hybrid_conversation_notification.xml
@@ -27,12 +27,13 @@
android:layout_height="@dimen/conversation_single_line_face_pile_size"
android:paddingHorizontal="16dp"
>
- <ImageView
+ <com.android.internal.widget.CachingIconView
android:id="@*android:id/conversation_icon"
android:layout_width="@dimen/conversation_single_line_avatar_size"
android:layout_height="@dimen/conversation_single_line_avatar_size"
android:layout_gravity="center_vertical|end"
- />
+ android:scaleType="centerCrop"
+ />
<ViewStub
android:id="@*android:id/conversation_face_pile"
diff --git a/packages/SystemUI/res/layout/notification_2025_hybrid.xml b/packages/SystemUI/res/layout/notification_2025_hybrid.xml
new file mode 100644
index 0000000..8c34cd4
--- /dev/null
+++ b/packages/SystemUI/res/layout/notification_2025_hybrid.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2024 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+
+<!-- extends LinearLayout -->
+<com.android.systemui.statusbar.notification.row.HybridNotificationView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="bottom|start"
+ android:paddingStart="@*android:dimen/notification_2025_content_margin_start"
+ android:paddingEnd="12dp">
+ <TextView
+ android:id="@+id/notification_title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Notification.Title"
+ android:paddingEnd="4dp"
+ />
+ <TextView
+ android:id="@+id/notification_text"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:paddingEnd="4dp"
+ style="@*android:style/Widget.DeviceDefault.Notification.Text"
+ />
+</com.android.systemui.statusbar.notification.row.HybridNotificationView>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/notification_2025_hybrid_conversation.xml b/packages/SystemUI/res/layout/notification_2025_hybrid_conversation.xml
new file mode 100644
index 0000000..ff5d9d3
--- /dev/null
+++ b/packages/SystemUI/res/layout/notification_2025_hybrid_conversation.xml
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2024 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<!-- extends LinearLayout -->
+<com.android.systemui.statusbar.notification.row.HybridConversationNotificationView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_vertical|start"
+ android:paddingStart="@*android:dimen/notification_2025_content_margin_start"
+ android:paddingEnd="12dp">
+
+ <FrameLayout
+ android:layout_width="@dimen/notification_2025_single_line_face_pile_size"
+ android:layout_height="@dimen/notification_2025_single_line_face_pile_size"
+ android:layout_marginEnd="8dp"
+ >
+ <ImageView
+ android:id="@*android:id/conversation_icon"
+ android:layout_width="@dimen/notification_2025_single_line_avatar_size"
+ android:layout_height="@dimen/notification_2025_single_line_avatar_size"
+ android:layout_gravity="center_vertical|end"
+ />
+
+ <ViewStub
+ android:id="@*android:id/conversation_face_pile"
+ android:layout="@*android:layout/conversation_face_pile_layout"
+ android:layout_width="@dimen/notification_2025_single_line_face_pile_size"
+ android:layout_height="@dimen/notification_2025_single_line_face_pile_size"
+ android:layout_gravity="center_vertical|end"
+ />
+ </FrameLayout>
+
+ <TextView
+ android:id="@+id/notification_title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:paddingEnd="4dp"
+ android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Notification.Title"
+ />
+
+ <TextView
+ android:id="@+id/conversation_notification_sender"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:paddingEnd="4dp"
+ style="@*android:style/Widget.DeviceDefault.Notification.Text"
+ />
+
+ <TextView
+ android:id="@+id/notification_text"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:paddingEnd="4dp"
+ style="@*android:style/Widget.DeviceDefault.Notification.Text"
+ />
+</com.android.systemui.statusbar.notification.row.HybridConversationNotificationView>
diff --git a/packages/SystemUI/res/layout/ongoing_activity_chip.xml b/packages/SystemUI/res/layout/ongoing_activity_chip.xml
index d0a1ce8..215e4e4 100644
--- a/packages/SystemUI/res/layout/ongoing_activity_chip.xml
+++ b/packages/SystemUI/res/layout/ongoing_activity_chip.xml
@@ -55,9 +55,14 @@
/>
<!-- Shows generic text. -->
+ <!-- Since there's so little room in the status bar chip area, don't ellipsize the text and
+ instead just fade it out a bit at the end. -->
<TextView
android:id="@+id/ongoing_activity_chip_text"
style="@style/StatusBar.Chip.Text"
+ android:ellipsize="none"
+ android:requiresFadingEdge="horizontal"
+ android:fadingEdgeLength="@dimen/ongoing_activity_chip_text_fading_edge_length"
android:visibility="gone"
/>
diff --git a/packages/SystemUI/res/layout/status_bar_notification_footer_redesign.xml b/packages/SystemUI/res/layout/status_bar_notification_footer_redesign.xml
index 7c59aad..71c77a5 100644
--- a/packages/SystemUI/res/layout/status_bar_notification_footer_redesign.xml
+++ b/packages/SystemUI/res/layout/status_bar_notification_footer_redesign.xml
@@ -44,13 +44,13 @@
android:layout_height="wrap_content">
<com.android.systemui.statusbar.notification.row.FooterViewButton
- android:id="@+id/settings_button"
+ android:id="@+id/history_button"
style="@style/TextAppearance.NotificationFooterButtonRedesign"
android:layout_width="wrap_content"
android:layout_height="48dp"
android:background="@drawable/notif_footer_btn_background"
- android:contentDescription="@string/notification_settings_button_description"
- android:drawableStart="@drawable/notif_footer_btn_settings"
+ android:contentDescription="@string/notification_history_button_description"
+ android:drawableStart="@drawable/notif_footer_btn_history"
android:focusable="true"
app:layout_constraintStart_toStartOf="parent" />
@@ -64,17 +64,17 @@
android:contentDescription="@string/accessibility_clear_all"
android:focusable="true"
android:text="@string/clear_all_notifications_text"
- app:layout_constraintEnd_toStartOf="@id/history_button"
- app:layout_constraintStart_toEndOf="@id/settings_button" />
+ app:layout_constraintEnd_toStartOf="@id/settings_button"
+ app:layout_constraintStart_toEndOf="@id/history_button" />
<com.android.systemui.statusbar.notification.row.FooterViewButton
- android:id="@+id/history_button"
+ android:id="@+id/settings_button"
style="@style/TextAppearance.NotificationFooterButtonRedesign"
android:layout_width="wrap_content"
android:layout_height="48dp"
android:background="@drawable/notif_footer_btn_background"
- android:contentDescription="@string/notification_history_button_description"
- android:drawableStart="@drawable/notif_footer_btn_history"
+ android:contentDescription="@string/notification_settings_button_description"
+ android:drawableStart="@drawable/notif_footer_btn_settings"
android:focusable="true"
app:layout_constraintEnd_toEndOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/packages/SystemUI/res/layout/volume_dialog.xml b/packages/SystemUI/res/layout/volume_dialog.xml
index f187ce6..694357d 100644
--- a/packages/SystemUI/res/layout/volume_dialog.xml
+++ b/packages/SystemUI/res/layout/volume_dialog.xml
@@ -13,58 +13,70 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<androidx.constraintlayout.motion.widget.MotionLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/volume_dialog_root"
android:layout_width="match_parent"
- android:layout_height="match_parent">
+ android:layout_height="match_parent"
+ app:layoutDescription="@xml/volume_dialog_scene">
- <LinearLayout
- android:id="@+id/volume_dialog_container"
+ <View
+ android:id="@+id/volume_dialog_background"
+ android:layout_width="@dimen/volume_dialog_width"
+ android:layout_height="0dp"
+ android:layout_marginTop="@dimen/volume_dialog_background_vertical_margin"
+ android:layout_marginBottom="@dimen/volume_dialog_background_vertical_margin"
+ android:background="@drawable/volume_dialog_background"
+ app:layout_constraintBottom_toBottomOf="@id/volume_dialog_settings"
+ app:layout_constraintEnd_toEndOf="@id/volume_dialog_main_slider_container"
+ app:layout_constraintStart_toStartOf="@id/volume_dialog_main_slider_container"
+ app:layout_constraintTop_toTopOf="@id/volume_ringer_and_drawer_container" />
+
+ <include
+ android:id="@id/volume_ringer_and_drawer_container"
+ layout="@layout/volume_ringer_drawer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_gravity="center_vertical|end"
+ android:layout_marginBottom="@dimen/volume_dialog_components_spacing"
+ app:layout_constraintBottom_toTopOf="@id/volume_dialog_main_slider_container"
+ app:layout_constraintEnd_toEndOf="@id/volume_dialog_main_slider_container"
+ app:layout_constraintStart_toStartOf="@id/volume_dialog_main_slider_container"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintVertical_bias="1" />
+
+ <include
+ android:id="@+id/volume_dialog_main_slider_container"
+ layout="@layout/volume_dialog_slider" />
+
+ <ImageButton
+ android:id="@+id/volume_dialog_settings"
+ android:layout_width="@dimen/volume_dialog_button_size"
+ android:layout_height="@dimen/volume_dialog_button_size"
+ android:layout_marginTop="@dimen/volume_dialog_components_spacing"
+ android:background="@drawable/ripple_drawable_20dp"
+ android:contentDescription="@string/accessibility_volume_settings"
+ android:soundEffectsEnabled="false"
+ android:src="@drawable/horizontal_ellipsis"
+ android:tint="?androidprv:attr/materialColorPrimary"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toEndOf="@id/volume_dialog_main_slider_container"
+ app:layout_constraintStart_toStartOf="@id/volume_dialog_main_slider_container"
+ app:layout_constraintTop_toBottomOf="@id/volume_dialog_main_slider_container"
+ app:layout_constraintVertical_bias="0" />
+
+ <LinearLayout
+ android:id="@+id/volume_dialog_floating_sliders_container"
+ android:layout_width="wrap_content"
+ android:layout_height="0dp"
+ android:layout_marginTop="@dimen/volume_dialog_floating_sliders_vertical_padding_negative"
+ android:layout_marginBottom="@dimen/volume_dialog_floating_sliders_vertical_padding_negative"
android:divider="@drawable/volume_dialog_floating_sliders_spacer"
+ android:gravity="bottom"
android:orientation="horizontal"
- android:showDividers="middle|end|beginning">
+ android:showDividers="middle|beginning|end"
+ app:layout_constraintBottom_toBottomOf="@id/volume_dialog_main_slider_container"
+ app:layout_constraintEnd_toStartOf="@id/volume_dialog_background"
+ app:layout_constraintTop_toTopOf="@id/volume_dialog_main_slider_container" />
- <LinearLayout
- android:id="@+id/volume_dialog_floating_sliders_container"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:divider="@drawable/volume_dialog_floating_sliders_spacer"
- android:gravity="bottom"
- android:orientation="horizontal"
- android:paddingBottom="@dimen/volume_dialog_floating_sliders_bottom_padding"
- android:showDividers="middle" />
-
- <LinearLayout
- android:id="@+id/volume_dialog"
- android:layout_width="@dimen/volume_dialog_width"
- android:layout_height="wrap_content"
- android:background="@drawable/volume_dialog_background"
- android:clipChildren="false"
- android:clipToOutline="false"
- android:clipToPadding="false"
- android:divider="@drawable/volume_dialog_spacer"
- android:gravity="center_horizontal"
- android:orientation="vertical"
- android:paddingVertical="@dimen/volume_dialog_vertical_padding"
- android:showDividers="middle">
-
- <include layout="@layout/volume_ringer_drawer" />
-
- <include layout="@layout/volume_dialog_slider" />
-
- <ImageButton
- android:id="@+id/volume_dialog_settings"
- android:layout_width="@dimen/volume_dialog_button_size"
- android:layout_height="@dimen/volume_dialog_button_size"
- android:background="@drawable/ripple_drawable_20dp"
- android:contentDescription="@string/accessibility_volume_settings"
- android:soundEffectsEnabled="false"
- android:src="@drawable/horizontal_ellipsis"
- android:tint="?androidprv:attr/materialColorPrimary" />
- </LinearLayout>
- </LinearLayout>
-</FrameLayout>
\ No newline at end of file
+</androidx.constraintlayout.motion.widget.MotionLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/volume_ringer_drawer.xml b/packages/SystemUI/res/layout/volume_ringer_drawer.xml
index 7c266e6..b71c470 100644
--- a/packages/SystemUI/res/layout/volume_ringer_drawer.xml
+++ b/packages/SystemUI/res/layout/volume_ringer_drawer.xml
@@ -19,13 +19,10 @@
android:id="@+id/volume_ringer_and_drawer_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:gravity="center"
- android:paddingLeft="@dimen/volume_dialog_ringer_horizontal_padding"
- android:paddingRight="@dimen/volume_dialog_ringer_horizontal_padding"
- android:layoutDirection="ltr"
- android:clipToPadding="false"
android:clipChildren="false"
- android:background="@drawable/volume_background_top">
+ android:clipToPadding="false"
+ android:gravity="center"
+ android:layoutDirection="ltr">
<!-- Drawer view, invisible by default. -->
<FrameLayout
@@ -37,10 +34,10 @@
<!-- View that is animated to a tapped ringer selection, so it appears selected. -->
<FrameLayout
android:id="@+id/volume_drawer_selection_background"
- android:alpha="0.0"
android:layout_width="@dimen/volume_dialog_ringer_drawer_button_size"
android:layout_height="@dimen/volume_dialog_ringer_drawer_button_size"
android:layout_gravity="bottom|right"
+ android:alpha="0.0"
android:background="@drawable/volume_drawer_selection_bg" />
<LinearLayout
@@ -65,7 +62,6 @@
android:background="@drawable/volume_drawer_selection_bg"
android:contentDescription="@string/volume_ringer_change"
android:gravity="center"
- android:padding="@dimen/volume_dialog_ringer_horizontal_padding"
android:src="@drawable/ic_volume_media"
android:tint="?androidprv:attr/materialColorOnPrimary" />
diff --git a/packages/SystemUI/res/values-land/dimens.xml b/packages/SystemUI/res/values-land/dimens.xml
index 235015b..70ae5c1 100644
--- a/packages/SystemUI/res/values-land/dimens.xml
+++ b/packages/SystemUI/res/values-land/dimens.xml
@@ -91,7 +91,6 @@
<dimen name="notification_blocker_channel_list_height">128dp</dimen>
<dimen name="keyguard_indication_margin_bottom">8dp</dimen>
- <dimen name="lock_icon_margin_bottom">24dp</dimen>
<!-- Keyboard shortcuts helper -->
<dimen name="ksh_container_horizontal_margin">48dp</dimen>
diff --git a/packages/SystemUI/res/values-sw600dp-land/dimens.xml b/packages/SystemUI/res/values-sw600dp-land/dimens.xml
index 4a53df9..055c3a6 100644
--- a/packages/SystemUI/res/values-sw600dp-land/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp-land/dimens.xml
@@ -20,7 +20,6 @@
<!-- keyguard-->
<dimen name="keyguard_indication_margin_bottom">25dp</dimen>
<dimen name="ambient_indication_margin_bottom">115dp</dimen>
- <dimen name="lock_icon_margin_bottom">60dp</dimen>
<!-- margin from keyguard status bar to clock. For split shade it should be
keyguard_split_shade_top_margin - status_bar_header_height_keyguard = 8dp -->
<dimen name="keyguard_clock_top_margin">8dp</dimen>
@@ -46,8 +45,6 @@
<dimen name="lockscreen_shade_max_over_scroll_amount">32dp</dimen>
- <dimen name="status_view_margin_horizontal">8dp</dimen>
-
<!-- Lockscreen shade transition values -->
<dimen name="lockscreen_shade_transition_by_tap_distance">200dp</dimen>
<dimen name="lockscreen_shade_full_transition_distance">200dp</dimen>
diff --git a/packages/SystemUI/res/values-sw600dp-port/dimens.xml b/packages/SystemUI/res/values-sw600dp-port/dimens.xml
index 707bc9e..f73e91a 100644
--- a/packages/SystemUI/res/values-sw600dp-port/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp-port/dimens.xml
@@ -16,8 +16,6 @@
-->
<resources>
<dimen name="notification_panel_margin_horizontal">48dp</dimen>
- <dimen name="status_view_margin_horizontal">62dp</dimen>
-
<!-- qs_tiles_page_horizontal_margin should be margin / 2, otherwise full space between two
pages is margin * 2, and that makes tiles page not appear immediately after user swipes to
the side -->
diff --git a/packages/SystemUI/res/values-sw720dp-land/dimens.xml b/packages/SystemUI/res/values-sw720dp-land/dimens.xml
index 8583f05..41bb37e 100644
--- a/packages/SystemUI/res/values-sw720dp-land/dimens.xml
+++ b/packages/SystemUI/res/values-sw720dp-land/dimens.xml
@@ -22,8 +22,6 @@
<dimen name="keyguard_split_shade_top_margin">72dp</dimen>
- <dimen name="status_view_margin_horizontal">24dp</dimen>
-
<dimen name="qs_media_session_height_expanded">184dp</dimen>
<dimen name="qs_content_horizontal_padding">40dp</dimen>
<dimen name="qs_horizontal_margin">40dp</dimen>
diff --git a/packages/SystemUI/res/values-sw720dp-port/dimens.xml b/packages/SystemUI/res/values-sw720dp-port/dimens.xml
index 9248d58..eb570b8 100644
--- a/packages/SystemUI/res/values-sw720dp-port/dimens.xml
+++ b/packages/SystemUI/res/values-sw720dp-port/dimens.xml
@@ -20,8 +20,6 @@
<!-- These resources are around just to allow their values to be customized
for different hardware and product builds. -->
<resources>
- <dimen name="status_view_margin_horizontal">124dp</dimen>
-
<dimen name="large_screen_shade_header_left_padding">24dp</dimen>
<dimen name="qqs_layout_padding_bottom">40dp</dimen>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 7af0057..8cab155 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -788,6 +788,18 @@
<!-- Size of an avatar shown on one-line (children of a group) conversation notifications -->
<dimen name="conversation_single_line_avatar_size">24dp</dimen>
+ <!-- Size of the face pile shown on one-line (children of a group) conversation notifications
+ (2025 redesign version) -->
+ <dimen name="notification_2025_single_line_face_pile_size">16dp</dimen>
+
+ <!-- Size of the avatars within a face pile shown on one-line (children of a group) conversation
+ notifications (2025 redesign version) -->
+ <dimen name="notification_2025_single_line_face_pile_avatar_size">11dp</dimen>
+
+ <!-- Size of an avatar shown on one-line (children of a group) conversation notifications
+ (2025 redesign version) -->
+ <dimen name="notification_2025_single_line_avatar_size">16dp</dimen>
+
<!-- Border width for avatars in the face pile shown on one-line (children of a group) conversation notifications -->
<dimen name="conversation_single_line_face_pile_protection_width">1dp</dimen>
@@ -936,7 +948,6 @@
<dimen name="keyguard_translate_distance_on_swipe_up">-200dp</dimen>
<dimen name="keyguard_indication_margin_bottom">32dp</dimen>
- <dimen name="lock_icon_margin_bottom">74dp</dimen>
<dimen name="ambient_indication_margin_bottom">71dp</dimen>
@@ -1749,6 +1760,7 @@
<dimen name="ongoing_activity_chip_icon_text_padding">4dp</dimen>
<!-- The end padding for the timer text view. Only used if an embedded padding icon is used. -->
<dimen name="ongoing_activity_chip_text_end_padding_for_embedded_padding_icon">6dp</dimen>
+ <dimen name="ongoing_activity_chip_text_fading_edge_length">12dp</dimen>
<dimen name="ongoing_activity_chip_corner_radius">28dp</dimen>
<!-- Status bar user chip -->
@@ -1955,8 +1967,6 @@
<dimen name="dream_overlay_entry_y_offset">40dp</dimen>
<dimen name="dream_overlay_exit_y_offset">40dp</dimen>
- <dimen name="status_view_margin_horizontal">0dp</dimen>
-
<!-- Media output broadcast dialog QR code picture size -->
<dimen name="media_output_qrcode_size">216dp</dimen>
<dimen name="media_output_broadcast_info">21dp</dimen>
@@ -2046,6 +2056,9 @@
<!-- UDFPS view attributes -->
<!-- UDFPS icon size in microns/um -->
<dimen name="udfps_icon_size" format="float">6000</dimen>
+ <!-- Limits the updates to at most one update per debounce duration to avoid too many
+ updates due to quick changes to padding. -->
+ <integer name="udfps_padding_debounce_duration">100</integer>
<!-- Microns/ums (1000 um = 1mm) per pixel for the given device. If unspecified, UI that
relies on this value will not be sized correctly. -->
<item name="pixel_pitch" format="float" type="dimen">-1</item>
@@ -2060,26 +2073,29 @@
<dimen name="contextual_edu_dialog_elevation">2dp</dimen>
<!-- Volume start -->
- <dimen name="volume_dialog_background_corner_radius">30dp</dimen>
<dimen name="volume_dialog_width">60dp</dimen>
- <dimen name="volume_dialog_vertical_padding">10dp</dimen>
+
+ <dimen name="volume_dialog_background_corner_radius">30dp</dimen>
+ <dimen name="volume_dialog_background_vertical_margin">-10dp</dimen>
+
<dimen name="volume_dialog_components_spacing">8dp</dimen>
<dimen name="volume_dialog_floating_sliders_spacing">8dp</dimen>
<dimen name="volume_dialog_floating_sliders_vertical_padding">10dp</dimen>
+ <dimen name="volume_dialog_floating_sliders_vertical_padding_negative">-10dp</dimen>
<dimen name="volume_dialog_floating_sliders_horizontal_padding">4dp</dimen>
- <dimen name="volume_dialog_spacing">4dp</dimen>
<dimen name="volume_dialog_button_size">48dp</dimen>
- <dimen name="volume_dialog_floating_sliders_bottom_padding">48dp</dimen>
<dimen name="volume_dialog_slider_width">52dp</dimen>
<dimen name="volume_dialog_slider_height">254dp</dimen>
- <dimen name="volume_panel_slice_vertical_padding">8dp</dimen>
- <dimen name="volume_panel_slice_horizontal_padding">24dp</dimen>
+ <fraction name="volume_dialog_half_opened_bias">0.2</fraction>
- <dimen name="volume_dialog_ringer_horizontal_padding">10dp</dimen>
+ <dimen name="volume_dialog_background_square_corner_radius">12dp</dimen>
+
<dimen name="volume_dialog_ringer_drawer_button_size">40dp</dimen>
<dimen name="volume_dialog_ringer_drawer_button_icon_radius">10dp</dimen>
- <dimen name="volume_dialog_background_square_corner_radius">12dp</dimen>
<dimen name="volume_dialog_ringer_selected_button_background_radius">20dp</dimen>
+
+ <dimen name="volume_panel_slice_vertical_padding">8dp</dimen>
+ <dimen name="volume_panel_slice_horizontal_padding">24dp</dimen>
<!-- Volume end -->
</resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index b45aadd..c3f4222 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -3214,6 +3214,9 @@
<!-- Text to display when copying the build number off QS [CHAR LIMIT=NONE]-->
<string name="build_number_copy_toast">Build number copied to clipboard.</string>
+ <!-- Text for accessibility action for copying content to clipboard [CHAR LIMIT=NONE]-->
+ <string name="copy_to_clipboard_a11y_action">copy to clipboard.</string>
+
<!-- Status for conversation without interaction data [CHAR LIMIT=120] -->
<string name="basic_status">Open conversation</string>
<!--Title text for Conversation widget set up screen [CHAR LIMIT=180] -->
@@ -3568,6 +3571,8 @@
<string name="rear_display_accessibility_folded_animation">Foldable device being unfolded</string>
<!-- Text for education page content description for unfolded animation. [CHAR_LIMIT=NONE] -->
<string name="rear_display_accessibility_unfolded_animation">Foldable device being flipped around</string>
+ <!-- Text for a dialog telling the user that the front screen is turned on. [CHAR_LIMIT=NONE] -->
+ <string name="rear_display_unfolded_front_screen_on">Front screen turned on</string>
<!-- QuickSettings: Additional label for the auto-rotation quicksettings tile indicating that the setting corresponds to the folded posture for a foldable device [CHAR LIMIT=32] -->
<string name="quick_settings_rotation_posture_folded">folded</string>
diff --git a/packages/SystemUI/res/xml/volume_dialog_constraint_set.xml b/packages/SystemUI/res/xml/volume_dialog_constraint_set.xml
new file mode 100644
index 0000000..9018e5b
--- /dev/null
+++ b/packages/SystemUI/res/xml/volume_dialog_constraint_set.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<ConstraintSet xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/volume_dialog_constraint_set">
+
+ <Constraint
+ android:id="@id/volume_dialog_main_slider_container"
+ android:layout_width="@dimen/volume_dialog_slider_width"
+ android:layout_height="@dimen/volume_dialog_slider_height"
+ android:layout_marginEnd="@dimen/volume_dialog_components_spacing"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintVertical_bias="0.5" />
+</ConstraintSet>
\ No newline at end of file
diff --git a/packages/SystemUI/res/xml/volume_dialog_half_folded_constraint_set.xml b/packages/SystemUI/res/xml/volume_dialog_half_folded_constraint_set.xml
new file mode 100644
index 0000000..297c388
--- /dev/null
+++ b/packages/SystemUI/res/xml/volume_dialog_half_folded_constraint_set.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<ConstraintSet xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/volume_dialog_half_folded_constraint_set">
+
+ <Constraint
+ android:id="@id/volume_dialog_main_slider_container"
+ android:layout_width="@dimen/volume_dialog_slider_width"
+ android:layout_height="@dimen/volume_dialog_slider_height"
+ android:layout_marginEnd="@dimen/volume_dialog_components_spacing"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintVertical_bias="@fraction/volume_dialog_half_opened_bias" />
+</ConstraintSet>
\ No newline at end of file
diff --git a/packages/SystemUI/res/xml/volume_dialog_scene.xml b/packages/SystemUI/res/xml/volume_dialog_scene.xml
new file mode 100644
index 0000000..b813474
--- /dev/null
+++ b/packages/SystemUI/res/xml/volume_dialog_scene.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2024 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<MotionScene xmlns:motion="http://schemas.android.com/apk/res-auto">
+
+ <Transition
+ motion:autoTransition="none"
+ motion:constraintSetEnd="@id/volume_dialog_half_folded_constraint_set"
+ motion:constraintSetStart="@id/volume_dialog_constraint_set"
+ motion:duration="150" />
+
+ <Include motion:constraintSet="@xml/volume_dialog_constraint_set" />
+ <Include motion:constraintSet="@xml/volume_dialog_half_folded_constraint_set" />
+</MotionScene>
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
index 95830b5..add459b 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
@@ -44,7 +44,7 @@
import com.android.systemui.navigationbar.NavigationBarController;
import com.android.systemui.navigationbar.views.NavigationBarView;
import com.android.systemui.settings.DisplayTracker;
-import com.android.systemui.shade.data.repository.ShadePositionRepository;
+import com.android.systemui.shade.data.repository.ShadeDisplaysRepository;
import com.android.systemui.shade.shared.flag.ShadeWindowGoesAround;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -66,7 +66,7 @@
private final DisplayManager mDisplayService;
private final DisplayTracker mDisplayTracker;
private final Lazy<NavigationBarController> mNavigationBarControllerLazy;
- private final Provider<ShadePositionRepository> mShadePositionRepositoryProvider;
+ private final Provider<ShadeDisplaysRepository> mShadePositionRepositoryProvider;
private final ConnectedDisplayKeyguardPresentation.Factory
mConnectedDisplayKeyguardPresentationFactory;
private final Context mContext;
@@ -112,7 +112,7 @@
KeyguardStateController keyguardStateController,
ConnectedDisplayKeyguardPresentation.Factory
connectedDisplayKeyguardPresentationFactory,
- Provider<ShadePositionRepository> shadePositionRepositoryProvider,
+ Provider<ShadeDisplaysRepository> shadePositionRepositoryProvider,
@Application CoroutineScope appScope) {
mContext = context;
mNavigationBarControllerLazy = navigationBarControllerLazy;
diff --git a/packages/SystemUI/src/com/android/systemui/ambient/touch/TouchMonitor.java b/packages/SystemUI/src/com/android/systemui/ambient/touch/TouchMonitor.java
index fb00d6e..db4d613 100644
--- a/packages/SystemUI/src/com/android/systemui/ambient/touch/TouchMonitor.java
+++ b/packages/SystemUI/src/com/android/systemui/ambient/touch/TouchMonitor.java
@@ -52,6 +52,8 @@
import com.google.common.util.concurrent.ListenableFuture;
+import kotlin.Unit;
+
import kotlinx.coroutines.Job;
import java.util.Collection;
@@ -87,7 +89,7 @@
private final ConfigurationInteractor mConfigurationInteractor;
private final Lifecycle mLifecycle;
- private Rect mExclusionRect = null;
+ private Rect mExclusionRect = new Rect();
private ISystemGestureExclusionListener mGestureExclusionListener;
@@ -298,9 +300,18 @@
public void onSystemGestureExclusionChanged(int displayId,
Region systemGestureExclusion,
Region systemGestureExclusionUnrestricted) {
- mExclusionRect = systemGestureExclusion.getBounds();
+ final Rect bounds = systemGestureExclusion.getBounds();
+ if (!mExclusionRect.equals(bounds)) {
+ mExclusionRect = bounds;
+ mLogger.i(msg -> "Exclusion rect updated to " + msg.getStr1(),
+ msg -> {
+ msg.setStr1(bounds.toString());
+ return Unit.INSTANCE;
+ });
+ }
}
};
+ mLogger.i("Registering system gesture exclusion listener");
mWindowManagerService.registerSystemGestureExclusionListener(
mGestureExclusionListener, mDisplayId);
} catch (RemoteException e) {
@@ -320,11 +331,12 @@
* Destroys any active {@link InputSession}.
*/
private void stopMonitoring(boolean force) {
- mExclusionRect = null;
+ mExclusionRect = new Rect();
if (bouncerAreaExclusion()) {
mBackgroundExecutor.execute(() -> {
try {
if (mGestureExclusionListener != null) {
+ mLogger.i("Unregistering system gesture exclusion listener");
mWindowManagerService.unregisterSystemGestureExclusionListener(
mGestureExclusionListener, mDisplayId);
mGestureExclusionListener = null;
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractor.kt
index abbbd73..9763295 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractor.kt
@@ -36,8 +36,10 @@
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
+import kotlin.math.max
/** Encapsulates business logic for interacting with the UDFPS overlay. */
@SysUISingleton
@@ -124,8 +126,9 @@
udfpsOverlayParams.map { params ->
val sensorWidth = params.nativeSensorBounds.right - params.nativeSensorBounds.left
val nativePadding = (sensorWidth - iconSize) / 2
- (nativePadding * params.scaleFactor).toInt()
- }
+ // padding can be negative when udfpsOverlayParams has not been initialized yet.
+ max(0, (nativePadding * params.scaleFactor).toInt())
+ }.distinctUntilChanged()
companion object {
private const val TAG = "UdfpsOverlayInteractor"
diff --git a/packages/SystemUI/src/com/android/systemui/brightness/ui/compose/BrightnessSlider.kt b/packages/SystemUI/src/com/android/systemui/brightness/ui/compose/BrightnessSlider.kt
index 917a4ff..ccd953d 100644
--- a/packages/SystemUI/src/com/android/systemui/brightness/ui/compose/BrightnessSlider.kt
+++ b/packages/SystemUI/src/com/android/systemui/brightness/ui/compose/BrightnessSlider.kt
@@ -24,6 +24,7 @@
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.shape.CornerSize
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
@@ -31,7 +32,6 @@
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
-import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
@@ -61,6 +61,7 @@
import com.android.systemui.haptics.slider.SliderHapticFeedbackConfig
import com.android.systemui.haptics.slider.compose.ui.SliderHapticsViewModel
import com.android.systemui.lifecycle.rememberViewModel
+import com.android.systemui.qs.ui.compose.borderOnFocus
import com.android.systemui.res.R
import com.android.systemui.utils.PolicyRestriction
@@ -102,11 +103,12 @@
null
}
- val overriddenByAppState by if (Flags.showToastWhenAppControlBrightness()) {
- viewModel.brightnessOverriddenByWindow.collectAsStateWithLifecycle()
- } else {
- mutableStateOf(false)
- }
+ val overriddenByAppState =
+ if (Flags.showToastWhenAppControlBrightness()) {
+ viewModel.brightnessOverriddenByWindow.collectAsStateWithLifecycle().value
+ } else {
+ false
+ }
PlatformSlider(
value = animatedValue,
@@ -160,7 +162,7 @@
if (interaction is DragInteraction.Start && overriddenByAppState) {
viewModel.showToast(
context,
- R.string.quick_settings_brightness_unable_adjust_msg
+ R.string.quick_settings_brightness_unable_adjust_msg,
)
}
}
@@ -213,7 +215,11 @@
coroutineScope.launch { viewModel.onDrag(Drag.Stopped(GammaBrightness(it))) }
},
modifier =
- Modifier.then(if (viewModel.showMirror) Modifier.drawInOverlay() else Modifier)
+ Modifier.borderOnFocus(
+ color = MaterialTheme.colorScheme.secondary,
+ cornerSize = CornerSize(32.dp),
+ )
+ .then(if (viewModel.showMirror) Modifier.drawInOverlay() else Modifier)
.sliderBackground(containerColor)
.fillMaxWidth(),
formatter = viewModel::formatValue,
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt
index 4e0e112..5ecf2e6b 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt
@@ -29,6 +29,7 @@
import com.android.systemui.communal.shared.model.EditModeState
import com.android.systemui.communal.widgets.WidgetConfigurator
import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.media.controls.ui.controller.MediaCarouselController
import com.android.systemui.media.controls.ui.view.MediaHost
import com.android.systemui.util.kotlin.BooleanFlowOperators.anyOf
import com.android.systemui.util.kotlin.BooleanFlowOperators.not
@@ -43,6 +44,7 @@
val communalSceneInteractor: CommunalSceneInteractor,
private val communalInteractor: CommunalInteractor,
val mediaHost: MediaHost,
+ val mediaCarouselController: MediaCarouselController,
) {
val currentScene: Flow<SceneKey> = communalSceneInteractor.currentScene
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt
index ccff230..736ed5c 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt
@@ -46,6 +46,7 @@
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.core.Logger
import com.android.systemui.log.dagger.CommunalLog
+import com.android.systemui.media.controls.ui.controller.MediaCarouselController
import com.android.systemui.media.controls.ui.view.MediaHost
import com.android.systemui.media.dagger.MediaModule
import com.android.systemui.res.R
@@ -82,7 +83,14 @@
private val accessibilityManager: AccessibilityManager,
private val packageManager: PackageManager,
@Named(LAUNCHER_PACKAGE) private val launcherPackage: String,
-) : BaseCommunalViewModel(communalSceneInteractor, communalInteractor, mediaHost) {
+ mediaCarouselController: MediaCarouselController,
+) :
+ BaseCommunalViewModel(
+ communalSceneInteractor,
+ communalInteractor,
+ mediaHost,
+ mediaCarouselController,
+ ) {
private val logger = Logger(logBuffer, "CommunalEditModeViewModel")
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalTransitionViewModel.kt
index 6239373..3496230 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalTransitionViewModel.kt
@@ -31,6 +31,7 @@
import com.android.systemui.keyguard.ui.viewmodel.GlanceableHubToDreamingTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.GlanceableHubToLockscreenTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.LockscreenToGlanceableHubTransitionViewModel
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.util.kotlin.BooleanFlowOperators.allOf
import com.android.systemui.util.kotlin.BooleanFlowOperators.anyOf
@@ -41,6 +42,7 @@
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.merge
import kotlinx.coroutines.flow.onStart
@@ -60,7 +62,7 @@
glanceableHubToDreamTransitionViewModel: GlanceableHubToDreamingTransitionViewModel,
communalInteractor: CommunalInteractor,
private val communalSceneInteractor: CommunalSceneInteractor,
- keyguardTransitionInteractor: KeyguardTransitionInteractor
+ keyguardTransitionInteractor: KeyguardTransitionInteractor,
) {
/**
* Snaps to [CommunalScenes.Communal], showing the glanceable hub immediately without any
@@ -89,7 +91,7 @@
keyguardTransitionInteractor
.transition(
edge = Edge.create(from = Scenes.Communal),
- edgeWithoutSceneContainer = Edge.create(from = KeyguardState.GLANCEABLE_HUB)
+ edgeWithoutSceneContainer = Edge.create(from = KeyguardState.GLANCEABLE_HUB),
)
.filter {
it.to == KeyguardState.OCCLUDED &&
@@ -114,21 +116,24 @@
// from
// the hub, then pressing power twice to go back to the lock screen.
communalSceneInteractor.isCommunalVisible,
- merge(
- lockscreenToGlanceableHubTransitionViewModel.showUmo,
- glanceableHubToLockscreenTransitionViewModel.showUmo,
- dreamToGlanceableHubTransitionViewModel.showUmo,
- glanceableHubToDreamTransitionViewModel.showUmo,
- showUmoFromOccludedToGlanceableHub,
- showUmoFromGlanceableHubToOccluded,
- )
- .onStart { emit(false) }
- )
+ // TODO(b/378942852): polish UMO transitions when scene container is enabled
+ if (SceneContainerFlag.isEnabled) flowOf(true)
+ else
+ merge(
+ lockscreenToGlanceableHubTransitionViewModel.showUmo,
+ glanceableHubToLockscreenTransitionViewModel.showUmo,
+ dreamToGlanceableHubTransitionViewModel.showUmo,
+ glanceableHubToDreamTransitionViewModel.showUmo,
+ showUmoFromOccludedToGlanceableHub,
+ showUmoFromGlanceableHubToOccluded,
+ )
+ .onStart { emit(false) },
+ ),
)
.stateIn(
scope = applicationScope,
started = SharingStarted.WhileSubscribed(),
- initialValue = false
+ initialValue = false,
)
/** Whether to show communal when exiting the occluded state. */
@@ -145,8 +150,7 @@
val recentsBackgroundColor: Flow<Color?> =
combine(showCommunalFromOccluded, communalColors.backgroundColor) {
showCommunalFromOccluded,
- backgroundColor,
- ->
+ backgroundColor ->
if (showCommunalFromOccluded) {
backgroundColor
} else {
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
index 7990450..9cd6465 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
@@ -21,6 +21,7 @@
import android.os.Bundle
import android.view.View
import android.view.accessibility.AccessibilityNodeInfo
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.communal.domain.interactor.CommunalInteractor
import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
@@ -38,6 +39,7 @@
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.core.Logger
import com.android.systemui.log.dagger.CommunalLog
+import com.android.systemui.media.controls.ui.controller.MediaCarouselController
import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager
import com.android.systemui.media.controls.ui.view.MediaHost
import com.android.systemui.media.controls.ui.view.MediaHostState
@@ -72,7 +74,6 @@
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.stateIn
-import com.android.app.tracing.coroutines.launchTraced as launch
/** The default view model used for showing the communal hub. */
@OptIn(ExperimentalCoroutinesApi::class)
@@ -95,7 +96,14 @@
@Named(MediaModule.COMMUNAL_HUB) mediaHost: MediaHost,
@CommunalLog logBuffer: LogBuffer,
private val metricsLogger: CommunalMetricsLogger,
-) : BaseCommunalViewModel(communalSceneInteractor, communalInteractor, mediaHost) {
+ mediaCarouselController: MediaCarouselController,
+) :
+ BaseCommunalViewModel(
+ communalSceneInteractor,
+ communalInteractor,
+ mediaHost,
+ mediaCarouselController,
+ ) {
private val logger = Logger(logBuffer, "CommunalViewModel")
diff --git a/packages/SystemUI/src/com/android/systemui/development/data/repository/DevelopmentSettingRepository.kt b/packages/SystemUI/src/com/android/systemui/development/data/repository/DevelopmentSettingRepository.kt
new file mode 100644
index 0000000..a8fa979
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/development/data/repository/DevelopmentSettingRepository.kt
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.development.data.repository
+
+import android.content.pm.UserInfo
+import android.os.Build
+import android.os.UserManager
+import android.provider.Settings
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.util.kotlin.emitOnStart
+import com.android.systemui.util.settings.GlobalSettings
+import com.android.systemui.util.settings.SettingsProxyExt.observerFlow
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.withContext
+
+@SysUISingleton
+class DevelopmentSettingRepository
+@Inject
+constructor(
+ private val globalSettings: GlobalSettings,
+ private val userManager: UserManager,
+ @Background private val backgroundDispatcher: CoroutineDispatcher,
+) {
+ private val settingFlow = globalSettings.observerFlow(SETTING)
+
+ /**
+ * Indicates whether development settings is enabled for this user. The conditions are:
+ * * Setting is enabled (defaults to true in eng builds)
+ * * User is an admin
+ * * User is not restricted from Debugging features.
+ */
+ fun isDevelopmentSettingEnabled(userInfo: UserInfo): Flow<Boolean> {
+ return settingFlow
+ .emitOnStart()
+ .map { checkDevelopmentSettingEnabled(userInfo) }
+ .flowOn(backgroundDispatcher)
+ }
+
+ private suspend fun checkDevelopmentSettingEnabled(userInfo: UserInfo): Boolean {
+ val hasUserRestriction =
+ withContext(backgroundDispatcher) {
+ userManager.hasUserRestrictionForUser(
+ UserManager.DISALLOW_DEBUGGING_FEATURES,
+ userInfo.userHandle,
+ )
+ }
+ val isSettingEnabled =
+ withContext(backgroundDispatcher) {
+ globalSettings.getInt(SETTING, DEFAULT_ENABLED) != 0
+ }
+ val isAdmin = userInfo.isAdmin
+ return isAdmin && !hasUserRestriction && isSettingEnabled
+ }
+
+ private companion object {
+ val DEFAULT_ENABLED = if (Build.TYPE == "eng") 1 else 0
+
+ const val SETTING = Settings.Global.DEVELOPMENT_SETTINGS_ENABLED
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/development/domain/interactor/BuildNumberInteractor.kt b/packages/SystemUI/src/com/android/systemui/development/domain/interactor/BuildNumberInteractor.kt
new file mode 100644
index 0000000..4d786fe
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/development/domain/interactor/BuildNumberInteractor.kt
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.development.domain.interactor
+
+import android.content.ClipData
+import android.content.ClipboardManager
+import android.content.res.Resources
+import android.os.Build
+import android.os.UserHandle
+import com.android.internal.R as InternalR
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.development.data.repository.DevelopmentSettingRepository
+import com.android.systemui.development.shared.model.BuildNumber
+import com.android.systemui.res.R as SystemUIR
+import com.android.systemui.user.data.repository.UserRepository
+import com.android.systemui.user.utils.UserScopedService
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.flatMapConcat
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.withContext
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SysUISingleton
+class BuildNumberInteractor
+@Inject
+constructor(
+ repository: DevelopmentSettingRepository,
+ @Main resources: Resources,
+ private val userRepository: UserRepository,
+ private val clipboardManagerProvider: UserScopedService<ClipboardManager>,
+ @Background private val backgroundDispatcher: CoroutineDispatcher,
+) {
+
+ /**
+ * Build number, or `null` if Development Settings is not enabled for the current user.
+ *
+ * @see DevelopmentSettingRepository.isDevelopmentSettingEnabled
+ */
+ val buildNumber: Flow<BuildNumber?> =
+ userRepository.selectedUserInfo
+ .flatMapConcat { userInfo -> repository.isDevelopmentSettingEnabled(userInfo) }
+ .map { enabled -> buildText.takeIf { enabled } }
+
+ private val buildText =
+ BuildNumber(
+ resources.getString(
+ InternalR.string.bugreport_status,
+ Build.VERSION.RELEASE_OR_CODENAME,
+ Build.ID,
+ )
+ )
+
+ private val clipLabel = resources.getString(SystemUIR.string.build_number_clip_data_label)
+
+ private val currentUserHandle: UserHandle
+ get() = userRepository.getSelectedUserInfo().userHandle
+
+ /**
+ * Copy to the clipboard the build number for the current user.
+ *
+ * This can be performed regardless of the current user having Development Settings enabled
+ */
+ suspend fun copyBuildNumber() {
+ withContext(backgroundDispatcher) {
+ clipboardManagerProvider
+ .forUser(currentUserHandle)
+ .setPrimaryClip(ClipData.newPlainText(clipLabel, buildText.value))
+ }
+ }
+}
diff --git a/core/java/android/text/ClientFlags.java b/packages/SystemUI/src/com/android/systemui/development/shared/model/BuildNumber.kt
similarity index 60%
copy from core/java/android/text/ClientFlags.java
copy to packages/SystemUI/src/com/android/systemui/development/shared/model/BuildNumber.kt
index ca88764..5bd713f 100644
--- a/core/java/android/text/ClientFlags.java
+++ b/packages/SystemUI/src/com/android/systemui/development/shared/model/BuildNumber.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,16 +14,6 @@
* limitations under the License.
*/
-package android.text;
+package com.android.systemui.development.shared.model
-/**
- * An aconfig feature flags that can be accessible from application process without
- * ContentProvider IPCs.
- *
- * When you add new flags, you have to add flag string to {@link TextFlags#TEXT_ACONFIGS_FLAGS}.
- *
- * TODO(nona): Remove this class.
- * @hide
- */
-public class ClientFlags {
-}
+@JvmInline value class BuildNumber(val value: String)
diff --git a/packages/SystemUI/src/com/android/systemui/development/ui/compose/BuildNumber.kt b/packages/SystemUI/src/com/android/systemui/development/ui/compose/BuildNumber.kt
new file mode 100644
index 0000000..72e1ced
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/development/ui/compose/BuildNumber.kt
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.development.ui.compose
+
+import androidx.compose.foundation.basicMarquee
+import androidx.compose.foundation.focusable
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.wrapContentWidth
+import androidx.compose.material3.Text
+import androidx.compose.material3.minimumInteractiveComponentSize
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.hapticfeedback.HapticFeedbackType
+import androidx.compose.ui.input.pointer.pointerInput
+import androidx.compose.ui.platform.LocalHapticFeedback
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.semantics.onLongClick
+import androidx.compose.ui.semantics.semantics
+import com.android.systemui.communal.ui.compose.extensions.detectLongPressGesture
+import com.android.systemui.development.ui.viewmodel.BuildNumberViewModel
+import com.android.systemui.lifecycle.rememberViewModel
+import com.android.systemui.res.R
+
+@Composable
+fun BuildNumber(
+ viewModelFactory: BuildNumberViewModel.Factory,
+ textColor: Color,
+ modifier: Modifier = Modifier,
+) {
+ val viewModel = rememberViewModel(traceName = "BuildNumber") { viewModelFactory.create() }
+
+ val buildNumber = viewModel.buildNumber
+
+ if (buildNumber != null) {
+ val haptics = LocalHapticFeedback.current
+ val copyToClipboardActionLabel = stringResource(id = R.string.copy_to_clipboard_a11y_action)
+
+ Text(
+ text = buildNumber.value,
+ modifier =
+ modifier
+ .focusable()
+ .wrapContentWidth()
+ // Using this instead of combinedClickable because this node should not support
+ // single click
+ .pointerInput(Unit) {
+ detectLongPressGesture {
+ haptics.performHapticFeedback(HapticFeedbackType.LongPress)
+ viewModel.onBuildNumberLongPress()
+ }
+ }
+ .semantics {
+ onLongClick(copyToClipboardActionLabel) {
+ viewModel.onBuildNumberLongPress()
+ true
+ }
+ }
+ .basicMarquee(iterations = 1, initialDelayMillis = 2000)
+ .minimumInteractiveComponentSize(),
+ color = textColor,
+ maxLines = 1,
+ )
+ } else {
+ Spacer(modifier)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/development/ui/viewmodel/BuildNumberViewModel.kt b/packages/SystemUI/src/com/android/systemui/development/ui/viewmodel/BuildNumberViewModel.kt
new file mode 100644
index 0000000..68c51ea
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/development/ui/viewmodel/BuildNumberViewModel.kt
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.development.ui.viewmodel
+
+import androidx.compose.runtime.getValue
+import com.android.systemui.development.domain.interactor.BuildNumberInteractor
+import com.android.systemui.development.shared.model.BuildNumber
+import com.android.systemui.lifecycle.ExclusiveActivatable
+import com.android.systemui.lifecycle.Hydrator
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import kotlinx.coroutines.awaitCancellation
+import kotlinx.coroutines.channels.Channel
+import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.flow.receiveAsFlow
+import kotlinx.coroutines.launch
+
+/** View model for UI that (optionally) shows the build number and copies it on long press. */
+class BuildNumberViewModel
+@AssistedInject
+constructor(private val buildNumberInteractor: BuildNumberInteractor) : ExclusiveActivatable() {
+
+ private val hydrator = Hydrator("BuildNumberViewModel")
+
+ private val copyRequests = Channel<Unit>()
+
+ val buildNumber: BuildNumber? by
+ hydrator.hydratedStateOf(
+ traceName = "buildNumber",
+ initialValue = null,
+ source = buildNumberInteractor.buildNumber,
+ )
+
+ fun onBuildNumberLongPress() {
+ copyRequests.trySend(Unit)
+ }
+
+ override suspend fun onActivated(): Nothing {
+ coroutineScope {
+ launch { hydrator.activate() }
+ launch {
+ copyRequests.receiveAsFlow().collect { buildNumberInteractor.copyBuildNumber() }
+ }
+ awaitCancellation()
+ }
+ }
+
+ @AssistedFactory
+ interface Factory {
+ fun create(): BuildNumberViewModel
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt
index 1a56541..400d097 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt
@@ -16,6 +16,7 @@
package com.android.systemui.deviceentry.domain.interactor
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.internal.policy.IKeyguardDismissCallback
import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
@@ -43,7 +44,6 @@
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.stateIn
-import com.android.app.tracing.coroutines.launchTraced as launch
/**
* Hosts application business logic related to device entry.
@@ -174,6 +174,14 @@
}
/**
+ * Whether lockscreen bypass is enabled. When enabled, the lockscreen will be automatically
+ * dismissed once the authentication challenge is completed. For example, completing a biometric
+ * authentication challenge via face unlock or fingerprint sensor can automatically bypass the
+ * lockscreen.
+ */
+ val isBypassEnabled: StateFlow<Boolean> = repository.isBypassEnabled
+
+ /**
* Attempt to enter the device and dismiss the lockscreen. If authentication is required to
* unlock the device it will transition to bouncer.
*
@@ -238,11 +246,8 @@
isLockscreenEnabled()
}
- /**
- * Whether lockscreen bypass is enabled. When enabled, the lockscreen will be automatically
- * dismissed once the authentication challenge is completed. For example, completing a biometric
- * authentication challenge via face unlock or fingerprint sensor can automatically bypass the
- * lockscreen.
- */
- val isBypassEnabled: StateFlow<Boolean> = repository.isBypassEnabled
+ /** Locks the device instantly. */
+ fun lockNow() {
+ deviceUnlockedInteractor.lockNow()
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractor.kt
index b74ca03..7d684ca 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractor.kt
@@ -35,6 +35,7 @@
import com.android.systemui.lifecycle.ExclusiveActivatable
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.power.shared.model.WakeSleepReason
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.util.settings.repository.UserAwareSecureSettingsRepository
import com.android.systemui.utils.coroutines.flow.flatMapLatestConflated
import javax.inject.Inject
@@ -42,6 +43,7 @@
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.awaitCancellation
+import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
@@ -56,6 +58,7 @@
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.merge
+import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.launch
@OptIn(ExperimentalCoroutinesApi::class)
@@ -177,6 +180,8 @@
val deviceUnlockStatus: StateFlow<DeviceUnlockStatus> =
repository.deviceUnlockStatus.asStateFlow()
+ private val lockNowRequests = Channel<Unit>()
+
override suspend fun onActivated(): Nothing {
authenticationInteractor.authenticationMethod.collectLatest { authMethod ->
if (!authMethod.isSecure) {
@@ -195,6 +200,11 @@
awaitCancellation()
}
+ /** Locks the device instantly. */
+ fun lockNow() {
+ lockNowRequests.trySend(Unit)
+ }
+
private suspend fun handleLockAndUnlockEvents() {
try {
Log.d(TAG, "started watching for lock and unlock events")
@@ -224,10 +234,12 @@
.map { (isAsleep, lastSleepReason) ->
if (isAsleep) {
if (
- lastSleepReason == WakeSleepReason.POWER_BUTTON &&
+ (lastSleepReason == WakeSleepReason.POWER_BUTTON) &&
authenticationInteractor.getPowerButtonInstantlyLocks()
) {
LockImmediately("locked instantly from power button")
+ } else if (lastSleepReason == WakeSleepReason.SLEEP_BUTTON) {
+ LockImmediately("locked instantly from sleep button")
} else {
LockWithDelay("entering sleep")
}
@@ -255,6 +267,7 @@
emptyFlow()
}
},
+ lockNowRequests.receiveAsFlow().map { LockImmediately("lockNow") },
)
.collectLatest(::onLockEvent)
}
@@ -363,6 +376,9 @@
private val interactor: DeviceUnlockedInteractor,
) : CoreStartable {
override fun start() {
+ if (!SceneContainerFlag.isEnabled)
+ return
+
applicationScope.launch { interactor.activate() }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/display/DisplayModule.kt b/packages/SystemUI/src/com/android/systemui/display/DisplayModule.kt
index 589dbf9..e862525 100644
--- a/packages/SystemUI/src/com/android/systemui/display/DisplayModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/display/DisplayModule.kt
@@ -30,6 +30,8 @@
import com.android.systemui.display.data.repository.FocusedDisplayRepositoryImpl
import com.android.systemui.display.domain.interactor.ConnectedDisplayInteractor
import com.android.systemui.display.domain.interactor.ConnectedDisplayInteractorImpl
+import com.android.systemui.display.domain.interactor.RearDisplayStateInteractor
+import com.android.systemui.display.domain.interactor.RearDisplayStateInteractorImpl
import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
import dagger.Binds
import dagger.Lazy
@@ -46,6 +48,11 @@
provider: ConnectedDisplayInteractorImpl
): ConnectedDisplayInteractor
+ @Binds
+ fun bindRearDisplayStateInteractor(
+ provider: RearDisplayStateInteractorImpl
+ ): RearDisplayStateInteractor
+
@Binds fun bindsDisplayRepository(displayRepository: DisplayRepositoryImpl): DisplayRepository
@Binds
diff --git a/packages/SystemUI/src/com/android/systemui/display/data/repository/DeviceStateRepository.kt b/packages/SystemUI/src/com/android/systemui/display/data/repository/DeviceStateRepository.kt
index 1da5351..29044d0 100644
--- a/packages/SystemUI/src/com/android/systemui/display/data/repository/DeviceStateRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/display/data/repository/DeviceStateRepository.kt
@@ -20,6 +20,7 @@
import android.hardware.devicestate.DeviceState as PlatformDeviceState
import android.hardware.devicestate.DeviceState.PROPERTY_FEATURE_DUAL_DISPLAY_INTERNAL_DEFAULT
import android.hardware.devicestate.DeviceState.PROPERTY_FEATURE_REAR_DISPLAY
+import android.hardware.devicestate.DeviceState.PROPERTY_FEATURE_REAR_DISPLAY_OUTER_DEFAULT
import android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY
import android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY
import android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_HALF_OPEN
@@ -49,6 +50,15 @@
UNFOLDED,
/** Device state that corresponds to the device being in rear display mode */
REAR_DISPLAY,
+ /**
+ * Device state that corresponds to the device being in rear display mode with the inner
+ * display showing a system-provided affordance to cancel the mode.
+ *
+ * TODO(b/371095273): This state will be removed after the RDM_V2 flag lifecycle is complete
+ * at which point the REAR_DISPLAY state will be the will be the new and only rear display
+ * mode.
+ */
+ REAR_DISPLAY_OUTER_DEFAULT,
/** Device state in that corresponds to the device being in concurrent display mode */
CONCURRENT_DISPLAY,
/** Device state in none of the other arrays. */
@@ -62,7 +72,7 @@
val context: Context,
val deviceStateManager: DeviceStateManager,
@Background bgScope: CoroutineScope,
- @Background executor: Executor
+ @Background executor: Executor,
) : DeviceStateRepository {
override val state: StateFlow<DeviceState> =
@@ -105,6 +115,12 @@
*/
private fun PlatformDeviceState.toDeviceStateEnum(): DeviceState {
return when {
+ hasProperties(
+ PROPERTY_FEATURE_REAR_DISPLAY,
+ PROPERTY_FEATURE_REAR_DISPLAY_OUTER_DEFAULT,
+ ) -> {
+ DeviceState.REAR_DISPLAY_OUTER_DEFAULT
+ }
hasProperty(PROPERTY_FEATURE_REAR_DISPLAY) -> DeviceState.REAR_DISPLAY
hasProperty(PROPERTY_FEATURE_DUAL_DISPLAY_INTERNAL_DEFAULT) -> {
DeviceState.CONCURRENT_DISPLAY
@@ -112,7 +128,7 @@
hasProperty(PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY) -> DeviceState.FOLDED
hasProperties(
PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY,
- PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_HALF_OPEN
+ PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_HALF_OPEN,
) -> DeviceState.HALF_FOLDED
hasProperty(PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY) -> {
DeviceState.UNFOLDED
diff --git a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayWindowPropertiesRepository.kt b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayWindowPropertiesRepository.kt
index 80eb9ee..f310b30 100644
--- a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayWindowPropertiesRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayWindowPropertiesRepository.kt
@@ -27,6 +27,7 @@
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.display.shared.model.DisplayWindowProperties
import com.android.systemui.res.R
+import com.android.systemui.shade.shared.flag.ShadeWindowGoesAround
import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
import com.google.common.collect.HashBasedTable
import com.google.common.collect.Table
@@ -60,7 +61,10 @@
) : DisplayWindowPropertiesRepository, CoreStartable {
init {
- StatusBarConnectedDisplays.assertInNewMode()
+ check(StatusBarConnectedDisplays.isEnabled || ShadeWindowGoesAround.isEnabled) {
+ "This should be instantiated only when wither StatusBarConnectedDisplays or " +
+ "ShadeWindowGoesAround are enabled."
+ }
}
private val properties: Table<Int, Int, DisplayWindowProperties> = HashBasedTable.create()
diff --git a/packages/SystemUI/src/com/android/systemui/display/data/repository/PerDisplayStore.kt b/packages/SystemUI/src/com/android/systemui/display/data/repository/PerDisplayStore.kt
index ecddef6..711534f 100644
--- a/packages/SystemUI/src/com/android/systemui/display/data/repository/PerDisplayStore.kt
+++ b/packages/SystemUI/src/com/android/systemui/display/data/repository/PerDisplayStore.kt
@@ -17,13 +17,12 @@
package com.android.systemui.display.data.repository
import android.view.Display
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.CoreStartable
import com.android.systemui.dagger.qualifiers.Background
import java.io.PrintWriter
import java.util.concurrent.ConcurrentHashMap
-import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.CoroutineScope
-import com.android.app.tracing.coroutines.launchTraced as launch
/** Provides per display instances of [T]. */
interface PerDisplayStore<T> {
diff --git a/packages/SystemUI/src/com/android/systemui/display/domain/interactor/RearDisplayStateInteractor.kt b/packages/SystemUI/src/com/android/systemui/display/domain/interactor/RearDisplayStateInteractor.kt
new file mode 100644
index 0000000..b743377
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/display/domain/interactor/RearDisplayStateInteractor.kt
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.display.domain.interactor
+
+import android.view.Display
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.display.data.repository.DeviceStateRepository
+import com.android.systemui.display.data.repository.DisplayRepository
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combineTransform
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flowOn
+
+/** Provides information about the status of Rear Display Mode. */
+interface RearDisplayStateInteractor {
+
+ /** A flow notifying the subscriber of Rear Display state changes */
+ val state: Flow<State>
+
+ sealed class State {
+ /** Indicates that the rear display is disabled */
+ data object Disabled : State()
+
+ /**
+ * Indicates that the device is in Rear Display Mode, and that the inner display is ready to
+ * show a system-provided affordance allowing the user to cancel out of the Rear Display
+ * Mode.
+ */
+ data class Enabled(val innerDisplay: Display) : State()
+ }
+}
+
+@SysUISingleton
+class RearDisplayStateInteractorImpl
+@Inject
+constructor(
+ displayRepository: DisplayRepository,
+ deviceStateRepository: DeviceStateRepository,
+ @Background backgroundCoroutineDispatcher: CoroutineDispatcher,
+) : RearDisplayStateInteractor {
+
+ override val state: Flow<RearDisplayStateInteractor.State> =
+ deviceStateRepository.state
+ .combineTransform(displayRepository.displays) { state, displays ->
+ val innerDisplay = displays.find { it.flags and Display.FLAG_REAR != 0 }
+
+ if (state != DeviceStateRepository.DeviceState.REAR_DISPLAY_OUTER_DEFAULT) {
+ emit(RearDisplayStateInteractor.State.Disabled)
+ } else if (innerDisplay != null) {
+ emit(RearDisplayStateInteractor.State.Enabled(innerDisplay))
+ }
+ }
+ .distinctUntilChanged()
+ .flowOn(backgroundCoroutineDispatcher)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
index 89fce4a..abc810a 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
@@ -469,9 +469,8 @@
|| posture >= mLightSensorOptional.length) {
return;
}
-
- final Sensor oldSensor = mLightSensorOptional[mDevicePosture].get();
- final Sensor newSensor = mLightSensorOptional[posture].get();
+ Sensor oldSensor = mLightSensorOptional[mDevicePosture].orElse(null);
+ Sensor newSensor = mLightSensorOptional[posture].orElse(null);
if (Objects.equals(oldSensor, newSensor)) {
mDevicePosture = posture;
// uses the same sensor for the new posture
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
index 1ffbbd2..b330ba3 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
@@ -65,6 +65,9 @@
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
import com.android.systemui.navigationbar.gestural.domain.GestureInteractor;
import com.android.systemui.navigationbar.gestural.domain.TaskMatcher;
+import com.android.systemui.scene.domain.interactor.SceneInteractor;
+import com.android.systemui.scene.shared.flag.SceneContainerFlag;
+import com.android.systemui.scene.shared.model.Scenes;
import com.android.systemui.shade.ShadeExpansionChangeEvent;
import com.android.systemui.touch.TouchInsetManager;
import com.android.systemui.util.concurrency.DelayableExecutor;
@@ -162,6 +165,7 @@
private TouchMonitor mTouchMonitor;
+ private final SceneInteractor mSceneInteractor;
private final CommunalInteractor mCommunalInteractor;
private boolean mCommunalAvailable;
@@ -378,6 +382,7 @@
KeyguardUpdateMonitor keyguardUpdateMonitor,
ScrimManager scrimManager,
CommunalInteractor communalInteractor,
+ SceneInteractor sceneInteractor,
SystemDialogsCloser systemDialogsCloser,
UiEventLogger uiEventLogger,
@Named(DREAM_TOUCH_INSET_MANAGER) TouchInsetManager touchInsetManager,
@@ -405,6 +410,7 @@
mDreamOverlayCallbackController = dreamOverlayCallbackController;
mWindowTitle = windowTitle;
mCommunalInteractor = communalInteractor;
+ mSceneInteractor = sceneInteractor;
mSystemDialogsCloser = systemDialogsCloser;
mGestureInteractor = gestureInteractor;
mDreamOverlayComponentFactory = dreamOverlayComponentFactory;
@@ -551,9 +557,15 @@
@Override
public void onWakeRequested() {
mUiEventLogger.log(CommunalUiEvent.DREAM_TO_COMMUNAL_HUB_DREAM_AWAKE_START);
- mCommunalInteractor.changeScene(CommunalScenes.Communal,
- "dream wake requested",
- null);
+ if (SceneContainerFlag.isEnabled()) {
+ // Scene interactor can only be modified on main thread.
+ mExecutor.execute(() -> mSceneInteractor.changeScene(Scenes.Communal,
+ "dream wake redirect to communal"));
+ } else {
+ mCommunalInteractor.changeScene(CommunalScenes.Communal,
+ "dream wake requested",
+ null);
+ }
}
private void updateGestureBlockingLocked() {
@@ -617,7 +629,13 @@
mSystemDialogsCloser.closeSystemDialogs();
// Hide glanceable hub (this is a nop if glanceable hub is not open).
- mCommunalInteractor.changeScene(CommunalScenes.Blank, "dream come to front", null);
+ if (SceneContainerFlag.isEnabled()) {
+ // Scene interactor can only be modified on main thread.
+ mExecutor.execute(
+ () -> mSceneInteractor.changeScene(Scenes.Dream, "closing hub to go to dream"));
+ } else {
+ mCommunalInteractor.changeScene(CommunalScenes.Blank, "dream come to front", null);
+ }
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/ui/viewmodel/DreamUserActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/dreams/ui/viewmodel/DreamUserActionsViewModel.kt
index 8b6cc8c..5a9e52a 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/ui/viewmodel/DreamUserActionsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/dreams/ui/viewmodel/DreamUserActionsViewModel.kt
@@ -16,17 +16,66 @@
package com.android.systemui.dreams.ui.viewmodel
+import com.android.compose.animation.scene.Swipe
import com.android.compose.animation.scene.UserAction
import com.android.compose.animation.scene.UserActionResult
+import com.android.systemui.deviceentry.domain.interactor.DeviceUnlockedInteractor
+import com.android.systemui.scene.shared.model.SceneFamilies
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.scene.ui.viewmodel.UserActionsViewModel
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.shade.shared.model.ShadeMode
+import com.android.systemui.shade.ui.viewmodel.dualShadeActions
+import com.android.systemui.shade.ui.viewmodel.singleShadeActions
+import com.android.systemui.shade.ui.viewmodel.splitShadeActions
+import com.android.systemui.utils.coroutines.flow.flatMapLatestConflated
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.map
/** Handles user input for the dream scene. */
-class DreamUserActionsViewModel @AssistedInject constructor() : UserActionsViewModel() {
+class DreamUserActionsViewModel
+@AssistedInject
+constructor(
+ private val deviceUnlockedInteractor: DeviceUnlockedInteractor,
+ private val shadeInteractor: ShadeInteractor,
+) : UserActionsViewModel() {
override suspend fun hydrateActions(setActions: (Map<UserAction, UserActionResult>) -> Unit) {
- setActions(emptyMap())
+ shadeInteractor.isShadeTouchable
+ .flatMapLatestConflated { isShadeTouchable ->
+ if (!isShadeTouchable) {
+ flowOf(emptyMap())
+ } else {
+ combine(
+ deviceUnlockedInteractor.deviceUnlockStatus.map { it.isUnlocked },
+ shadeInteractor.shadeMode,
+ ) { isDeviceUnlocked, shadeMode ->
+ buildList {
+ add(Swipe.Start to Scenes.Communal)
+
+ val bouncerOrGone =
+ if (isDeviceUnlocked) Scenes.Gone else Scenes.Bouncer
+ add(Swipe.Up to bouncerOrGone)
+
+ // "Home" is either Dream, Lockscreen, or Gone.
+ add(Swipe.End to SceneFamilies.Home)
+
+ addAll(
+ when (shadeMode) {
+ ShadeMode.Single -> singleShadeActions()
+ ShadeMode.Split -> splitShadeActions()
+ ShadeMode.Dual -> dualShadeActions()
+ }
+ )
+ }
+ .associate { it }
+ }
+ }
+ }
+ .collect { setActions(it) }
}
@AssistedFactory
diff --git a/packages/SystemUI/src/com/android/systemui/haptics/msdl/qs/TileHapticsViewModel.kt b/packages/SystemUI/src/com/android/systemui/haptics/msdl/qs/TileHapticsViewModel.kt
index ed7d182..316964a 100644
--- a/packages/SystemUI/src/com/android/systemui/haptics/msdl/qs/TileHapticsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/haptics/msdl/qs/TileHapticsViewModel.kt
@@ -83,9 +83,6 @@
interactionState == TileInteractionState.LONG_CLICKED &&
animationState == TileAnimationState.ACTIVITY_LAUNCH ->
TileHapticsState.LONG_PRESS
- interactionState == TileInteractionState.LONG_CLICKED &&
- !tileViewModel.currentState.handlesLongClick ->
- TileHapticsState.FAILED_LONGPRESS
else -> TileHapticsState.NO_HAPTICS
}
}
@@ -102,7 +99,6 @@
TileHapticsState.TOGGLE_ON -> MSDLToken.SWITCH_ON
TileHapticsState.TOGGLE_OFF -> MSDLToken.SWITCH_OFF
TileHapticsState.LONG_PRESS -> MSDLToken.LONG_PRESS
- TileHapticsState.FAILED_LONGPRESS -> MSDLToken.FAILURE
TileHapticsState.NO_HAPTICS -> null
}
tokenToPlay?.let {
@@ -154,7 +150,6 @@
TOGGLE_ON,
TOGGLE_OFF,
LONG_PRESS,
- FAILED_LONGPRESS,
NO_HAPTICS,
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ShortcutHelperModule.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ShortcutHelperModule.kt
index 7b3380a..1af7340 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ShortcutHelperModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ShortcutHelperModule.kt
@@ -18,6 +18,9 @@
import com.android.systemui.CoreStartable
import com.android.systemui.Flags.keyboardShortcutHelperRewrite
+import com.android.systemui.keyboard.shortcut.data.repository.CustomShortcutCategoriesRepository
+import com.android.systemui.keyboard.shortcut.data.repository.DefaultShortcutCategoriesRepository
+import com.android.systemui.keyboard.shortcut.data.repository.ShortcutCategoriesRepository
import com.android.systemui.keyboard.shortcut.data.repository.ShortcutHelperStateRepository
import com.android.systemui.keyboard.shortcut.data.source.AppCategoriesShortcutsSource
import com.android.systemui.keyboard.shortcut.data.source.CurrentAppShortcutsSource
@@ -27,6 +30,8 @@
import com.android.systemui.keyboard.shortcut.data.source.SystemShortcutsSource
import com.android.systemui.keyboard.shortcut.qualifiers.AppCategoriesShortcuts
import com.android.systemui.keyboard.shortcut.qualifiers.CurrentAppShortcuts
+import com.android.systemui.keyboard.shortcut.qualifiers.CustomShortcutCategories
+import com.android.systemui.keyboard.shortcut.qualifiers.DefaultShortcutCategories
import com.android.systemui.keyboard.shortcut.qualifiers.InputShortcuts
import com.android.systemui.keyboard.shortcut.qualifiers.MultitaskingShortcuts
import com.android.systemui.keyboard.shortcut.qualifiers.SystemShortcuts
@@ -63,6 +68,18 @@
impl: AppCategoriesShortcutsSource
): KeyboardShortcutGroupsSource
+ @Binds
+ @DefaultShortcutCategories
+ fun defaultShortcutCategoriesRepository(
+ impl: DefaultShortcutCategoriesRepository
+ ): ShortcutCategoriesRepository
+
+ @Binds
+ @CustomShortcutCategories
+ fun customShortcutCategoriesRepository(
+ impl: CustomShortcutCategoriesRepository
+ ): ShortcutCategoriesRepository
+
companion object {
@Provides
@IntoMap
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepository.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepository.kt
index 7f8fbb5..da5590a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepository.kt
@@ -23,19 +23,24 @@
import android.hardware.input.InputManager
import android.hardware.input.InputSettings
import android.hardware.input.KeyGestureEvent
+import com.android.systemui.Flags.shortcutHelperKeyGlyph
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.keyboard.shortcut.data.model.InternalKeyboardShortcutGroup
import com.android.systemui.keyboard.shortcut.data.model.InternalKeyboardShortcutInfo
+import com.android.systemui.keyboard.shortcut.shared.model.KeyCombination
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategory
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutHelperState.Active
+import com.android.systemui.keyboard.shortcut.shared.model.ShortcutKey
import com.android.systemui.settings.UserTracker
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.withContext
@@ -49,6 +54,7 @@
@Background private val backgroundScope: CoroutineScope,
@Background private val backgroundDispatcher: CoroutineDispatcher,
private val shortcutCategoriesUtils: ShortcutCategoriesUtils,
+ private val context: Context,
) : ShortcutCategoriesRepository {
private val userContext: Context
@@ -59,6 +65,8 @@
private val inputManager: InputManager
get() = userContext.getSystemService(INPUT_SERVICE) as InputManager
+ private val _selectedKeyCombination = MutableStateFlow<KeyCombination?>(null)
+
private val activeInputDevice =
stateRepository.state.map {
if (it is Active) {
@@ -68,6 +76,41 @@
}
}
+ val pressedKeys =
+ _selectedKeyCombination
+ .combine(activeInputDevice) { keyCombination, inputDevice ->
+ if (inputDevice == null || keyCombination == null) {
+ return@combine emptyList()
+ } else {
+ val keyGlyphMap =
+ if (shortcutHelperKeyGlyph()) {
+ inputManager.getKeyGlyphMap(inputDevice.id)
+ } else null
+ val modifiers =
+ shortcutCategoriesUtils.toShortcutModifierKeys(
+ keyCombination.modifiers,
+ keyGlyphMap,
+ )
+ val triggerKey =
+ keyCombination.keyCode?.let {
+ shortcutCategoriesUtils.toShortcutKey(
+ keyGlyphMap,
+ inputDevice.keyCharacterMap,
+ keyCode = it,
+ )
+ }
+ val keys = mutableListOf<ShortcutKey>()
+ modifiers?.let { keys += it }
+ triggerKey?.let { keys += it }
+ return@combine keys
+ }
+ }
+ .stateIn(
+ scope = backgroundScope,
+ started = SharingStarted.Lazily,
+ initialValue = emptyList(),
+ )
+
override val categories: Flow<List<ShortcutCategory>> =
activeInputDevice
.map { inputDevice ->
@@ -103,6 +146,10 @@
started = SharingStarted.Lazily,
)
+ fun updateUserKeyCombination(keyCombination: KeyCombination?) {
+ _selectedKeyCombination.value = keyCombination
+ }
+
private fun toInternalGroupSources(
inputGestures: List<InputGestureData>
): List<InternalGroupsSource> {
@@ -147,25 +194,23 @@
private fun fetchGroupLabelByGestureType(
@KeyGestureEvent.KeyGestureType keyGestureType: Int
): String? {
- return InputGestures.gestureToInternalKeyboardShortcutGroupLabelMap.getOrDefault(
- keyGestureType,
- null,
- )
+ InputGestures.gestureToInternalKeyboardShortcutGroupLabelResIdMap[keyGestureType]?.let {
+ return context.getString(it)
+ } ?: return null
}
private fun fetchShortcutInfoLabelByGestureType(
@KeyGestureEvent.KeyGestureType keyGestureType: Int
): String? {
- return InputGestures.gestureToInternalKeyboardShortcutInfoLabelMap.getOrDefault(
- keyGestureType,
- null,
- )
+ InputGestures.gestureToInternalKeyboardShortcutInfoLabelResIdMap[keyGestureType]?.let {
+ return context.getString(it)
+ } ?: return null
}
private fun fetchShortcutCategoryTypeByGestureType(
@KeyGestureEvent.KeyGestureType keyGestureType: Int
): ShortcutCategoryType? {
- return InputGestures.gestureToShortcutCategoryTypeMap.getOrDefault(keyGestureType, null)
+ return InputGestures.gestureToShortcutCategoryTypeMap[keyGestureType]
}
private data class InternalGroupsSource(
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/InputGestures.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/InputGestures.kt
index 28134db..7bb294d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/InputGestures.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/InputGestures.kt
@@ -44,6 +44,7 @@
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.AppCategories
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.MultiTasking
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.System
+import com.android.systemui.res.R
object InputGestures {
val gestureToShortcutCategoryTypeMap =
@@ -80,77 +81,97 @@
KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MESSAGING to AppCategories,
)
- // TODO move all string to to resources use the same resources as the original shortcuts
- // - that way when the strings are translated there are no discrepancies
- val gestureToInternalKeyboardShortcutGroupLabelMap =
+ val gestureToInternalKeyboardShortcutGroupLabelResIdMap =
mapOf(
// System Category
- KEY_GESTURE_TYPE_HOME to "System controls",
- KEY_GESTURE_TYPE_RECENT_APPS to "System controls",
- KEY_GESTURE_TYPE_BACK to "System controls",
- KEY_GESTURE_TYPE_TAKE_SCREENSHOT to "System controls",
- KEY_GESTURE_TYPE_OPEN_SHORTCUT_HELPER to "System controls",
- KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL to "System controls",
- KEY_GESTURE_TYPE_LOCK_SCREEN to "System controls",
- KEY_GESTURE_TYPE_ALL_APPS to "System controls",
- KEY_GESTURE_TYPE_OPEN_NOTES to "System apps",
- KEY_GESTURE_TYPE_LAUNCH_SYSTEM_SETTINGS to "System apps",
- KEY_GESTURE_TYPE_LAUNCH_ASSISTANT to "System apps",
- KEY_GESTURE_TYPE_LAUNCH_VOICE_ASSISTANT to "System apps",
+ KEY_GESTURE_TYPE_HOME to R.string.shortcut_helper_category_system_controls,
+ KEY_GESTURE_TYPE_RECENT_APPS to R.string.shortcut_helper_category_system_controls,
+ KEY_GESTURE_TYPE_BACK to R.string.shortcut_helper_category_system_controls,
+ KEY_GESTURE_TYPE_TAKE_SCREENSHOT to R.string.shortcut_helper_category_system_controls,
+ KEY_GESTURE_TYPE_OPEN_SHORTCUT_HELPER to
+ R.string.shortcut_helper_category_system_controls,
+ KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL to
+ R.string.shortcut_helper_category_system_controls,
+ KEY_GESTURE_TYPE_LOCK_SCREEN to R.string.shortcut_helper_category_system_controls,
+ KEY_GESTURE_TYPE_ALL_APPS to R.string.shortcut_helper_category_system_controls,
+ KEY_GESTURE_TYPE_OPEN_NOTES to R.string.shortcut_helper_category_system_apps,
+ KEY_GESTURE_TYPE_LAUNCH_SYSTEM_SETTINGS to
+ R.string.shortcut_helper_category_system_apps,
+ KEY_GESTURE_TYPE_LAUNCH_ASSISTANT to R.string.shortcut_helper_category_system_apps,
+ KEY_GESTURE_TYPE_LAUNCH_VOICE_ASSISTANT to
+ R.string.shortcut_helper_category_system_apps,
// Multitasking Category
- KEY_GESTURE_TYPE_RECENT_APPS_SWITCHER to "Recent apps",
- KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_LEFT to "Split screen",
- KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_RIGHT to "Split screen",
- KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION to "Split screen",
- KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_LEFT to "Split screen",
- KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_RIGHT to "Split screen",
+ KEY_GESTURE_TYPE_RECENT_APPS_SWITCHER to R.string.shortcutHelper_category_recent_apps,
+ KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_LEFT to
+ R.string.shortcutHelper_category_split_screen,
+ KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_RIGHT to
+ R.string.shortcutHelper_category_split_screen,
+ KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION to
+ R.string.shortcutHelper_category_split_screen,
+ KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_LEFT to
+ R.string.shortcutHelper_category_split_screen,
+ KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_RIGHT to
+ R.string.shortcutHelper_category_split_screen,
// App Category
- KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALCULATOR to "Applications",
- KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALENDAR to "Applications",
- KEY_GESTURE_TYPE_LAUNCH_DEFAULT_BROWSER to "Applications",
- KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CONTACTS to "Applications",
- KEY_GESTURE_TYPE_LAUNCH_DEFAULT_EMAIL to "Applications",
- KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MAPS to "Applications",
- KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MESSAGING to "Applications",
+ KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALCULATOR to
+ R.string.keyboard_shortcut_group_applications,
+ KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALENDAR to
+ R.string.keyboard_shortcut_group_applications,
+ KEY_GESTURE_TYPE_LAUNCH_DEFAULT_BROWSER to
+ R.string.keyboard_shortcut_group_applications,
+ KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CONTACTS to
+ R.string.keyboard_shortcut_group_applications,
+ KEY_GESTURE_TYPE_LAUNCH_DEFAULT_EMAIL to R.string.keyboard_shortcut_group_applications,
+ KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MAPS to R.string.keyboard_shortcut_group_applications,
+ KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MESSAGING to
+ R.string.keyboard_shortcut_group_applications,
)
- val gestureToInternalKeyboardShortcutInfoLabelMap =
+ val gestureToInternalKeyboardShortcutInfoLabelResIdMap =
mapOf(
// System Category
- KEY_GESTURE_TYPE_HOME to "Go to home screen",
- KEY_GESTURE_TYPE_RECENT_APPS to "View recent apps",
- KEY_GESTURE_TYPE_BACK to "Go back",
- KEY_GESTURE_TYPE_TAKE_SCREENSHOT to "Take screenshot",
- KEY_GESTURE_TYPE_OPEN_SHORTCUT_HELPER to "Show shortcuts",
- KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL to "View notifications",
- KEY_GESTURE_TYPE_LOCK_SCREEN to "Lock screen",
- KEY_GESTURE_TYPE_ALL_APPS to "Open apps list",
- KEY_GESTURE_TYPE_OPEN_NOTES to "Take a note",
- KEY_GESTURE_TYPE_LAUNCH_SYSTEM_SETTINGS to "Open settings",
- KEY_GESTURE_TYPE_LAUNCH_ASSISTANT to "Open assistant",
- KEY_GESTURE_TYPE_LAUNCH_VOICE_ASSISTANT to "Open assistant",
+ KEY_GESTURE_TYPE_HOME to R.string.group_system_access_home_screen,
+ KEY_GESTURE_TYPE_RECENT_APPS to R.string.group_system_overview_open_apps,
+ KEY_GESTURE_TYPE_BACK to R.string.group_system_go_back,
+ KEY_GESTURE_TYPE_TAKE_SCREENSHOT to R.string.group_system_full_screenshot,
+ KEY_GESTURE_TYPE_OPEN_SHORTCUT_HELPER to
+ R.string.group_system_access_system_app_shortcuts,
+ KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL to
+ R.string.group_system_access_notification_shade,
+ KEY_GESTURE_TYPE_LOCK_SCREEN to R.string.group_system_lock_screen,
+ KEY_GESTURE_TYPE_ALL_APPS to R.string.group_system_access_all_apps_search,
+ KEY_GESTURE_TYPE_OPEN_NOTES to R.string.group_system_quick_memo,
+ KEY_GESTURE_TYPE_LAUNCH_SYSTEM_SETTINGS to R.string.group_system_access_system_settings,
+ KEY_GESTURE_TYPE_LAUNCH_ASSISTANT to R.string.group_system_access_google_assistant,
+ KEY_GESTURE_TYPE_LAUNCH_VOICE_ASSISTANT to
+ R.string.group_system_access_google_assistant,
// Multitasking Category
- KEY_GESTURE_TYPE_RECENT_APPS_SWITCHER to "Cycle forward through recent apps",
- KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_LEFT to
- "Use split screen with current app on the left",
- KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_RIGHT to
- "Use split screen with current app on the right",
- KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION to "Switch from split screen to full screen",
+ KEY_GESTURE_TYPE_RECENT_APPS_SWITCHER to R.string.group_system_cycle_forward,
+ KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_LEFT to R.string.system_multitasking_lhs,
+ KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_RIGHT to R.string.system_multitasking_rhs,
+ KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION to R.string.system_multitasking_full_screen,
KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_LEFT to
- "Switch to app on left or above while using split screen",
+ R.string.system_multitasking_splitscreen_focus_lhs,
KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_RIGHT to
- "Switch to app on right or below while using split screen",
+ R.string.system_multitasking_splitscreen_focus_rhs,
// App Category
- KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALCULATOR to "Calculator",
- KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALENDAR to "Calendar",
- KEY_GESTURE_TYPE_LAUNCH_DEFAULT_BROWSER to "Chrome",
- KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CONTACTS to "Contacts",
- KEY_GESTURE_TYPE_LAUNCH_DEFAULT_EMAIL to "Gmail",
- KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MAPS to "Maps",
- KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MESSAGING to "Messages",
+ KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALCULATOR to
+ R.string.keyboard_shortcut_group_applications_calculator,
+ KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALENDAR to
+ R.string.keyboard_shortcut_group_applications_calendar,
+ KEY_GESTURE_TYPE_LAUNCH_DEFAULT_BROWSER to
+ R.string.keyboard_shortcut_group_applications_browser,
+ KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CONTACTS to
+ R.string.keyboard_shortcut_group_applications_contacts,
+ KEY_GESTURE_TYPE_LAUNCH_DEFAULT_EMAIL to
+ R.string.keyboard_shortcut_group_applications_email,
+ KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MAPS to
+ R.string.keyboard_shortcut_group_applications_maps,
+ KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MESSAGING to
+ R.string.keyboard_shortcut_group_applications_sms,
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutCategoriesUtils.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutCategoriesUtils.kt
index 899fd15..3988d1f1 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutCategoriesUtils.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutCategoriesUtils.kt
@@ -24,6 +24,7 @@
import android.view.InputDevice
import android.view.KeyCharacterMap
import android.view.KeyEvent
+import android.view.KeyEvent.META_META_ON
import com.android.systemui.Flags.shortcutHelperKeyGlyph
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.keyboard.shortcut.data.model.InternalKeyboardShortcutGroup
@@ -92,7 +93,7 @@
}
.filter { it.shortcuts.isNotEmpty() }
return if (subCategories.isEmpty()) {
- Log.wtf(TAG, "Empty sub categories after converting $shortcutGroups")
+ Log.w(TAG, "Empty sub categories after converting $shortcutGroups")
null
} else {
ShortcutCategory(type, subCategories)
@@ -161,7 +162,7 @@
}
if (remainingModifiers != 0) {
// There is a remaining modifier we don't support
- Log.wtf(TAG, "Unsupported modifiers remaining: $remainingModifiers")
+ Log.w(TAG, "Unsupported modifiers remaining: $remainingModifiers")
return null
}
if (info.keycode != 0 || info.baseCharacter > Char.MIN_VALUE) {
@@ -170,21 +171,32 @@
?: return null
}
if (keys.isEmpty()) {
- Log.wtf(TAG, "No keys for $info")
+ Log.w(TAG, "No keys for $info")
return null
}
return ShortcutCommand(keys = keys, isCustom = info.isCustomShortcut)
}
+ fun toShortcutModifierKeys(modifiers: Int, keyGlyphMap: KeyGlyphMap?): List<ShortcutKey>? {
+ val keys: MutableList<ShortcutKey> = mutableListOf()
+ var remainingModifiers = modifiers
+ SUPPORTED_MODIFIERS.forEach { supportedModifier ->
+ if ((supportedModifier and remainingModifiers) != 0) {
+ keys += toShortcutModifierKey(keyGlyphMap, supportedModifier) ?: return null
+ remainingModifiers = remainingModifiers and supportedModifier.inv()
+ }
+ }
+ return keys
+ }
+
private fun toShortcutModifierKey(keyGlyphMap: KeyGlyphMap?, modifierMask: Int): ShortcutKey? {
val modifierDrawable = keyGlyphMap?.getDrawableForModifierState(context, modifierMask)
if (modifierDrawable != null) {
return ShortcutKey.Icon.DrawableIcon(drawable = modifierDrawable)
}
- val iconResId = ShortcutHelperKeys.keyIcons[modifierMask]
- if (iconResId != null) {
- return ShortcutKey.Icon.ResIdIcon(iconResId)
+ if (modifierMask == META_META_ON) {
+ return ShortcutKey.Icon.ResIdIcon(ShortcutHelperKeys.metaModifierIconResId)
}
val modifierLabel = ShortcutHelperKeys.modifierLabels[modifierMask]
@@ -195,7 +207,7 @@
return null
}
- private fun toShortcutKey(
+ fun toShortcutKey(
keyGlyphMap: KeyGlyphMap?,
keyCharacterMap: KeyCharacterMap,
keyCode: Int,
@@ -222,7 +234,7 @@
if (displayLabelCharacter.code != 0) {
return ShortcutKey.Text(displayLabelCharacter.toString())
}
- Log.wtf(TAG, "Couldn't find label or icon for key: $keyCode")
+ Log.w(TAG, "Couldn't find label or icon for key: $keyCode")
return null
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperKeys.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperKeys.kt
index 288efa2..e47b33f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperKeys.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperKeys.kt
@@ -116,9 +116,10 @@
object ShortcutHelperKeys {
+ val metaModifierIconResId = R.drawable.ic_ksh_key_meta
+
val keyIcons =
mapOf(
- META_META_ON to R.drawable.ic_ksh_key_meta,
KEYCODE_BACK to R.drawable.ic_arrow_back_2,
KEYCODE_HOME to R.drawable.ic_radio_button_unchecked,
KEYCODE_RECENT_APPS to R.drawable.ic_check_box_outline_blank,
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutCustomizationInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutCustomizationInteractor.kt
index 85d2214..aad55dc 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutCustomizationInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutCustomizationInteractor.kt
@@ -16,13 +16,22 @@
package com.android.systemui.keyboard.shortcut.domain.interactor
-import android.view.KeyEvent.META_META_ON
+import com.android.systemui.keyboard.shortcut.data.repository.CustomShortcutCategoriesRepository
import com.android.systemui.keyboard.shortcut.data.repository.ShortcutHelperKeys
+import com.android.systemui.keyboard.shortcut.shared.model.KeyCombination
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutKey
import javax.inject.Inject
-class ShortcutCustomizationInteractor @Inject constructor() {
+class ShortcutCustomizationInteractor
+@Inject
+constructor(private val customShortcutRepository: CustomShortcutCategoriesRepository) {
+ val pressedKeys = customShortcutRepository.pressedKeys
+
+ fun updateUserSelectedKeyCombination(keyCombination: KeyCombination?) {
+ customShortcutRepository.updateUserKeyCombination(keyCombination)
+ }
+
fun getDefaultCustomShortcutModifierKey(): ShortcutKey.Icon.ResIdIcon {
- return ShortcutKey.Icon.ResIdIcon(ShortcutHelperKeys.keyIcons[META_META_ON]!!)
+ return ShortcutKey.Icon.ResIdIcon(ShortcutHelperKeys.metaModifierIconResId)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperCategoriesInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperCategoriesInteractor.kt
index 39fc27d..0381eae 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperCategoriesInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperCategoriesInteractor.kt
@@ -16,37 +16,66 @@
package com.android.systemui.keyboard.shortcut.domain.interactor
+import com.android.systemui.Flags.keyboardShortcutHelperShortcutCustomizer
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.keyboard.shortcut.data.repository.DefaultShortcutCategoriesRepository
+import com.android.systemui.keyboard.shortcut.data.repository.ShortcutCategoriesRepository
+import com.android.systemui.keyboard.shortcut.qualifiers.CustomShortcutCategories
+import com.android.systemui.keyboard.shortcut.qualifiers.DefaultShortcutCategories
import com.android.systemui.keyboard.shortcut.shared.model.Shortcut
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategory
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutSubCategory
+import dagger.Lazy
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.flowOf
@SysUISingleton
class ShortcutHelperCategoriesInteractor
@Inject
-constructor(categoriesRepository: DefaultShortcutCategoriesRepository) {
-
+constructor(
+ @DefaultShortcutCategories defaultCategoriesRepository: ShortcutCategoriesRepository,
+ @CustomShortcutCategories customCategoriesRepositoryLazy: Lazy<ShortcutCategoriesRepository>,
+) {
val shortcutCategories: Flow<List<ShortcutCategory>> =
- categoriesRepository.categories.map { categories ->
- categories.map { category -> groupSubCategoriesInCategory(category) }
+ defaultCategoriesRepository.categories.combine(
+ if (keyboardShortcutHelperShortcutCustomizer()) {
+ customCategoriesRepositoryLazy.get().categories
+ } else {
+ flowOf(emptyList())
+ }
+ ) { defaultShortcutCategories, customShortcutCategories ->
+ groupCategories(defaultShortcutCategories + customShortcutCategories)
}
- private fun groupSubCategoriesInCategory(shortcutCategory: ShortcutCategory): ShortcutCategory {
- val subCategoriesWithGroupedShortcuts =
- shortcutCategory.subCategories.map {
- ShortcutSubCategory(
- label = it.label,
- shortcuts = groupShortcutsInSubcategory(it.shortcuts),
+ private fun groupCategories(
+ shortcutCategories: List<ShortcutCategory>
+ ): List<ShortcutCategory> {
+ return shortcutCategories
+ .groupBy { it.type }
+ .entries
+ .map { (categoryType, groupedCategories) ->
+ ShortcutCategory(
+ type = categoryType,
+ subCategories =
+ groupSubCategories(groupedCategories.flatMap { it.subCategories }),
)
}
- return ShortcutCategory(
- type = shortcutCategory.type,
- subCategories = subCategoriesWithGroupedShortcuts,
- )
+ }
+
+ private fun groupSubCategories(
+ subCategories: List<ShortcutSubCategory>
+ ): List<ShortcutSubCategory> {
+ return subCategories
+ .groupBy { it.label }
+ .entries
+ .map { (label, groupedSubcategories) ->
+ ShortcutSubCategory(
+ label = label,
+ shortcuts =
+ groupShortcutsInSubcategory(groupedSubcategories.flatMap { it.shortcuts }),
+ )
+ }
}
private fun groupShortcutsInSubcategory(shortcuts: List<Shortcut>) =
diff --git a/core/java/android/text/ClientFlags.java b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/qualifiers/CustomShortcutCategories.kt
similarity index 60%
copy from core/java/android/text/ClientFlags.java
copy to packages/SystemUI/src/com/android/systemui/keyboard/shortcut/qualifiers/CustomShortcutCategories.kt
index ca88764..8acb9ee 100644
--- a/core/java/android/text/ClientFlags.java
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/qualifiers/CustomShortcutCategories.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,16 +14,8 @@
* limitations under the License.
*/
-package android.text;
+package com.android.systemui.keyboard.shortcut.qualifiers
-/**
- * An aconfig feature flags that can be accessible from application process without
- * ContentProvider IPCs.
- *
- * When you add new flags, you have to add flag string to {@link TextFlags#TEXT_ACONFIGS_FLAGS}.
- *
- * TODO(nona): Remove this class.
- * @hide
- */
-public class ClientFlags {
-}
+import javax.inject.Qualifier
+
+@Qualifier annotation class CustomShortcutCategories
diff --git a/core/java/android/text/ClientFlags.java b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/qualifiers/DefaultShortcutCategories.kt
similarity index 60%
copy from core/java/android/text/ClientFlags.java
copy to packages/SystemUI/src/com/android/systemui/keyboard/shortcut/qualifiers/DefaultShortcutCategories.kt
index ca88764..f94e10d 100644
--- a/core/java/android/text/ClientFlags.java
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/qualifiers/DefaultShortcutCategories.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,16 +14,8 @@
* limitations under the License.
*/
-package android.text;
+package com.android.systemui.keyboard.shortcut.qualifiers
-/**
- * An aconfig feature flags that can be accessible from application process without
- * ContentProvider IPCs.
- *
- * When you add new flags, you have to add flag string to {@link TextFlags#TEXT_ACONFIGS_FLAGS}.
- *
- * TODO(nona): Remove this class.
- * @hide
- */
-public class ClientFlags {
-}
+import javax.inject.Qualifier
+
+@Qualifier annotation class DefaultShortcutCategories
diff --git a/core/java/android/text/ClientFlags.java b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/KeyCombination.kt
similarity index 60%
copy from core/java/android/text/ClientFlags.java
copy to packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/KeyCombination.kt
index ca88764..5e4031b 100644
--- a/core/java/android/text/ClientFlags.java
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/KeyCombination.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,16 +14,6 @@
* limitations under the License.
*/
-package android.text;
+package com.android.systemui.keyboard.shortcut.shared.model
-/**
- * An aconfig feature flags that can be accessible from application process without
- * ContentProvider IPCs.
- *
- * When you add new flags, you have to add flag string to {@link TextFlags#TEXT_ACONFIGS_FLAGS}.
- *
- * TODO(nona): Remove this class.
- * @hide
- */
-public class ClientFlags {
-}
+data class KeyCombination(val modifiers: Int, val keyCode: Int?)
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt
index 41e6929..b6b5d17 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt
@@ -72,6 +72,7 @@
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@@ -90,15 +91,18 @@
import androidx.compose.ui.input.key.key
import androidx.compose.ui.input.key.onKeyEvent
import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.Role
+import androidx.compose.ui.semantics.isTraversalGroup
import androidx.compose.ui.semantics.role
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.withStyle
+import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
@@ -377,15 +381,19 @@
Column(modifier = modifier.fillMaxSize().padding(horizontal = 24.dp)) {
Row(modifier = Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) {
- Box(modifier = Modifier.padding(start = 202.dp).width(412.dp)) {
- TitleBar(isCustomizing)
- }
- Spacer(modifier = Modifier.weight(1f))
- if (isShortcutCustomizerFlagEnabled) {
- if (isCustomizing) {
- DoneButton(onClick = { isCustomizing = false })
+ // Keep title centered whether customize button is visible or not.
+ Box(modifier = Modifier.fillMaxWidth(), contentAlignment = Alignment.CenterEnd) {
+ Box(modifier = Modifier.fillMaxWidth(), contentAlignment = Alignment.Center) {
+ TitleBar(isCustomizing)
+ }
+ if (isShortcutCustomizerFlagEnabled) {
+ if (isCustomizing) {
+ DoneButton(onClick = { isCustomizing = false })
+ } else {
+ CustomizeButton(onClick = { isCustomizing = true })
+ }
} else {
- CustomizeButton(onClick = { isCustomizing = true })
+ Spacer(modifier = Modifier.width(if (isCustomizing) 69.dp else 133.dp))
}
}
}
@@ -393,7 +401,7 @@
Row(Modifier.fillMaxWidth()) {
StartSidePanel(
onSearchQueryChanged = onSearchQueryChanged,
- modifier = Modifier.width(240.dp),
+ modifier = Modifier.width(240.dp).semantics { isTraversalGroup = true },
categories = categories,
onKeyboardSettingsClicked = onKeyboardSettingsClicked,
selectedCategory = selectedCategoryType,
@@ -402,7 +410,7 @@
Spacer(modifier = Modifier.width(24.dp))
EndSidePanel(
searchQuery,
- Modifier.fillMaxSize().padding(top = 8.dp),
+ Modifier.fillMaxSize().padding(top = 8.dp).semantics { isTraversalGroup = true },
selectedCategory,
isCustomizing = isCustomizing,
onCustomizationRequested = onCustomizationRequested,
@@ -550,7 +558,7 @@
.padding(8.dp)
) {
Row(
- modifier = Modifier.width(128.dp).align(Alignment.CenterVertically),
+ modifier = Modifier.width(128.dp).align(Alignment.CenterVertically).weight(0.333f),
horizontalArrangement = Arrangement.spacedBy(16.dp),
verticalAlignment = Alignment.CenterVertically,
) {
@@ -561,7 +569,7 @@
}
Spacer(modifier = Modifier.width(24.dp))
ShortcutKeyCombinations(
- modifier = Modifier.weight(1f),
+ modifier = Modifier.weight(.666f),
shortcut = shortcut,
isCustomizing = isCustomizing,
onAddShortcutRequested = { onCustomizationRequested(shortcut.label) },
@@ -791,16 +799,25 @@
selectedCategory: ShortcutCategoryType?,
onCategoryClicked: (ShortcutCategoryUi) -> Unit,
) {
- Column(modifier) {
- ShortcutsSearchBar(onSearchQueryChanged)
- Spacer(modifier = Modifier.heightIn(8.dp))
- CategoriesPanelTwoPane(categories, selectedCategory, onCategoryClicked)
- Spacer(modifier = Modifier.weight(1f))
- KeyboardSettings(
- horizontalPadding = 24.dp,
- verticalPadding = 24.dp,
- onKeyboardSettingsClicked,
- )
+ CompositionLocalProvider(
+ // Restrict system font scale increases up to a max so categories display correctly.
+ LocalDensity provides
+ Density(
+ density = LocalDensity.current.density,
+ fontScale = LocalDensity.current.fontScale.coerceIn(1f, 1.5f),
+ )
+ ) {
+ Column(modifier) {
+ ShortcutsSearchBar(onSearchQueryChanged)
+ Spacer(modifier = Modifier.heightIn(8.dp))
+ CategoriesPanelTwoPane(categories, selectedCategory, onCategoryClicked)
+ Spacer(modifier = Modifier.weight(1f))
+ KeyboardSettings(
+ horizontalPadding = 24.dp,
+ verticalPadding = 24.dp,
+ onKeyboardSettingsClicked,
+ )
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelperUtils.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelperUtils.kt
index e295564..f9904f6 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelperUtils.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelperUtils.kt
@@ -49,5 +49,5 @@
object ShortcutHelperBottomSheet {
val DefaultWidth = 412.dp
val LargeScreenWidthPortrait = 704.dp
- val LargeScreenWidthLandscape = 864.dp
+ val LargeScreenWidthLandscape = 960.dp
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/Surfaces.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/Surfaces.kt
index e761c73..58ce194 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/Surfaces.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/Surfaces.kt
@@ -18,6 +18,7 @@
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.IndicationNodeFactory
+import androidx.compose.foundation.LocalIndication
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
@@ -126,8 +127,7 @@
.selectable(
selected = selected,
interactionSource = interactionSource,
- indication =
- ShortcutHelperIndication(interactionSource, interactionsConfig),
+ indication = ShortcutHelperIndication(interactionsConfig),
enabled = enabled,
onClick = onClick,
)
@@ -181,8 +181,7 @@
)
.clickable(
interactionSource = interactionSource,
- indication =
- ShortcutHelperIndication(interactionSource, interactionsConfig),
+ indication = ShortcutHelperIndication(interactionsConfig),
enabled = enabled,
onClick = onClick,
),
@@ -507,10 +506,8 @@
}
}
-data class ShortcutHelperIndication(
- private val interactionSource: InteractionSource,
- private val interactionsConfig: InteractionsConfig,
-) : IndicationNodeFactory {
+data class ShortcutHelperIndication(private val interactionsConfig: InteractionsConfig) :
+ IndicationNodeFactory {
override fun create(interactionSource: InteractionSource): DelegatableNode {
return ShortcutHelperInteractionsNode(interactionSource, interactionsConfig)
}
@@ -529,3 +526,15 @@
val hoverPadding: Dp = 0.dp,
val pressedPadding: Dp = hoverPadding,
)
+
+@Composable
+fun ProvideShortcutHelperIndication(
+ interactionsConfig: InteractionsConfig,
+ content: @Composable () -> Unit,
+) {
+ CompositionLocalProvider(
+ LocalIndication provides ShortcutHelperIndication(interactionsConfig)
+ ) {
+ content()
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModel.kt
index 912bfe9..08fd0af8 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModel.kt
@@ -123,7 +123,7 @@
userContext.packageManager.getApplicationIcon(type.packageName)
IconSource(painter = DrawablePainter(drawable = iconDrawable))
} catch (e: NameNotFoundException) {
- Log.wtf(
+ Log.w(
"ShortcutHelperViewModel",
"Package not found when retrieving icon for ${type.packageName}",
)
@@ -153,7 +153,7 @@
packageManagerForUser.getApplicationInfo(type.packageName, /* flags= */ 0)
return packageManagerForUser.getApplicationLabel(currentAppInfo).toString()
} catch (e: NameNotFoundException) {
- Log.wtf(
+ Log.w(
"ShortcutHelperViewModel",
"Package Not found when retrieving Label for ${type.packageName}",
)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
index 32c2bc7..7097c1d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
@@ -662,7 +662,9 @@
trace("doKeyguardTimeout");
checkPermission();
- if (KeyguardWmStateRefactor.isEnabled()) {
+ if (SceneContainerFlag.isEnabled()) {
+ mDeviceEntryInteractorLazy.get().lockNow();
+ } else if (KeyguardWmStateRefactor.isEnabled()) {
mKeyguardLockWhileAwakeInteractor.onKeyguardServiceDoKeyguardTimeout(options);
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
index 2d05600..5ec6d37 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
@@ -95,7 +95,7 @@
private val keyguardBlueprintViewModel: KeyguardBlueprintViewModel,
private val keyguardStatusViewComponentFactory: KeyguardStatusViewComponent.Factory,
@ShadeDisplayAware private val configuration: ConfigurationState,
- private val context: Context,
+ @ShadeDisplayAware private val context: Context,
private val keyguardIndicationController: KeyguardIndicationController,
private val shadeInteractor: ShadeInteractor,
private val interactionJankMonitor: InteractionJankMonitor,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 2ee9ddb..01ec4d0 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -2875,7 +2875,7 @@
}
if (ENABLE_NEW_KEYGUARD_SHELL_TRANSITIONS) {
- mKeyguardTransitions.startKeyguardTransition(showing, aodShowing);
+ startKeyguardTransition(showing, aodShowing);
} else {
try {
@@ -3019,7 +3019,7 @@
final int keyguardFlag = flags;
mUiBgExecutor.execute(() -> {
if (ENABLE_NEW_KEYGUARD_SHELL_TRANSITIONS) {
- mKeyguardTransitions.startKeyguardTransition(
+ startKeyguardTransition(
false /* keyguardShowing */, false /* aodShowing */);
return;
}
@@ -3035,6 +3035,10 @@
}
};
+ private void startKeyguardTransition(boolean keyguardShowing, boolean aodShowing) {
+ mKeyguardTransitions.startKeyguardTransition(keyguardShowing, aodShowing);
+ }
+
private final Runnable mHideAnimationFinishedRunnable = () -> {
Log.e(TAG, "mHideAnimationFinishedRunnable#run");
mHideAnimationRunning = false;
@@ -3490,8 +3494,7 @@
mSurfaceBehindRemoteAnimationRequested = true;
if (ENABLE_NEW_KEYGUARD_SHELL_TRANSITIONS) {
- mKeyguardTransitions.startKeyguardTransition(
- false /* keyguardShowing */, false /* aodShowing */);
+ startKeyguardTransition(false /* keyguardShowing */, false /* aodShowing */);
return;
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/WakefulnessLifecycle.java b/packages/SystemUI/src/com/android/systemui/keyguard/WakefulnessLifecycle.java
index 39144b5..c0ffda6 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/WakefulnessLifecycle.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/WakefulnessLifecycle.java
@@ -34,6 +34,7 @@
import com.android.systemui.dump.DumpManager;
import com.android.systemui.power.domain.interactor.PowerInteractor;
import com.android.systemui.res.R;
+import com.android.systemui.shade.ShadeDisplayAware;
import com.android.systemui.util.time.SystemClock;
import java.io.PrintWriter;
@@ -91,7 +92,7 @@
@Inject
public WakefulnessLifecycle(
- Context context,
+ @ShadeDisplayAware Context context,
@Nullable IWallpaperManager wallpaperManagerService,
SystemClock systemClock,
DumpManager dumpManager) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt b/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt
index 2914cb9..a137d6c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt
@@ -122,10 +122,7 @@
if (visible) {
if (enableNewKeyguardShellTransitions) {
- keyguardTransitions.startKeyguardTransition(
- false /* keyguardShowing */,
- false, /* aodShowing */
- )
+ startKeyguardTransition(false, /* keyguardShowing */ false /* aodShowing */)
isKeyguardGoingAway = true
return
}
@@ -233,7 +230,7 @@
"aodVisible=$aodVisible).",
)
if (enableNewKeyguardShellTransitions) {
- keyguardTransitions.startKeyguardTransition(lockscreenShowing, aodVisible)
+ startKeyguardTransition(lockscreenShowing, aodVisible)
} else {
activityTaskManagerService.setLockScreenShown(lockscreenShowing, aodVisible)
}
@@ -241,6 +238,10 @@
this.isAodVisible = aodVisible
}
+ private fun startKeyguardTransition(keyguardShowing: Boolean, aodShowing: Boolean) {
+ keyguardTransitions.startKeyguardTransition(keyguardShowing, aodShowing)
+ }
+
private fun endKeyguardGoingAwayAnimation() {
if (!isKeyguardGoingAway) {
Log.d(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerOcclusionManager.kt b/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerOcclusionManager.kt
index 585bd6a..4bac8f7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerOcclusionManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerOcclusionManager.kt
@@ -46,6 +46,7 @@
import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.res.R
+import com.android.systemui.shade.ShadeDisplayAware
import java.util.concurrent.Executor
import javax.inject.Inject
@@ -83,7 +84,7 @@
val activityTransitionAnimator: ActivityTransitionAnimator,
val keyguardViewController: dagger.Lazy<KeyguardViewController>,
val powerInteractor: PowerInteractor,
- val context: Context,
+ @ShadeDisplayAware val context: Context,
val interactionJankMonitor: InteractionJankMonitor,
@Main executor: Executor,
val dreamingToLockscreenTransitionViewModel: DreamingToLockscreenTransitionViewModel,
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 7638079..0101e09 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
@@ -68,6 +68,7 @@
import com.android.systemui.process.ProcessWrapper;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.shade.ShadeController;
+import com.android.systemui.shade.ShadeDisplayAware;
import com.android.systemui.statusbar.NotificationShadeDepthController;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
@@ -126,7 +127,7 @@
@Provides
@SysUISingleton
static KeyguardViewMediator newKeyguardViewMediator(
- Context context,
+ @ShadeDisplayAware Context context,
UiEventLogger uiEventLogger,
SessionTracker sessionTracker,
UserTracker userTracker,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfig.kt
index 77e8179..74ee052 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfig.kt
@@ -27,9 +27,9 @@
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.settings.UserTracker
+import com.android.systemui.shade.ShadeDisplayAware
import dagger.Lazy
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
@@ -41,7 +41,7 @@
class CameraQuickAffordanceConfig
@Inject
constructor(
- @Application private val context: Context,
+ @ShadeDisplayAware private val context: Context,
private val packageManager: PackageManager,
private val cameraGestureHelper: Lazy<CameraGestureHelper>,
private val userTracker: UserTracker,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfig.kt
index be87334..d1f9fa2 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfig.kt
@@ -40,6 +40,7 @@
import com.android.systemui.modes.shared.ModesUi
import com.android.systemui.res.R
import com.android.systemui.settings.UserTracker
+import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.statusbar.policy.ZenModeController
import com.android.systemui.statusbar.policy.domain.interactor.ZenModeInteractor
import com.android.systemui.util.settings.SecureSettings
@@ -75,7 +76,7 @@
@Inject
constructor(
- context: Context,
+ @ShadeDisplayAware context: Context,
controller: ZenModeController,
interactor: ZenModeInteractor,
secureSettings: SecureSettings,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfig.kt
index a7999c1..480ef5e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfig.kt
@@ -27,6 +27,7 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.shared.quickaffordance.ActivationState
+import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.statusbar.policy.FlashlightController
import javax.inject.Inject
import kotlinx.coroutines.channels.awaitClose
@@ -36,7 +37,7 @@
class FlashlightQuickAffordanceConfig
@Inject
constructor(
- @Application private val context: Context,
+ @ShadeDisplayAware private val context: Context,
private val flashlightController: FlashlightController,
) : KeyguardQuickAffordanceConfig {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfig.kt
index cc36961..3555f06 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfig.kt
@@ -36,6 +36,7 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig.Companion.appStoreIntent
+import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.util.kotlin.getOrNull
import javax.inject.Inject
import kotlinx.coroutines.channels.awaitClose
@@ -48,7 +49,7 @@
class HomeControlsKeyguardQuickAffordanceConfig
@Inject
constructor(
- @Application private val context: Context,
+ @ShadeDisplayAware private val context: Context,
private val component: ControlsComponent,
) : KeyguardQuickAffordanceConfig {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManager.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManager.kt
index 796374a..f08576a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManager.kt
@@ -29,6 +29,7 @@
import com.android.systemui.res.R
import com.android.systemui.settings.UserFileManager
import com.android.systemui.settings.UserTracker
+import com.android.systemui.shade.ShadeDisplayAware
import javax.inject.Inject
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.channels.awaitClose
@@ -47,7 +48,7 @@
class KeyguardQuickAffordanceLocalUserSelectionManager
@Inject
constructor(
- @Application private val context: Context,
+ @ShadeDisplayAware private val context: Context,
private val userFileManager: UserFileManager,
private val userTracker: UserTracker,
broadcastDispatcher: BroadcastDispatcher,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfig.kt
index 6c1bdad..1358634 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfig.kt
@@ -33,6 +33,7 @@
import com.android.systemui.res.R
import com.android.systemui.settings.UserFileManager
import com.android.systemui.settings.UserTracker
+import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.util.RingerModeTracker
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
@@ -51,7 +52,7 @@
class MuteQuickAffordanceConfig
@Inject
constructor(
- private val context: Context,
+ @ShadeDisplayAware private val context: Context,
private val userTracker: UserTracker,
private val userFileManager: UserFileManager,
private val ringerModeTracker: RingerModeTracker,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfig.kt
index a503541..d12c42a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfig.kt
@@ -27,6 +27,7 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.qrcodescanner.controller.QRCodeScannerController
+import com.android.systemui.shade.ShadeDisplayAware
import javax.inject.Inject
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
@@ -36,7 +37,7 @@
class QrCodeScannerKeyguardQuickAffordanceConfig
@Inject
constructor(
- @Application private val context: Context,
+ @ShadeDisplayAware private val context: Context,
private val controller: QRCodeScannerController,
) : KeyguardQuickAffordanceConfig {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt
index 56b520e..eafa1ce 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt
@@ -34,6 +34,7 @@
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.res.R
+import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.wallet.controller.QuickAccessWalletController
import com.android.systemui.wallet.util.getPaymentCards
import javax.inject.Inject
@@ -51,7 +52,7 @@
class QuickAccessWalletKeyguardQuickAffordanceConfig
@Inject
constructor(
- @Application private val context: Context,
+ @ShadeDisplayAware private val context: Context,
@Background private val backgroundDispatcher: CoroutineDispatcher,
private val walletController: QuickAccessWalletController,
private val activityStarter: ActivityStarter,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/VideoCameraQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/VideoCameraQuickAffordanceConfig.kt
index 3e6e3b7..ceaeeca 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/VideoCameraQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/VideoCameraQuickAffordanceConfig.kt
@@ -32,6 +32,7 @@
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.settings.UserTracker
+import com.android.systemui.shade.ShadeDisplayAware
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.Flow
@@ -42,7 +43,7 @@
class VideoCameraQuickAffordanceConfig
@Inject
constructor(
- @Application private val context: Context,
+ @ShadeDisplayAware private val context: Context,
private val cameraIntents: CameraIntentsWrapper,
private val activityIntentHelper: ActivityIntentHelper,
private val userTracker: UserTracker,
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 dd3e619..ab8cc71 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
@@ -40,6 +40,7 @@
import com.android.systemui.keyguard.shared.model.AuthenticationFlags
import com.android.systemui.keyguard.shared.model.DevicePosture
import com.android.systemui.res.R
+import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionsRepository
import com.android.systemui.user.data.repository.UserRepository
import java.io.PrintWriter
@@ -123,7 +124,7 @@
class BiometricSettingsRepositoryImpl
@Inject
constructor(
- context: Context,
+ @ShadeDisplayAware context: Context,
lockPatternUtils: LockPatternUtils,
broadcastDispatcher: BroadcastDispatcher,
authController: AuthController,
@@ -354,7 +355,10 @@
}
@OptIn(ExperimentalCoroutinesApi::class)
-private class StrongAuthTracker(private val userRepository: UserRepository, context: Context?) :
+private class StrongAuthTracker(
+ private val userRepository: UserRepository,
+ @ShadeDisplayAware context: Context?
+) :
LockPatternUtils.StrongAuthTracker(context) {
private val selectedUserId =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardClockRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardClockRepository.kt
index 95d1b5d..9718e75 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardClockRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardClockRepository.kt
@@ -31,6 +31,7 @@
import com.android.systemui.plugins.clocks.ClockId
import com.android.systemui.res.R
import com.android.systemui.scene.shared.flag.SceneContainerFlag
+import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.shared.clocks.ClockRegistry
import com.android.systemui.util.settings.SecureSettings
import com.android.systemui.util.settings.SettingsProxyExt.observerFlow
@@ -89,7 +90,7 @@
override val clockEventController: ClockEventController,
@Background private val backgroundDispatcher: CoroutineDispatcher,
@Application private val applicationScope: CoroutineScope,
- @Application private val applicationContext: Context,
+ @ShadeDisplayAware private val context: Context,
private val featureFlags: FeatureFlagsClassic,
) : KeyguardClockRepository {
@@ -143,7 +144,7 @@
.stateIn(
scope = applicationScope,
started = SharingStarted.WhileSubscribed(),
- initialValue = clockRegistry.createCurrentClock(),
+ initialValue = null,
)
override val previewClock: Flow<ClockController> =
@@ -166,7 +167,7 @@
get() =
featureFlags.isEnabled(Flags.LOCKSCREEN_ENABLE_LANDSCAPE) &&
// True on small landscape screens
- applicationContext.resources.getBoolean(R.bool.force_small_clock_on_lockscreen)
+ context.resources.getBoolean(R.bool.force_small_clock_on_lockscreen)
private fun getClockSize(): ClockSizeSetting {
return ClockSizeSetting.fromSettingValue(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepository.kt
index d0de21b..c1ec88b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepository.kt
@@ -36,6 +36,7 @@
import com.android.systemui.keyguard.shared.model.KeyguardSlotPickerRepresentation
import com.android.systemui.res.R
import com.android.systemui.settings.UserTracker
+import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.util.kotlin.FlowDumperImpl
import java.io.PrintWriter
import javax.inject.Inject
@@ -57,7 +58,7 @@
class KeyguardQuickAffordanceRepository
@Inject
constructor(
- @Application private val appContext: Context,
+ @ShadeDisplayAware private val appContext: Context,
@Application private val scope: CoroutineScope,
private val localUserSelectionManager: KeyguardQuickAffordanceLocalUserSelectionManager,
private val remoteUserSelectionManager: KeyguardQuickAffordanceRemoteUserSelectionManager,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardSmartspaceRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardSmartspaceRepository.kt
index b67fd4b..549a508 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardSmartspaceRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardSmartspaceRepository.kt
@@ -16,7 +16,6 @@
package com.android.systemui.keyguard.data.repository
-import android.content.Context
import android.os.UserHandle
import android.provider.Settings
import android.view.View
@@ -46,7 +45,6 @@
class KeyguardSmartspaceRepositoryImpl
@Inject
constructor(
- context: Context,
private val secureSettings: SecureSettings,
private val userTracker: UserTracker,
@Application private val applicationScope: CoroutineScope,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepository.kt
index 4c9c282..4f6319a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepository.kt
@@ -30,6 +30,7 @@
import com.android.systemui.power.shared.model.WakeSleepReason
import com.android.systemui.power.shared.model.WakeSleepReason.TAP
import com.android.systemui.res.R
+import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.statusbar.CircleReveal
import com.android.systemui.statusbar.LiftReveal
import com.android.systemui.statusbar.LightRevealEffect
@@ -77,7 +78,7 @@
@Inject
constructor(
keyguardRepository: KeyguardRepository,
- val context: Context,
+ @ShadeDisplayAware val context: Context,
powerRepository: PowerRepository,
private val scrimLogger: ScrimLogger,
) : LightRevealScrimRepository {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BurnInInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BurnInInteractor.kt
index 73a4cc3..68ec4f3 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BurnInInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BurnInInteractor.kt
@@ -45,7 +45,7 @@
class BurnInInteractor
@Inject
constructor(
- private val context: Context,
+ @ShadeDisplayAware private val context: Context,
private val burnInHelperWrapper: BurnInHelperWrapper,
@Application private val scope: CoroutineScope,
@ShadeDisplayAware private val configurationInteractor: ConfigurationInteractor,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractor.kt
index 03cf1a4..6367b5c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractor.kt
@@ -28,6 +28,7 @@
import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.shade.ShadeDisplayAware
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
@@ -47,7 +48,7 @@
class DeviceEntrySideFpsOverlayInteractor
@Inject
constructor(
- @Application private val context: Context,
+ @ShadeDisplayAware private val context: Context,
deviceEntryFingerprintAuthRepository: DeviceEntryFingerprintAuthRepository,
private val sceneInteractor: SceneInteractor,
private val primaryBouncerInteractor: PrimaryBouncerInteractor,
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 d4d7e75..2d81be6 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
@@ -28,6 +28,7 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.shade.ShadeController
+import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
import javax.inject.Inject
@@ -39,7 +40,7 @@
class KeyguardKeyEventInteractor
@Inject
constructor(
- private val context: Context,
+ @ShadeDisplayAware private val context: Context,
private val statusBarStateController: StatusBarStateController,
private val statusBarKeyguardViewManager: StatusBarKeyguardViewManager,
private val shadeController: ShadeController,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
index 8c9473f..ae55825 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
@@ -51,6 +51,7 @@
import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.settings.UserTracker
+import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.shared.customization.data.content.CustomizationProviderContract as Contract
import com.android.systemui.shared.quickaffordance.shared.model.KeyguardPreviewConstants.KEYGUARD_QUICK_AFFORDANCE_ID_NONE
@@ -90,7 +91,7 @@
private val dockManager: DockManager,
private val biometricSettingsRepository: BiometricSettingsRepository,
@Background private val backgroundDispatcher: CoroutineDispatcher,
- @Application private val appContext: Context,
+ @ShadeDisplayAware private val appContext: Context,
private val sceneInteractor: Lazy<SceneInteractor>,
) {
/**
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardSurfaceBehindInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardSurfaceBehindInteractor.kt
index 377a03e..b9784f1 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardSurfaceBehindInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardSurfaceBehindInteractor.kt
@@ -23,6 +23,7 @@
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.KeyguardSurfaceBehindModel
import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.statusbar.notification.domain.interactor.NotificationLaunchAnimationInteractor
import com.android.systemui.util.kotlin.sample
import com.android.systemui.util.kotlin.toPx
@@ -45,7 +46,7 @@
@Inject
constructor(
private val repository: KeyguardSurfaceBehindRepository,
- context: Context,
+ @ShadeDisplayAware context: Context,
transitionInteractor: KeyguardTransitionInteractor,
inWindowLauncherUnlockAnimationInteractor: Lazy<InWindowLauncherUnlockAnimationInteractor>,
swipeToDismissInteractor: SwipeToDismissInteractor,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTouchHandlingInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTouchHandlingInteractor.kt
index b2031d3..274a1dd 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTouchHandlingInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTouchHandlingInteractor.kt
@@ -34,6 +34,7 @@
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.res.R
import com.android.systemui.shade.PulsingGestureListener
+import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
@@ -58,7 +59,7 @@
class KeyguardTouchHandlingInteractor
@Inject
constructor(
- @Application private val appContext: Context,
+ @ShadeDisplayAware private val context: Context,
@Application private val scope: CoroutineScope,
transitionInteractor: KeyguardTransitionInteractor,
repository: KeyguardRepository,
@@ -188,7 +189,7 @@
private fun isFeatureEnabled(): Boolean {
return featureFlags.isEnabled(Flags.LOCK_SCREEN_LONG_PRESS_ENABLED) &&
- appContext.resources.getBoolean(R.bool.long_press_keyguard_customize_lockscreen_enabled)
+ context.resources.getBoolean(R.bool.long_press_keyguard_customize_lockscreen_enabled)
}
/** Updates application state to ask to show the menu. */
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractor.kt
index 9c98a96..fbc7e2a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractor.kt
@@ -38,6 +38,7 @@
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.power.shared.model.WakeSleepReason
import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.systemui.util.kotlin.sample
import com.android.systemui.util.settings.SecureSettings
@@ -75,7 +76,7 @@
@Inject
constructor(
@Application private val scope: CoroutineScope,
- private val context: Context,
+ @ShadeDisplayAware private val context: Context,
private val repository: KeyguardRepository,
private val systemClock: SystemClock,
private val alarmManager: AlarmManager,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/NaturalScrollingSettingObserver.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/NaturalScrollingSettingObserver.kt
index 508fb59..7e77423 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/NaturalScrollingSettingObserver.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/NaturalScrollingSettingObserver.kt
@@ -24,6 +24,7 @@
import android.provider.Settings.SettingNotFoundException
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.shade.ShadeDisplayAware
import javax.inject.Inject
@SysUISingleton
@@ -31,7 +32,7 @@
@Inject
constructor(
@Main private val handler: Handler,
- private val context: Context,
+ @ShadeDisplayAware private val context: Context,
) {
var isNaturalScrollingEnabled = true
get() {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/StatusBarDisableFlagsInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/StatusBarDisableFlagsInteractor.kt
index 73e80ff..d6a110a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/StatusBarDisableFlagsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/StatusBarDisableFlagsInteractor.kt
@@ -40,6 +40,7 @@
import com.android.systemui.power.shared.model.WakeSleepReason
import com.android.systemui.power.shared.model.WakefulnessModel
import com.android.systemui.scene.shared.flag.SceneContainerFlag
+import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
@@ -62,7 +63,7 @@
@Inject
constructor(
@Application private val scope: CoroutineScope,
- @Application private val applicationContext: Context,
+ @ShadeDisplayAware private val context: Context,
@Background private val backgroundDispatcher: CoroutineDispatcher,
private val deviceEntryFaceAuthInteractor: DeviceEntryFaceAuthInteractor,
private val statusBarService: IStatusBarService,
@@ -142,7 +143,7 @@
scope.launch {
disableFlagsForUserId.collect { (selectedUserId, flags) ->
- if (applicationContext.getSystemService(Context.STATUS_BAR_SERVICE) == null) {
+ if (context.getSystemService(Context.STATUS_BAR_SERVICE) == null) {
return@collect
}
@@ -151,7 +152,7 @@
statusBarService.disableForUser(
flags,
disableToken,
- applicationContext.packageName,
+ context.packageName,
selectedUserId,
)
} catch (e: RemoteException) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/SwipeUpAnywhereGestureHandler.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/SwipeUpAnywhereGestureHandler.kt
index 3540a0c..b6aa209 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/SwipeUpAnywhereGestureHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/SwipeUpAnywhereGestureHandler.kt
@@ -20,6 +20,7 @@
import android.view.MotionEvent
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.settings.DisplayTracker
+import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.statusbar.gesture.SwipeUpGestureHandler
import com.android.systemui.statusbar.gesture.SwipeUpGestureLogger
import javax.inject.Inject
@@ -29,7 +30,7 @@
class SwipeUpAnywhereGestureHandler
@Inject
constructor(
- context: Context,
+ @ShadeDisplayAware context: Context,
displayTracker: DisplayTracker,
logger: SwipeUpGestureLogger,
) :
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewClockViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewClockViewBinder.kt
index 914fdd2..6c03b24 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewClockViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewClockViewBinder.kt
@@ -151,7 +151,5 @@
cs.applyTo(rootView)
}
- private const val DATE_WEATHER_VIEW_HEIGHT = "date_weather_view_height"
- private const val ENHANCED_SMARTSPACE_HEIGHT = "enhanced_smartspace_height"
private const val TAG = "KeyguardPreviewClockViewBinder"
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AccessibilityActionsSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AccessibilityActionsSection.kt
index 717a898..587a8fe 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AccessibilityActionsSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AccessibilityActionsSection.kt
@@ -26,6 +26,7 @@
import com.android.systemui.keyguard.ui.binder.AccessibilityActionsViewBinder
import com.android.systemui.keyguard.ui.viewmodel.AccessibilityActionsViewModel
import com.android.systemui.res.R
+import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.util.Utils
import javax.inject.Inject
import kotlinx.coroutines.DisposableHandle
@@ -37,7 +38,7 @@
class AccessibilityActionsSection
@Inject
constructor(
- private val context: Context,
+ @ShadeDisplayAware private val context: Context,
private val communalSettingsInteractor: CommunalSettingsInteractor,
private val accessibilityActionsViewModel: AccessibilityActionsViewModel,
) : KeyguardSection() {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodBurnInSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodBurnInSection.kt
index d639978..8622ffc 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodBurnInSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodBurnInSection.kt
@@ -28,13 +28,14 @@
import com.android.systemui.keyguard.ui.view.KeyguardRootView
import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel
import com.android.systemui.res.R
+import com.android.systemui.shade.ShadeDisplayAware
import javax.inject.Inject
/** Adds a layer to group elements for translation for burn-in preventation */
class AodBurnInSection
@Inject
constructor(
- private val context: Context,
+ @ShadeDisplayAware private val context: Context,
private val rootView: KeyguardRootView,
private val clockViewModel: KeyguardClockViewModel,
) : KeyguardSection() {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodNotificationIconsSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodNotificationIconsSection.kt
index faa4978..4c23adf 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodNotificationIconsSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodNotificationIconsSection.kt
@@ -29,6 +29,7 @@
import androidx.constraintlayout.widget.ConstraintSet.START
import androidx.constraintlayout.widget.ConstraintSet.TOP
import com.android.systemui.common.ui.ConfigurationState
+import com.android.systemui.customization.R as customR
import com.android.systemui.keyguard.MigrateClocksToBlueprint
import com.android.systemui.keyguard.shared.model.KeyguardSection
import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
@@ -47,7 +48,7 @@
class AodNotificationIconsSection
@Inject
constructor(
- private val context: Context,
+ @ShadeDisplayAware private val context: Context,
@ShadeDisplayAware private val configurationState: ConfigurationState,
private val iconBindingFailureTracker: StatusBarIconViewBindingFailureTracker,
private val nicAodViewModel: NotificationIconContainerAlwaysOnDisplayViewModel,
@@ -114,14 +115,14 @@
START,
PARENT_ID,
START,
- context.resources.getDimensionPixelSize(R.dimen.status_view_margin_horizontal),
+ context.resources.getDimensionPixelSize(customR.dimen.status_view_margin_horizontal),
)
connect(
nicId,
END,
PARENT_ID,
END,
- context.resources.getDimensionPixelSize(R.dimen.status_view_margin_horizontal),
+ context.resources.getDimensionPixelSize(customR.dimen.status_view_margin_horizontal),
)
constrainHeight(
nicId,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt
index 6096cf7..6c98d5b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt
@@ -45,6 +45,7 @@
import com.android.systemui.plugins.clocks.ClockFaceLayout
import com.android.systemui.res.R
import com.android.systemui.shade.LargeScreenHeaderHelper
+import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.shared.R as sharedR
import com.android.systemui.util.ui.value
import dagger.Lazy
@@ -69,7 +70,7 @@
constructor(
private val clockInteractor: KeyguardClockInteractor,
protected val keyguardClockViewModel: KeyguardClockViewModel,
- private val context: Context,
+ @ShadeDisplayAware private val context: Context,
val smartspaceViewModel: KeyguardSmartspaceViewModel,
val blueprintInteractor: Lazy<KeyguardBlueprintInteractor>,
private val rootViewModel: KeyguardRootViewModel,
@@ -220,7 +221,9 @@
PARENT_ID,
START,
context.resources.getDimensionPixelSize(customR.dimen.clock_padding_start) +
- context.resources.getDimensionPixelSize(R.dimen.status_view_margin_horizontal),
+ context.resources.getDimensionPixelSize(
+ customR.dimen.status_view_margin_horizontal
+ ),
)
val smallClockTopMargin = keyguardClockViewModel.getSmallClockTopMargin()
create(R.id.small_clock_guideline_top, ConstraintSet.HORIZONTAL_GUIDELINE)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt
index 8d2bfb5..aa7eb29 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt
@@ -28,6 +28,7 @@
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
import com.android.systemui.biometrics.AuthController
+import com.android.systemui.customization.R as customR
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
@@ -45,6 +46,7 @@
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.res.R
import com.android.systemui.shade.NotificationPanelView
+import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.statusbar.VibratorHelper
import dagger.Lazy
import javax.inject.Inject
@@ -60,7 +62,7 @@
@Application private val applicationScope: CoroutineScope,
private val authController: AuthController,
private val windowManager: WindowManager,
- private val context: Context,
+ @ShadeDisplayAware private val context: Context,
private val notificationPanelView: NotificationPanelView,
private val featureFlags: FeatureFlags,
private val deviceEntryIconViewModel: Lazy<DeviceEntryIconViewModel>,
@@ -114,7 +116,7 @@
val scaleFactor: Float = authController.scaleFactor
val mBottomPaddingPx =
- context.resources.getDimensionPixelSize(R.dimen.lock_icon_margin_bottom)
+ context.resources.getDimensionPixelSize(customR.dimen.lock_icon_margin_bottom)
val bounds = windowManager.currentWindowMetrics.bounds
var widthPixels = bounds.right.toFloat()
if (featureFlags.isEnabled(Flags.LOCKSCREEN_ENABLE_LANDSCAPE)) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSection.kt
index af0528a..2d9dac4 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSection.kt
@@ -27,6 +27,7 @@
import com.android.systemui.keyguard.ui.view.KeyguardIndicationArea
import com.android.systemui.keyguard.ui.viewmodel.KeyguardIndicationAreaViewModel
import com.android.systemui.res.R
+import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.statusbar.KeyguardIndicationController
import javax.inject.Inject
import kotlinx.coroutines.DisposableHandle
@@ -34,7 +35,7 @@
class DefaultIndicationAreaSection
@Inject
constructor(
- private val context: Context,
+ @ShadeDisplayAware private val context: Context,
private val keyguardIndicationAreaViewModel: KeyguardIndicationAreaViewModel,
private val indicationController: KeyguardIndicationController,
) : KeyguardSection() {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultNotificationStackScrollLayoutSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultNotificationStackScrollLayoutSection.kt
index 6ac33af..3a791fd 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultNotificationStackScrollLayoutSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultNotificationStackScrollLayoutSection.kt
@@ -28,6 +28,7 @@
import com.android.systemui.res.R
import com.android.systemui.shade.LargeScreenHeaderHelper
import com.android.systemui.shade.NotificationPanelView
+import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer
import com.android.systemui.statusbar.notification.stack.ui.viewbinder.SharedNotificationContainerBinder
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.SharedNotificationContainerViewModel
@@ -38,7 +39,7 @@
class DefaultNotificationStackScrollLayoutSection
@Inject
constructor(
- context: Context,
+ @ShadeDisplayAware context: Context,
notificationPanelView: NotificationPanelView,
sharedNotificationContainer: SharedNotificationContainer,
sharedNotificationContainerViewModel: SharedNotificationContainerViewModel,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultStatusBarSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultStatusBarSection.kt
index 9b5fae3..9d9be44 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultStatusBarSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultStatusBarSection.kt
@@ -31,6 +31,7 @@
import com.android.systemui.res.R
import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.shade.NotificationPanelView
+import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.shade.ShadeViewStateProvider
import com.android.systemui.statusbar.phone.KeyguardStatusBarView
import com.android.systemui.util.Utils
@@ -40,7 +41,7 @@
class DefaultStatusBarSection
@Inject
constructor(
- private val context: Context,
+ @ShadeDisplayAware private val context: Context,
private val notificationPanelView: NotificationPanelView,
private val keyguardStatusBarViewComponentFactory: KeyguardStatusBarViewComponent.Factory,
) : KeyguardSection() {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultStatusViewSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultStatusViewSection.kt
index 45641db..57ea1ad 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultStatusViewSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultStatusViewSection.kt
@@ -38,6 +38,7 @@
import com.android.systemui.res.R
import com.android.systemui.shade.NotificationPanelView
import com.android.systemui.shade.NotificationPanelViewController
+import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.statusbar.policy.SplitShadeStateController
import com.android.systemui.util.Utils
import dagger.Lazy
@@ -47,7 +48,7 @@
class DefaultStatusViewSection
@Inject
constructor(
- private val context: Context,
+ @ShadeDisplayAware private val context: Context,
private val notificationPanelView: NotificationPanelView,
private val keyguardStatusViewComponentFactory: KeyguardStatusViewComponent.Factory,
private val keyguardViewConfigurator: Lazy<KeyguardViewConfigurator>,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultUdfpsAccessibilityOverlaySection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultUdfpsAccessibilityOverlaySection.kt
index 2abb7ba..0ae1400 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultUdfpsAccessibilityOverlaySection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultUdfpsAccessibilityOverlaySection.kt
@@ -26,6 +26,7 @@
import com.android.systemui.keyguard.KeyguardBottomAreaRefactor
import com.android.systemui.keyguard.shared.model.KeyguardSection
import com.android.systemui.res.R
+import com.android.systemui.shade.ShadeDisplayAware
import javax.inject.Inject
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -34,7 +35,7 @@
class DefaultUdfpsAccessibilityOverlaySection
@Inject
constructor(
- private val context: Context,
+ @ShadeDisplayAware private val context: Context,
private val viewModel: DeviceEntryUdfpsAccessibilityOverlayViewModel,
) : KeyguardSection() {
private val viewId = R.id.udfps_accessibility_overlay
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt
index 6ddcae3..d54d411 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt
@@ -33,6 +33,7 @@
import com.android.systemui.keyguard.ui.binder.KeyguardSmartspaceViewBinder
import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardSmartspaceViewModel
+import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.res.R as R
import com.android.systemui.shared.R as sharedR
import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController
@@ -44,7 +45,7 @@
open class SmartspaceSection
@Inject
constructor(
- val context: Context,
+ @ShadeDisplayAware val context: Context,
val keyguardClockViewModel: KeyguardClockViewModel,
val keyguardSmartspaceViewModel: KeyguardSmartspaceViewModel,
private val keyguardSmartspaceInteractor: KeyguardSmartspaceInteractor,
@@ -123,7 +124,7 @@
ConstraintSet.START,
ConstraintSet.PARENT_ID,
ConstraintSet.START,
- horizontalPaddingStart
+ horizontalPaddingStart,
)
// migrate addSmartspaceView from KeyguardClockSwitchController
@@ -134,15 +135,15 @@
ConstraintSet.START,
ConstraintSet.PARENT_ID,
ConstraintSet.START,
- horizontalPaddingStart
+ horizontalPaddingStart,
)
connect(
sharedR.id.bc_smartspace_view,
ConstraintSet.END,
- if (keyguardClockViewModel.clockShouldBeCentered.value) ConstraintSet.PARENT_ID
- else R.id.split_shade_guideline,
+ if (keyguardSmartspaceViewModel.isShadeLayoutWide.value) R.id.split_shade_guideline
+ else ConstraintSet.PARENT_ID,
ConstraintSet.END,
- horizontalPaddingEnd
+ horizontalPaddingEnd,
)
if (keyguardClockViewModel.hasCustomWeatherDataDisplay.value) {
@@ -151,7 +152,7 @@
sharedR.id.date_smartspace_view,
ConstraintSet.BOTTOM,
sharedR.id.bc_smartspace_view,
- ConstraintSet.TOP
+ ConstraintSet.TOP,
)
} else {
clear(sharedR.id.date_smartspace_view, ConstraintSet.BOTTOM)
@@ -159,13 +160,13 @@
sharedR.id.date_smartspace_view,
ConstraintSet.TOP,
customR.id.lockscreen_clock_view,
- ConstraintSet.BOTTOM
+ ConstraintSet.BOTTOM,
)
connect(
sharedR.id.bc_smartspace_view,
ConstraintSet.TOP,
sharedR.id.date_smartspace_view,
- ConstraintSet.BOTTOM
+ ConstraintSet.BOTTOM,
)
}
@@ -173,10 +174,7 @@
R.id.smart_space_barrier_bottom,
Barrier.BOTTOM,
0,
- *intArrayOf(
- sharedR.id.bc_smartspace_view,
- sharedR.id.date_smartspace_view,
- )
+ *intArrayOf(sharedR.id.bc_smartspace_view, sharedR.id.date_smartspace_view),
)
}
updateVisibility(constraintSet)
@@ -211,7 +209,7 @@
setVisibility(sharedR.id.weather_smartspace_view, weatherVisibility)
setAlpha(
sharedR.id.weather_smartspace_view,
- if (weatherVisibility == View.VISIBLE) 1f else 0f
+ if (weatherVisibility == View.VISIBLE) 1f else 0f,
)
val dateVisibility =
if (keyguardClockViewModel.hasCustomWeatherDataDisplay.value) ConstraintSet.GONE
@@ -219,7 +217,7 @@
setVisibility(sharedR.id.date_smartspace_view, dateVisibility)
setAlpha(
sharedR.id.date_smartspace_view,
- if (dateVisibility == ConstraintSet.VISIBLE) 1f else 0f
+ if (dateVisibility == ConstraintSet.VISIBLE) 1f else 0f,
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeMediaSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeMediaSection.kt
index 5dbba75..f0d21f2 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeMediaSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeMediaSection.kt
@@ -28,20 +28,22 @@
import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
import androidx.constraintlayout.widget.ConstraintSet.START
import androidx.constraintlayout.widget.ConstraintSet.TOP
+import com.android.systemui.customization.R as customR
import com.android.systemui.keyguard.MigrateClocksToBlueprint
import com.android.systemui.keyguard.shared.model.KeyguardSection
import com.android.systemui.media.controls.ui.controller.KeyguardMediaController
import com.android.systemui.res.R
import com.android.systemui.shade.NotificationPanelView
+import com.android.systemui.shade.ShadeDisplayAware
import javax.inject.Inject
/** Aligns media on left side for split shade, below smartspace, date, and weather. */
class SplitShadeMediaSection
@Inject
constructor(
- private val context: Context,
+ @ShadeDisplayAware private val context: Context,
private val notificationPanelView: NotificationPanelView,
- private val keyguardMediaController: KeyguardMediaController
+ private val keyguardMediaController: KeyguardMediaController,
) : KeyguardSection() {
private val mediaContainerId = R.id.status_view_media_container
@@ -61,7 +63,7 @@
val horizontalPadding =
padding +
context.resources.getDimensionPixelSize(
- R.dimen.status_view_margin_horizontal
+ customR.dimen.status_view_margin_horizontal
)
setPaddingRelative(horizontalPadding, padding, horizontalPadding, padding)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeNotificationStackScrollLayoutSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeNotificationStackScrollLayoutSection.kt
index 1a73866..729759a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeNotificationStackScrollLayoutSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeNotificationStackScrollLayoutSection.kt
@@ -26,6 +26,7 @@
import com.android.systemui.keyguard.MigrateClocksToBlueprint
import com.android.systemui.res.R
import com.android.systemui.shade.NotificationPanelView
+import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer
import com.android.systemui.statusbar.notification.stack.ui.viewbinder.SharedNotificationContainerBinder
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.SharedNotificationContainerViewModel
@@ -35,7 +36,7 @@
class SplitShadeNotificationStackScrollLayoutSection
@Inject
constructor(
- context: Context,
+ @ShadeDisplayAware context: Context,
notificationPanelView: NotificationPanelView,
sharedNotificationContainer: SharedNotificationContainer,
sharedNotificationContainerViewModel: SharedNotificationContainerViewModel,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerUdfpsIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerUdfpsIconViewModel.kt
index 56e3125..3a5263f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerUdfpsIconViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerUdfpsIconViewModel.kt
@@ -41,7 +41,7 @@
class AlternateBouncerUdfpsIconViewModel
@Inject
constructor(
- val context: Context,
+ @ShadeDisplayAware val context: Context,
@ShadeDisplayAware configurationInteractor: ConfigurationInteractor,
deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor,
deviceEntryBackgroundViewModel: DeviceEntryBackgroundViewModel,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModel.kt
index 12f9467..29ae4b9 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModel.kt
@@ -40,7 +40,7 @@
class DeviceEntryBackgroundViewModel
@Inject
constructor(
- val context: Context,
+ @ShadeDisplayAware val context: Context,
val deviceEntryIconViewModel: DeviceEntryIconViewModel,
keyguardTransitionInteractor: KeyguardTransitionInteractor,
@ShadeDisplayAware configurationInteractor: ConfigurationInteractor,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModel.kt
index 749f193..1965252 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModel.kt
@@ -31,8 +31,10 @@
import javax.inject.Inject
import kotlin.math.roundToInt
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
@@ -40,12 +42,13 @@
import kotlinx.coroutines.flow.onStart
/** Models the UI state for the device entry icon foreground view (displayed icon). */
+@OptIn(FlowPreview::class)
@ExperimentalCoroutinesApi
@SysUISingleton
class DeviceEntryForegroundViewModel
@Inject
constructor(
- val context: Context,
+ @ShadeDisplayAware val context: Context,
@ShadeDisplayAware configurationInteractor: ConfigurationInteractor,
deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor,
transitionInteractor: KeyguardTransitionInteractor,
@@ -97,7 +100,7 @@
private val padding: Flow<Int> =
deviceEntryUdfpsInteractor.isUdfpsSupported.flatMapLatest { udfpsSupported ->
if (udfpsSupported) {
- udfpsOverlayInteractor.iconPadding
+ udfpsOverlayInteractor.iconPadding.debounce(udfpsPaddingDebounceDuration.toLong())
} else {
configurationInteractor.scaleForResolution.map { scale ->
(context.resources.getDimensionPixelSize(R.dimen.lock_icon_padding) * scale)
@@ -120,6 +123,9 @@
)
}
+ private val udfpsPaddingDebounceDuration: Int
+ get() = context.resources.getInteger(R.integer.udfps_padding_debounce_duration)
+
data class ForegroundIconViewModel(
val type: DeviceEntryIconView.IconType,
val useAodVariant: Boolean,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardSmartspaceViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardSmartspaceViewModel.kt
index e30ddc6..3266dc4 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardSmartspaceViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardSmartspaceViewModel.kt
@@ -17,10 +17,12 @@
package com.android.systemui.keyguard.ui.viewmodel
import android.content.Context
+import com.android.systemui.customization.R as customR
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.domain.interactor.KeyguardSmartspaceInteractor
import com.android.systemui.res.R
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
@@ -38,6 +40,7 @@
smartspaceController: LockscreenSmartspaceController,
keyguardClockViewModel: KeyguardClockViewModel,
smartspaceInteractor: KeyguardSmartspaceInteractor,
+ shadeInteractor: ShadeInteractor,
) {
/** Whether the smartspace section is available in the build. */
val isSmartspaceEnabled: Boolean = smartspaceController.isEnabled
@@ -59,10 +62,9 @@
/** Whether the weather area should be visible. */
val isWeatherVisible: StateFlow<Boolean> =
- combine(
+ combine(isWeatherEnabled, keyguardClockViewModel.hasCustomWeatherDataDisplay) {
isWeatherEnabled,
- keyguardClockViewModel.hasCustomWeatherDataDisplay,
- ) { isWeatherEnabled, clockIncludesCustomWeatherDisplay ->
+ clockIncludesCustomWeatherDisplay ->
isWeatherVisible(
clockIncludesCustomWeatherDisplay = clockIncludesCustomWeatherDisplay,
isWeatherEnabled = isWeatherEnabled,
@@ -76,7 +78,7 @@
clockIncludesCustomWeatherDisplay =
keyguardClockViewModel.hasCustomWeatherDataDisplay.value,
isWeatherEnabled = smartspaceInteractor.isWeatherEnabled.value,
- )
+ ),
)
private fun isWeatherVisible(
@@ -89,15 +91,17 @@
/* trigger clock and smartspace constraints change when smartspace appears */
val bcSmartspaceVisibility: StateFlow<Int> = smartspaceInteractor.bcSmartspaceVisibility
+ val isShadeLayoutWide: StateFlow<Boolean> = shadeInteractor.isShadeLayoutWide
+
companion object {
fun getSmartspaceStartMargin(context: Context): Int {
return context.resources.getDimensionPixelSize(R.dimen.below_clock_padding_start) +
- context.resources.getDimensionPixelSize(R.dimen.status_view_margin_horizontal)
+ context.resources.getDimensionPixelSize(customR.dimen.status_view_margin_horizontal)
}
fun getSmartspaceEndMargin(context: Context): Int {
return context.resources.getDimensionPixelSize(R.dimen.below_clock_padding_end) +
- context.resources.getDimensionPixelSize(R.dimen.status_view_margin_horizontal)
+ context.resources.getDimensionPixelSize(customR.dimen.status_view_margin_horizontal)
}
}
}
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
index da96f3f..3de1f1e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/SideFpsProgressBarViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/SideFpsProgressBarViewModel.kt
@@ -40,6 +40,7 @@
import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.res.R
+import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.statusbar.phone.DozeServiceHost
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
@@ -66,7 +67,7 @@
class SideFpsProgressBarViewModel
@Inject
constructor(
- private val context: Context,
+ @ShadeDisplayAware private val context: Context,
biometricStatusInteractor: BiometricStatusInteractor,
deviceEntryFingerprintAuthInteractor: DeviceEntryFingerprintAuthInteractor,
private val sfpsSensorInteractor: SideFpsSensorInteractor,
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
index 4c21da5..8097d95 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
@@ -32,8 +32,6 @@
import com.android.systemui.qs.QSFragmentLegacy;
import com.android.systemui.qs.pipeline.shared.QSPipelineFlagsRepository;
import com.android.systemui.qs.pipeline.shared.TileSpec;
-import com.android.systemui.statusbar.notification.NotifPipelineFlags;
-import com.android.systemui.util.Compile;
import com.android.systemui.util.wakelock.WakeLockLog;
import dagger.Lazy;
@@ -56,61 +54,6 @@
return factory.create("DozeLog", 150);
}
- /** Provides a logging buffer for all logs related to the data layer of notifications. */
- @Provides
- @SysUISingleton
- @NotificationLog
- public static LogBuffer provideNotificationsLogBuffer(
- LogBufferFactory factory,
- NotifPipelineFlags notifPipelineFlags) {
- int maxSize = 1000;
- if (Compile.IS_DEBUG && notifPipelineFlags.isDevLoggingEnabled()) {
- maxSize *= 10;
- }
- return factory.create("NotifLog", maxSize, Compile.IS_DEBUG /* systrace */);
- }
-
- /** Provides a logging buffer for all logs related to notifications on the lockscreen. */
- @Provides
- @SysUISingleton
- @NotificationLockscreenLog
- public static LogBuffer provideNotificationLockScreenLogBuffer(
- LogBufferFactory factory) {
- return factory.create("NotifLockscreenLog", 50, false /* systrace */);
- }
-
- /** Provides a logging buffer for logs related to heads up presentation of notifications. */
- @Provides
- @SysUISingleton
- @NotificationHeadsUpLog
- public static LogBuffer provideNotificationHeadsUpLogBuffer(LogBufferFactory factory) {
- return factory.create("NotifHeadsUpLog", 1000);
- }
-
- /** Provides a logging buffer for logs related to inflation of notifications. */
- @Provides
- @SysUISingleton
- @NotifInflationLog
- public static LogBuffer provideNotifInflationLogBuffer(LogBufferFactory factory) {
- return factory.create("NotifInflationLog", 250);
- }
-
- /** Provides a logging buffer for notification interruption calculations. */
- @Provides
- @SysUISingleton
- @NotificationInterruptLog
- public static LogBuffer provideNotificationInterruptLogBuffer(LogBufferFactory factory) {
- return factory.create("NotifInterruptLog", 100);
- }
-
- /** Provides a logging buffer for notification rendering events. */
- @Provides
- @SysUISingleton
- @NotificationRenderLog
- public static LogBuffer provideNotificationRenderLogBuffer(LogBufferFactory factory) {
- return factory.create("NotifRenderLog", 100);
- }
-
/** Provides a logging buffer for all logs for lockscreen to shade transition events. */
@Provides
@SysUISingleton
@@ -119,16 +62,6 @@
return factory.create("LSShadeTransitionLog", 50);
}
- /** */
- @Provides
- @SysUISingleton
- @SensitiveNotificationProtectionLog
- public static LogBuffer provideSensitiveNotificationProtectionLogBuffer(
- LogBufferFactory factory
- ) {
- return factory.create("SensitiveNotificationProtectionLog", 10);
- }
-
/** Provides a logging buffer for shade window messages. */
@Provides
@SysUISingleton
@@ -153,30 +86,6 @@
return factory.create("ShadeTouchLog", 500, false);
}
- /** Provides a logging buffer for all logs related to managing notification sections. */
- @Provides
- @SysUISingleton
- @NotificationSectionLog
- public static LogBuffer provideNotificationSectionLogBuffer(LogBufferFactory factory) {
- return factory.create("NotifSectionLog", 1000 /* maxSize */, false /* systrace */);
- }
-
- /** Provides a logging buffer for all logs related to remote input controller. */
- @Provides
- @SysUISingleton
- @NotificationRemoteInputLog
- public static LogBuffer provideNotificationRemoteInputLogBuffer(LogBufferFactory factory) {
- return factory.create("NotifRemoteInputLog", 50 /* maxSize */, false /* systrace */);
- }
-
- /** Provides a logging buffer for all logs related to notification visual stability. */
- @Provides
- @SysUISingleton
- @VisualStabilityLog
- public static LogBuffer provideVisualStabilityLogBuffer(LogBufferFactory factory) {
- return factory.create("VisualStabilityLog", 50 /* maxSize */, false /* systrace */);
- }
-
/** Provides a logging buffer for all logs related to keyguard media controller. */
@Provides
@SysUISingleton
@@ -185,22 +94,6 @@
return factory.create("KeyguardMediaControllerLog", 50 /* maxSize */, false /* systrace */);
}
- /** Provides a logging buffer for all logs related to unseen notifications. */
- @Provides
- @SysUISingleton
- @UnseenNotificationLog
- public static LogBuffer provideUnseenNotificationLogBuffer(LogBufferFactory factory) {
- return factory.create("UnseenNotifLog", 20 /* maxSize */, false /* systrace */);
- }
-
- /** Provides a logging buffer for all logs related to the data layer of notifications. */
- @Provides
- @SysUISingleton
- @NotifInteractionLog
- public static LogBuffer provideNotifInteractionLogBuffer(LogBufferFactory factory) {
- return factory.create("NotifInteractionLog", 50);
- }
-
/** Provides a logging buffer for all logs related to Quick Settings. */
@Provides
@SysUISingleton
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/INoteTaskBubblesService.aidl b/packages/SystemUI/src/com/android/systemui/notetask/INoteTaskBubblesService.aidl
index 3e947d9..7803f22 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/INoteTaskBubblesService.aidl
+++ b/packages/SystemUI/src/com/android/systemui/notetask/INoteTaskBubblesService.aidl
@@ -16,6 +16,7 @@
package com.android.systemui.notetask;
+import com.android.systemui.notetask.NoteTaskBubbleExpandBehavior;
import android.content.Intent;
import android.graphics.drawable.Icon;
import android.os.UserHandle;
@@ -25,5 +26,6 @@
boolean areBubblesAvailable();
- void showOrHideAppBubble(in Intent intent, in UserHandle userHandle, in Icon icon);
+ void showOrHideAppBubble(in Intent intent, in UserHandle userHandle, in Icon icon,
+ in NoteTaskBubbleExpandBehavior bubbleExpandBehavior);
}
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskBubbleExpandBehavior.aidl b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskBubbleExpandBehavior.aidl
new file mode 100644
index 0000000..86a06a5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskBubbleExpandBehavior.aidl
@@ -0,0 +1,3 @@
+package com.android.systemui.notetask;
+
+parcelable NoteTaskBubbleExpandBehavior;
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskBubbleExpandBehavior.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskBubbleExpandBehavior.kt
new file mode 100644
index 0000000..63b38a1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskBubbleExpandBehavior.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.notetask
+
+import android.os.Parcel
+import android.os.Parcelable
+
+enum class NoteTaskBubbleExpandBehavior : Parcelable {
+ /**
+ * The default bubble expand behavior for note task bubble: The bubble will collapse if there is
+ * already an expanded bubble, The bubble will expand if there is a collapsed bubble.
+ */
+ DEFAULT,
+ /**
+ * The special bubble expand behavior for note task bubble: The bubble will stay expanded, not
+ * collapse, if there is already an expanded bubble, The bubble will expand if there is a
+ * collapsed bubble.
+ */
+ KEEP_IF_EXPANDED;
+
+ override fun describeContents(): Int {
+ return 0
+ }
+
+ override fun writeToParcel(dest: Parcel, flags: Int) {
+ dest.writeString(name)
+ }
+
+ companion object CREATOR : Parcelable.Creator<NoteTaskBubbleExpandBehavior> {
+ override fun createFromParcel(parcel: Parcel?): NoteTaskBubbleExpandBehavior {
+ return parcel?.readString()?.let { valueOf(it) } ?: DEFAULT
+ }
+
+ override fun newArray(size: Int) = arrayOfNulls<NoteTaskBubbleExpandBehavior>(size)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskBubblesController.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskBubblesController.kt
index ec205f8..169285f 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskBubblesController.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskBubblesController.kt
@@ -27,6 +27,7 @@
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.log.DebugLogger.debugLog
+import com.android.wm.shell.bubbles.Bubble
import com.android.wm.shell.bubbles.Bubbles
import java.util.Optional
import javax.inject.Inject
@@ -48,7 +49,7 @@
@Inject
constructor(
@Application private val context: Context,
- @Background private val bgDispatcher: CoroutineDispatcher
+ @Background private val bgDispatcher: CoroutineDispatcher,
) {
private val serviceConnector: ServiceConnector<INoteTaskBubblesService> =
@@ -57,7 +58,7 @@
Intent(context, NoteTaskBubblesService::class.java),
Context.BIND_AUTO_CREATE or Context.BIND_WAIVE_PRIORITY or Context.BIND_NOT_VISIBLE,
UserHandle.USER_SYSTEM,
- INoteTaskBubblesService.Stub::asInterface
+ INoteTaskBubblesService.Stub::asInterface,
)
/** Returns whether notes app bubble is supported. */
@@ -79,11 +80,12 @@
open suspend fun showOrHideAppBubble(
intent: Intent,
userHandle: UserHandle,
- icon: Icon
+ icon: Icon,
+ bubbleExpandBehavior: NoteTaskBubbleExpandBehavior,
) {
withContext(bgDispatcher) {
serviceConnector
- .post { it.showOrHideAppBubble(intent, userHandle, icon) }
+ .post { it.showOrHideAppBubble(intent, userHandle, icon, bubbleExpandBehavior) }
.whenComplete { _, error ->
if (error != null) {
debugLog(error = error) {
@@ -120,16 +122,28 @@
override fun showOrHideAppBubble(
intent: Intent,
userHandle: UserHandle,
- icon: Icon
+ icon: Icon,
+ bubbleExpandBehavior: NoteTaskBubbleExpandBehavior,
) {
mOptionalBubbles.ifPresentOrElse(
- { bubbles -> bubbles.showOrHideAppBubble(intent, userHandle, icon) },
+ { bubbles ->
+ if (
+ bubbleExpandBehavior ==
+ NoteTaskBubbleExpandBehavior.KEEP_IF_EXPANDED &&
+ bubbles.isBubbleExpanded(
+ Bubble.getAppBubbleKeyForApp(intent.`package`, userHandle)
+ )
+ ) {
+ return@ifPresentOrElse
+ }
+ bubbles.showOrHideAppBubble(intent, userHandle, icon)
+ },
{
debugLog {
"Failed to show or hide bubble for intent $intent," +
"user $user, and icon $icon as bubble is empty."
}
- }
+ },
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
index 1fa5baa..a615963 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
@@ -84,7 +84,7 @@
private val userTracker: UserTracker,
private val secureSettings: SecureSettings,
@Application private val applicationScope: CoroutineScope,
- @Background private val bgCoroutineContext: CoroutineContext
+ @Background private val bgCoroutineContext: CoroutineContext,
) {
@VisibleForTesting val infoReference = AtomicReference<NoteTaskInfo?>()
@@ -98,7 +98,7 @@
if (key != Bubble.getAppBubbleKeyForApp(info.packageName, info.user)) return
// Safe guard mechanism, this callback should only be called for app bubbles.
- if (info.launchMode != NoteTaskLaunchMode.AppBubble) return
+ if (info.launchMode !is NoteTaskLaunchMode.AppBubble) return
if (isExpanding) {
debugLog { "onBubbleExpandChanged - expanding: $info" }
@@ -117,10 +117,8 @@
} else {
getUserForHandlingNotesTaking(entryPoint)
}
- activityContext.startActivityAsUser(
- createNotesRoleHolderSettingsIntent(),
- user
- )
+
+ activityContext.startActivityAsUser(createNotesRoleHolderSettingsIntent(), user)
}
/**
@@ -140,8 +138,7 @@
entryPoint == QUICK_AFFORDANCE -> {
userTracker.userProfiles
.firstOrNull { userManager.isManagedProfile(it.id) }
- ?.userHandle
- ?: userTracker.userHandle
+ ?.userHandle ?: userTracker.userHandle
}
// On work profile devices, SysUI always run in the main user.
else -> userTracker.userHandle
@@ -158,19 +155,14 @@
*
* That will let users open other apps in full screen, and take contextual notes.
*/
- fun showNoteTask(
- entryPoint: NoteTaskEntryPoint,
- ) {
+ fun showNoteTask(entryPoint: NoteTaskEntryPoint) {
if (!isEnabled) return
showNoteTaskAsUser(entryPoint, getUserForHandlingNotesTaking(entryPoint))
}
/** A variant of [showNoteTask] which launches note task in the given [user]. */
- fun showNoteTaskAsUser(
- entryPoint: NoteTaskEntryPoint,
- user: UserHandle,
- ) {
+ fun showNoteTaskAsUser(entryPoint: NoteTaskEntryPoint, user: UserHandle) {
if (!isEnabled) return
applicationScope.launch("$TAG#showNoteTaskAsUser") {
@@ -178,10 +170,7 @@
}
}
- private suspend fun awaitShowNoteTaskAsUser(
- entryPoint: NoteTaskEntryPoint,
- user: UserHandle,
- ) {
+ private suspend fun awaitShowNoteTaskAsUser(entryPoint: NoteTaskEntryPoint, user: UserHandle) {
if (!isEnabled) return
if (!noteTaskBubblesController.areBubblesAvailable()) {
@@ -222,7 +211,13 @@
val intent = createNoteTaskIntent(info)
val icon =
Icon.createWithResource(context, R.drawable.ic_note_task_shortcut_widget)
- noteTaskBubblesController.showOrHideAppBubble(intent, user, icon)
+ noteTaskBubblesController.showOrHideAppBubble(
+ intent,
+ user,
+ icon,
+ info.launchMode.bubbleExpandBehavior,
+ )
+
// App bubble logging happens on `onBubbleExpandChanged`.
debugLog { "onShowNoteTask - opened as app bubble: $info" }
}
@@ -399,8 +394,8 @@
const val EXTRA_SHORTCUT_BADGE_OVERRIDE_PACKAGE = "extra_shortcut_badge_override_package"
/** Returns notes role holder settings intent. */
- fun createNotesRoleHolderSettingsIntent() = Intent(Intent.ACTION_MANAGE_DEFAULT_APP).
- putExtra(Intent.EXTRA_ROLE_NAME, ROLE_NOTES)
+ fun createNotesRoleHolderSettingsIntent() =
+ Intent(Intent.ACTION_MANAGE_DEFAULT_APP).putExtra(Intent.EXTRA_ROLE_NAME, ROLE_NOTES)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInfo.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInfo.kt
index 269eb87..8319e07 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInfo.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInfo.kt
@@ -31,6 +31,10 @@
if (isKeyguardLocked || entryPoint == WIDGET_PICKER_SHORTCUT_IN_MULTI_WINDOW_MODE) {
NoteTaskLaunchMode.Activity
} else {
- NoteTaskLaunchMode.AppBubble
+ if (entryPoint == NoteTaskEntryPoint.QS_NOTES_TILE) {
+ NoteTaskLaunchMode.AppBubble(NoteTaskBubbleExpandBehavior.KEEP_IF_EXPANDED)
+ } else {
+ NoteTaskLaunchMode.AppBubble(NoteTaskBubbleExpandBehavior.DEFAULT)
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskLaunchMode.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskLaunchMode.kt
index 836e103f..6c85f20 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskLaunchMode.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskLaunchMode.kt
@@ -26,7 +26,8 @@
sealed class NoteTaskLaunchMode {
/** @see Bubbles.showOrHideAppBubble */
- object AppBubble : NoteTaskLaunchMode()
+ data class AppBubble(val bubbleExpandBehavior: NoteTaskBubbleExpandBehavior) :
+ NoteTaskLaunchMode()
/** @see Context.startActivity */
object Activity : NoteTaskLaunchMode()
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskModule.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskModule.kt
index a1c5c9c..5d54656 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskModule.kt
@@ -41,6 +41,7 @@
import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
import com.android.systemui.qs.tiles.viewmodel.QSTileUIConfig
import com.android.systemui.qs.tiles.viewmodel.QSTileViewModel
+import com.android.systemui.qs.tiles.viewmodel.StubQSTileViewModel
import com.android.systemui.res.R
import dagger.Binds
import dagger.Module
@@ -106,12 +107,14 @@
stateInteractor: NotesTileDataInteractor,
userActionInteractor: NotesTileUserActionInteractor,
): QSTileViewModel =
- factory.create(
- TileSpec.create(NOTES_TILE_SPEC),
- userActionInteractor,
- stateInteractor,
- mapper,
- )
+ if (com.android.systemui.Flags.qsNewTilesFuture())
+ factory.create(
+ TileSpec.create(NOTES_TILE_SPEC),
+ userActionInteractor,
+ stateInteractor,
+ mapper,
+ )
+ else StubQSTileViewModel
@Provides
@IntoMap
@@ -120,10 +123,10 @@
QSTileConfig(
tileSpec = TileSpec.create(NOTES_TILE_SPEC),
uiConfig =
- QSTileUIConfig.Resource(
- iconRes = R.drawable.ic_qs_notes,
- labelRes = R.string.quick_settings_notes_label,
- ),
+ QSTileUIConfig.Resource(
+ iconRes = R.drawable.ic_qs_notes,
+ labelRes = R.string.quick_settings_notes_label,
+ ),
instanceId = uiEventLogger.getNewInstanceId(),
category = TileCategory.UTILITIES,
)
diff --git a/packages/SystemUI/src/com/android/systemui/power/shared/model/WakeSleepReason.kt b/packages/SystemUI/src/com/android/systemui/power/shared/model/WakeSleepReason.kt
index 776a8f4..c57b53b 100644
--- a/packages/SystemUI/src/com/android/systemui/power/shared/model/WakeSleepReason.kt
+++ b/packages/SystemUI/src/com/android/systemui/power/shared/model/WakeSleepReason.kt
@@ -26,6 +26,9 @@
/** The physical power button was pressed to wake up or sleep the device. */
POWER_BUTTON(isTouch = false, PowerManager.WAKE_REASON_POWER_BUTTON),
+ /** The sleep button was pressed to sleep the device. */
+ SLEEP_BUTTON(isTouch = false, PowerManager.GO_TO_SLEEP_REASON_SLEEP_BUTTON),
+
/** The user has tapped or double tapped to wake the screen. */
TAP(isTouch = true, PowerManager.WAKE_REASON_TAP),
@@ -78,6 +81,7 @@
fun fromPowerManagerSleepReason(reason: Int): WakeSleepReason {
return when (reason) {
PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON -> POWER_BUTTON
+ PowerManager.GO_TO_SLEEP_REASON_SLEEP_BUTTON -> SLEEP_BUTTON
PowerManager.GO_TO_SLEEP_REASON_TIMEOUT -> TIMEOUT
PowerManager.GO_TO_SLEEP_REASON_DEVICE_FOLD -> FOLD
else -> OTHER
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSHostAdapter.kt b/packages/SystemUI/src/com/android/systemui/qs/QSHostAdapter.kt
index 8b06942..0d464f5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSHostAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSHostAdapter.kt
@@ -28,6 +28,7 @@
import com.android.systemui.qs.pipeline.domain.interactor.CurrentTilesInteractor
import com.android.systemui.qs.pipeline.shared.QSPipelineFlagsRepository
import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.shade.ShadeDisplayAware
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
@@ -48,7 +49,7 @@
@Inject
constructor(
private val interactor: CurrentTilesInteractor,
- private val context: Context,
+ @ShadeDisplayAware private val context: Context,
private val tileServiceRequestControllerBuilder: TileServiceRequestController.Builder,
@Application private val scope: CoroutineScope,
dumpManager: DumpManager,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
index 85bcc25..afb852a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
@@ -50,6 +50,7 @@
import kotlin.Unit;
import kotlin.jvm.functions.Function1;
+import kotlinx.coroutines.DisposableHandle;
import kotlinx.coroutines.flow.StateFlow;
import java.io.PrintWriter;
@@ -107,6 +108,8 @@
setLayoutForMediaInScene();
};
+ private DisposableHandle mJavaAdapterDisposableHandle;
+
private boolean mLastListening;
@VisibleForTesting
@@ -221,6 +224,9 @@
mView.removeTile(record);
}
mRecords.clear();
+ if (mJavaAdapterDisposableHandle != null) {
+ mJavaAdapterDisposableHandle.dispose();
+ }
}
@Override
@@ -255,7 +261,7 @@
}
private void registerForMediaInteractorChanges() {
- JavaAdapterKt.collectFlow(
+ mJavaAdapterDisposableHandle = JavaAdapterKt.collectFlow(
mView,
getMediaVisibleFlow(),
mMediaOrRecommendationVisibleConsumer
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooterUtils.java b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooterUtils.java
index d38f849..88f0318 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooterUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooterUtils.java
@@ -87,6 +87,7 @@
import com.android.systemui.res.R;
import com.android.systemui.security.data.model.SecurityModel;
import com.android.systemui.settings.UserTracker;
+import com.android.systemui.shade.ShadeDisplayAware;
import com.android.systemui.statusbar.phone.SystemUIDialog;
import com.android.systemui.statusbar.policy.SecurityController;
@@ -177,7 +178,7 @@
@Inject
QSSecurityFooterUtils(
- @Application Context context, DevicePolicyManager devicePolicyManager,
+ @ShadeDisplayAware Context context, DevicePolicyManager devicePolicyManager,
UserTracker userTracker, @Main Handler mainHandler, ActivityStarter activityStarter,
SecurityController securityController, @Background Looper bgLooper,
DialogTransitionAnimator dialogTransitionAnimator) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt b/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt
index ec6a17b..21c45c5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt
@@ -51,6 +51,7 @@
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.verticalScroll
+import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
@@ -78,6 +79,7 @@
import androidx.compose.ui.semantics.customActions
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.unit.IntOffset
+import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.round
import androidx.compose.ui.util.fastRoundToInt
import androidx.compose.ui.viewinterop.AndroidView
@@ -102,6 +104,8 @@
import com.android.systemui.brightness.ui.compose.BrightnessSliderContainer
import com.android.systemui.compose.modifiers.sysuiResTag
import com.android.systemui.dump.DumpManager
+import com.android.systemui.keyboard.shortcut.ui.composable.InteractionsConfig
+import com.android.systemui.keyboard.shortcut.ui.composable.ProvideShortcutHelperIndication
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.lifecycle.setSnapshotBinding
import com.android.systemui.media.controls.ui.view.MediaHost
@@ -240,51 +244,54 @@
@Composable
private fun Content() {
- PlatformTheme {
- AnimatedVisibility(
- visible = viewModel.isQsVisible,
- modifier =
- Modifier.graphicsLayer { alpha = viewModel.viewAlpha }
- // Clipping before translation to match QSContainerImpl.onDraw
- .offset {
- IntOffset(x = 0, y = viewModel.viewTranslationY.fastRoundToInt())
- }
- .thenIf(notificationScrimClippingParams.isEnabled) {
- Modifier.notificationScrimClip {
- notificationScrimClippingParams.params
+ PlatformTheme(isDarkTheme = true) {
+ ProvideShortcutHelperIndication(interactionsConfig = interactionsConfig()) {
+ AnimatedVisibility(
+ visible = viewModel.isQsVisible,
+ modifier =
+ Modifier.graphicsLayer { alpha = viewModel.viewAlpha }
+ // Clipping before translation to match QSContainerImpl.onDraw
+ .offset {
+ IntOffset(x = 0, y = viewModel.viewTranslationY.fastRoundToInt())
}
+ .thenIf(notificationScrimClippingParams.isEnabled) {
+ Modifier.notificationScrimClip {
+ notificationScrimClippingParams.params
+ }
+ }
+ // Disable touches in the whole composable while the mirror is showing.
+ // While the mirror is showing, an ancestor of the ComposeView is made
+ // alpha 0, but touches are still being captured by the composables.
+ .gesturesDisabled(viewModel.showingMirror),
+ ) {
+ val isEditing by
+ viewModel.containerViewModel.editModeViewModel.isEditing
+ .collectAsStateWithLifecycle()
+ val animationSpecEditMode = tween<Float>(EDIT_MODE_TIME_MILLIS)
+ AnimatedContent(
+ targetState = isEditing,
+ transitionSpec = {
+ fadeIn(animationSpecEditMode) togetherWith
+ fadeOut(animationSpecEditMode)
+ },
+ label = "EditModeAnimatedContent",
+ ) { editing ->
+ if (editing) {
+ val qqsPadding = viewModel.qqsHeaderHeight
+ EditMode(
+ viewModel = viewModel.containerViewModel.editModeViewModel,
+ modifier =
+ Modifier.fillMaxWidth()
+ .padding(top = { qqsPadding })
+ .padding(
+ horizontal = {
+ QuickSettingsShade.Dimensions.Padding.roundToPx()
+ }
+ ),
+ )
+ } else {
+ CollapsableQuickSettingsSTL()
}
- // Disable touches in the whole composable while the mirror is showing.
- // While the mirror is showing, an ancestor of the ComposeView is made
- // alpha 0, but touches are still being captured by the composables.
- .gesturesDisabled(viewModel.showingMirror),
- ) {
- val isEditing by
- viewModel.containerViewModel.editModeViewModel.isEditing
- .collectAsStateWithLifecycle()
- val animationSpecEditMode = tween<Float>(EDIT_MODE_TIME_MILLIS)
- AnimatedContent(
- targetState = isEditing,
- transitionSpec = {
- fadeIn(animationSpecEditMode) togetherWith fadeOut(animationSpecEditMode)
- },
- label = "EditModeAnimatedContent",
- ) { editing ->
- if (editing) {
- val qqsPadding = viewModel.qqsHeaderHeight
- EditMode(
- viewModel = viewModel.containerViewModel.editModeViewModel,
- modifier =
- Modifier.fillMaxWidth()
- .padding(top = { qqsPadding })
- .padding(
- horizontal = {
- QuickSettingsShade.Dimensions.Padding.roundToPx()
- }
- ),
- )
- } else {
- CollapsableQuickSettingsSTL()
}
}
}
@@ -1090,3 +1097,14 @@
const val qsScroll = "expanded_qs_scroll_view"
const val qsFooterActions = "qs_footer_actions"
}
+
+@Composable
+private fun interactionsConfig() =
+ InteractionsConfig(
+ hoverOverlayColor = MaterialTheme.colorScheme.onSurface,
+ hoverOverlayAlpha = 0.11f,
+ pressedOverlayColor = MaterialTheme.colorScheme.onSurface,
+ pressedOverlayAlpha = 0.15f,
+ // we are OK using this as our content is clipped and all corner radius are larger than this
+ surfaceCornerRadius = 28.dp,
+ )
diff --git a/packages/SystemUI/src/com/android/systemui/qs/composefragment/dagger/QSFragmentComposeModule.kt b/packages/SystemUI/src/com/android/systemui/qs/composefragment/dagger/QSFragmentComposeModule.kt
index 676f6a4..2ec7292 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/composefragment/dagger/QSFragmentComposeModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/composefragment/dagger/QSFragmentComposeModule.kt
@@ -20,6 +20,7 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.qs.flags.QSComposeFragment
+import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.util.Utils
import dagger.Module
import dagger.Provides
@@ -34,7 +35,7 @@
@Provides
@SysUISingleton
@Named(QS_USING_MEDIA_PLAYER)
- fun providesUsingMedia(@Application context: Context): Boolean {
+ fun providesUsingMedia(@ShadeDisplayAware context: Context): Boolean {
return QSComposeFragment.isEnabled && Utils.useQsMediaPlayer(context)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModel.kt
index 9029563..e3de6d5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModel.kt
@@ -62,6 +62,7 @@
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.SysuiStatusBarStateController
import com.android.systemui.statusbar.disableflags.data.repository.DisableFlagsRepository
+import com.android.systemui.statusbar.disableflags.domain.interactor.DisableFlagsInteractor
import com.android.systemui.util.LargeScreenUtils
import com.android.systemui.util.asIndenting
import com.android.systemui.util.kotlin.emitOnStart
@@ -93,7 +94,7 @@
private val footerActionsController: FooterActionsController,
private val sysuiStatusBarStateController: SysuiStatusBarStateController,
deviceEntryInteractor: DeviceEntryInteractor,
- disableFlagsRepository: DisableFlagsRepository,
+ DisableFlagsInteractor: DisableFlagsInteractor,
keyguardTransitionInteractor: KeyguardTransitionInteractor,
private val largeScreenShadeInterpolator: LargeScreenShadeInterpolator,
@ShadeDisplayAware configurationInteractor: ConfigurationInteractor,
@@ -182,8 +183,8 @@
val isQsEnabled by
hydrator.hydratedStateOf(
traceName = "isQsEnabled",
- initialValue = disableFlagsRepository.disableFlags.value.isQuickSettingsEnabled(),
- source = disableFlagsRepository.disableFlags.map { it.isQuickSettingsEnabled() },
+ initialValue = DisableFlagsInteractor.disableFlags.value.isQuickSettingsEnabled(),
+ source = DisableFlagsInteractor.disableFlags.map { it.isQuickSettingsEnabled() },
)
var isInSplitShade by mutableStateOf(false)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
index 89f85ab..15e3499 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
@@ -43,6 +43,7 @@
import com.android.systemui.qs.tileimpl.QSTileImpl.DrawableIcon;
import com.android.systemui.res.R;
import com.android.systemui.settings.UserTracker;
+import com.android.systemui.shade.ShadeDisplayAware;
import java.util.ArrayList;
import java.util.Arrays;
@@ -69,7 +70,7 @@
@Inject
public TileQueryHelper(
- Context context,
+ @ShadeDisplayAware Context context,
UserTracker userTracker,
@Main Executor mainExecutor,
@Background Executor bgExecutor
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 564bc78..8ef6375 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
@@ -37,6 +37,7 @@
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.shade.ShadeDisplayAware
import com.android.systemui.util.icuMessageFormat
import javax.inject.Inject
import javax.inject.Named
@@ -112,7 +113,7 @@
class Factory
@Inject
constructor(
- @Application private val context: Context,
+ @ShadeDisplayAware private val context: Context,
private val falsingManager: FalsingManager,
private val footerActionsInteractor: FooterActionsInteractor,
private val globalActionsDialogLiteProvider: Provider<GlobalActionsDialogLite>,
@@ -175,7 +176,7 @@
}
fun FooterActionsViewModel(
- @Application appContext: Context,
+ @ShadeDisplayAware appContext: Context,
footerActionsInteractor: FooterActionsInteractor,
falsingManager: FalsingManager,
globalActionsDialogLite: GlobalActionsDialogLite,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayout.kt
index 2efe500..789fdeb 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayout.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayout.kt
@@ -16,12 +16,20 @@
package com.android.systemui.qs.panels.ui.compose
-import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Arrangement.spacedBy
import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.requiredHeight
+import androidx.compose.foundation.layout.wrapContentSize
+import androidx.compose.foundation.layout.wrapContentWidth
import androidx.compose.foundation.pager.HorizontalPager
+import androidx.compose.foundation.pager.PagerState
import androidx.compose.foundation.pager.rememberPagerState
+import androidx.compose.foundation.shape.CornerSize
+import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Edit
import androidx.compose.material3.Icon
@@ -32,22 +40,24 @@
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import com.android.compose.animation.scene.SceneScope
+import com.android.compose.modifiers.padding
import com.android.systemui.compose.modifiers.sysuiResTag
+import com.android.systemui.development.ui.compose.BuildNumber
+import com.android.systemui.development.ui.viewmodel.BuildNumberViewModel
import com.android.systemui.lifecycle.rememberViewModel
import com.android.systemui.qs.panels.dagger.PaginatedBaseLayoutType
-import com.android.systemui.qs.panels.ui.compose.PaginatedGridLayout.Dimensions.FooterHeight
-import com.android.systemui.qs.panels.ui.compose.PaginatedGridLayout.Dimensions.InterPageSpacing
+import com.android.systemui.qs.panels.ui.compose.Dimensions.FooterHeight
+import com.android.systemui.qs.panels.ui.compose.Dimensions.InterPageSpacing
import com.android.systemui.qs.panels.ui.viewmodel.PaginatedGridViewModel
import com.android.systemui.qs.panels.ui.viewmodel.TileViewModel
+import com.android.systemui.qs.ui.compose.borderOnFocus
import com.android.systemui.res.R
import javax.inject.Inject
@@ -89,9 +99,24 @@
}
Column {
+ val contentPaddingValue =
+ if (pages.size > 1) {
+ InterPageSpacing
+ } else {
+ 0.dp
+ }
+ val contentPadding = PaddingValues(horizontal = contentPaddingValue)
+
+ /* Use negative padding equal with value equal to content padding. That way, each page
+ * layout extends to the sides, but the content is as if there was no padding. That
+ * way, the clipping bounds of the HorizontalPager extend beyond the tiles in each page.
+ */
HorizontalPager(
state = pagerState,
- modifier = Modifier.sysuiResTag("qs_pager"),
+ modifier =
+ Modifier.sysuiResTag("qs_pager")
+ .padding(horizontal = { -contentPaddingValue.roundToPx() }),
+ contentPadding = contentPadding,
pageSpacing = if (pages.size > 1) InterPageSpacing else 0.dp,
beyondViewportPageCount = 1,
verticalAlignment = Alignment.Top,
@@ -102,32 +127,78 @@
TileGrid(tiles = page, modifier = Modifier, editModeStart = {})
}
}
- // Use requiredHeight so it won't be squished if the view doesn't quite fit. As this is
- // expected to be inside a scrollable container, this should not be an issue.
- Box(modifier = Modifier.requiredHeight(FooterHeight).fillMaxWidth()) {
- PagerDots(
- pagerState = pagerState,
- activeColor = MaterialTheme.colorScheme.primary,
- nonActiveColor = MaterialTheme.colorScheme.surfaceVariant,
- modifier = Modifier.align(Alignment.Center),
- )
- CompositionLocalProvider(value = LocalContentColor provides Color.White) {
- IconButton(
- onClick = editModeStart,
- modifier = Modifier.align(Alignment.CenterEnd),
- ) {
- Icon(
- imageVector = Icons.Default.Edit,
- contentDescription = stringResource(id = R.string.qs_edit),
+ FooterBar(
+ buildNumberViewModelFactory = viewModel.buildNumberViewModelFactory,
+ pagerState = pagerState,
+ editModeStart = editModeStart,
+ )
+ }
+ }
+}
+
+private object Dimensions {
+ val FooterHeight = 48.dp
+ val InterPageSpacing = 16.dp
+}
+
+@Composable
+private fun FooterBar(
+ buildNumberViewModelFactory: BuildNumberViewModel.Factory,
+ pagerState: PagerState,
+ editModeStart: () -> Unit,
+) {
+ // Use requiredHeight so it won't be squished if the view doesn't quite fit. As this is
+ // expected to be inside a scrollable container, this should not be an issue.
+ // Also, we construct the layout this way to do the following:
+ // * PagerDots is centered in the row, taking as much space as it needs.
+ // * On the start side, we place the BuildNumber, taking as much space as it needs, but
+ // constrained by the available space left over after PagerDots
+ // * On the end side, we place the edit mode button, with the same constraints as for
+ // BuildNumber (but it will usually fit, as it's just a square button).
+ Row(
+ modifier = Modifier.requiredHeight(FooterHeight).fillMaxWidth(),
+ verticalAlignment = Alignment.CenterVertically,
+ horizontalArrangement = spacedBy(8.dp),
+ ) {
+ Row(Modifier.weight(1f)) {
+ BuildNumber(
+ viewModelFactory = buildNumberViewModelFactory,
+ textColor = MaterialTheme.colorScheme.onSurface,
+ modifier =
+ Modifier.borderOnFocus(
+ color = MaterialTheme.colorScheme.secondary,
+ cornerSize = CornerSize(1.dp),
)
- }
+ .wrapContentSize(),
+ )
+ Spacer(modifier = Modifier.weight(1f))
+ }
+ PagerDots(
+ pagerState = pagerState,
+ activeColor = MaterialTheme.colorScheme.primary,
+ nonActiveColor = MaterialTheme.colorScheme.surfaceVariant,
+ modifier = Modifier.wrapContentWidth(),
+ )
+ Row(Modifier.weight(1f)) {
+ Spacer(modifier = Modifier.weight(1f))
+ CompositionLocalProvider(
+ value = LocalContentColor provides MaterialTheme.colorScheme.onSurface
+ ) {
+ IconButton(
+ onClick = editModeStart,
+ shape = RoundedCornerShape(CornerSize(28.dp)),
+ modifier =
+ Modifier.borderOnFocus(
+ color = MaterialTheme.colorScheme.secondary,
+ cornerSize = CornerSize(FooterHeight / 2),
+ ),
+ ) {
+ Icon(
+ imageVector = Icons.Default.Edit,
+ contentDescription = stringResource(id = R.string.qs_edit),
+ )
}
}
}
}
-
- private object Dimensions {
- val FooterHeight = 48.dp
- val InterPageSpacing = 16.dp
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/CommonTile.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/CommonTile.kt
index 177a5be..dbad602 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/CommonTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/CommonTile.kt
@@ -17,6 +17,7 @@
package com.android.systemui.qs.panels.ui.compose.infinitegrid
import android.graphics.drawable.Animatable
+import android.graphics.drawable.AnimatedVectorDrawable
import android.graphics.drawable.Drawable
import android.text.TextUtils
import androidx.compose.animation.animateColorAsState
@@ -50,7 +51,6 @@
import androidx.compose.ui.draw.drawBehind
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ColorFilter
-import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.Role
@@ -75,6 +75,7 @@
import com.android.systemui.qs.panels.ui.compose.infinitegrid.CommonTileDefaults.SideIconWidth
import com.android.systemui.qs.panels.ui.compose.infinitegrid.CommonTileDefaults.longPressLabel
import com.android.systemui.qs.panels.ui.viewmodel.AccessibilityUiState
+import com.android.systemui.qs.ui.compose.borderOnFocus
import com.android.systemui.res.R
private const val TEST_TAG_TOGGLE = "qs_tile_toggle_target"
@@ -88,7 +89,7 @@
colors: TileColors,
squishiness: () -> Float,
accessibilityUiState: AccessibilityUiState? = null,
- iconShape: Shape = RoundedCornerShape(CommonTileDefaults.InactiveCornerRadius),
+ iconShape: RoundedCornerShape = RoundedCornerShape(CommonTileDefaults.InactiveCornerRadius),
toggleClick: (() -> Unit)? = null,
onLongClick: (() -> Unit)? = null,
) {
@@ -100,10 +101,12 @@
val longPressLabel = longPressLabel().takeIf { onLongClick != null }
val animatedBackgroundColor by
animateColorAsState(colors.iconBackground, label = "QSTileDualTargetBackgroundColor")
+ val focusBorderColor = MaterialTheme.colorScheme.secondary
Box(
modifier =
Modifier.size(CommonTileDefaults.ToggleTargetSize).thenIf(toggleClick != null) {
- Modifier.clip(iconShape)
+ Modifier.borderOnFocus(color = focusBorderColor, iconShape.topEnd)
+ .clip(iconShape)
.verticalSquish(squishiness)
.drawBehind { drawRect(animatedBackgroundColor) }
.combinedClickable(
@@ -226,7 +229,14 @@
}
}
}
- is Icon.Loaded -> rememberDrawablePainter(loadedDrawable)
+ is Icon.Loaded -> {
+ LaunchedEffect(loadedDrawable) {
+ if (loadedDrawable is AnimatedVectorDrawable) {
+ loadedDrawable.forceAnimationOnUI()
+ }
+ }
+ rememberDrawablePainter(loadedDrawable)
+ }
}
Image(
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt
index fe59c4d3..0a80a19 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt
@@ -39,6 +39,7 @@
import androidx.compose.foundation.lazy.grid.LazyGridState
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.lazy.grid.rememberLazyGridState
+import androidx.compose.foundation.shape.CornerSize
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
@@ -49,6 +50,7 @@
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
+import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.platform.LocalConfiguration
@@ -59,6 +61,7 @@
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.semantics.stateDescription
import androidx.compose.ui.semantics.toggleableState
+import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
@@ -82,6 +85,7 @@
import com.android.systemui.qs.panels.ui.viewmodel.TileViewModel
import com.android.systemui.qs.panels.ui.viewmodel.toUiState
import com.android.systemui.qs.tileimpl.QSTileImpl
+import com.android.systemui.qs.ui.compose.borderOnFocus
import com.android.systemui.res.R
import java.util.function.Supplier
import kotlinx.coroutines.CoroutineScope
@@ -122,7 +126,7 @@
val currentBounceableInfo by rememberUpdatedState(bounceableInfo)
val resources = resources()
val uiState = remember(state, resources) { state.toUiState(resources) }
- val colors = TileDefaults.getColorForState(uiState)
+ val colors = TileDefaults.getColorForState(uiState, iconOnly)
val hapticsViewModel: TileHapticsViewModel? =
rememberViewModel(traceName = "TileHapticsViewModel") {
tileHapticsViewModelFactoryProvider.getHapticsViewModelFactory()?.create(tile)
@@ -139,6 +143,7 @@
hapticsViewModel = hapticsViewModel,
modifier =
modifier
+ .borderOnFocus(color = MaterialTheme.colorScheme.secondary, tileShape.topEnd)
.fillMaxWidth()
.bounceable(
bounceable = currentBounceableInfo.bounceable,
@@ -360,28 +365,30 @@
)
@Composable
- fun getColorForState(uiState: TileUiState): TileColors {
+ fun getColorForState(uiState: TileUiState, iconOnly: Boolean): TileColors {
return when (uiState.state) {
STATE_ACTIVE -> {
- if (uiState.handlesSecondaryClick) {
+ if (uiState.handlesSecondaryClick && !iconOnly) {
activeDualTargetTileColors()
} else {
activeTileColors()
}
}
+
STATE_INACTIVE -> {
- if (uiState.handlesSecondaryClick) {
+ if (uiState.handlesSecondaryClick && !iconOnly) {
inactiveDualTargetTileColors()
} else {
inactiveTileColors()
}
}
+
else -> unavailableTileColors()
}
}
@Composable
- fun animateIconShape(state: Int): Shape {
+ fun animateIconShape(state: Int): RoundedCornerShape {
return animateShape(
state = state,
activeCornerRadius = ActiveIconCornerRadius,
@@ -390,7 +397,7 @@
}
@Composable
- fun animateTileShape(state: Int): Shape {
+ fun animateTileShape(state: Int): RoundedCornerShape {
return animateShape(
state = state,
activeCornerRadius = ActiveTileCornerRadius,
@@ -399,7 +406,7 @@
}
@Composable
- fun animateShape(state: Int, activeCornerRadius: Dp, label: String): Shape {
+ fun animateShape(state: Int, activeCornerRadius: Dp, label: String): RoundedCornerShape {
val animatedCornerRadius by
animateDpAsState(
targetValue =
@@ -410,7 +417,15 @@
},
label = label,
)
- return RoundedCornerShape(animatedCornerRadius)
+
+ val corner = remember {
+ object : CornerSize {
+ override fun toPx(shapeSize: Size, density: Density): Float {
+ return with(density) { animatedCornerRadius.toPx() }
+ }
+ }
+ }
+ return RoundedCornerShape(corner)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModel.kt
index 4e34e73..faab696 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModel.kt
@@ -56,7 +56,7 @@
private val tilesAvailabilityInteractor: TilesAvailabilityInteractor,
private val minTilesInteractor: MinimumTilesInteractor,
@ShadeDisplayAware private val configurationInteractor: ConfigurationInteractor,
- @Application private val applicationContext: Context,
+ @ShadeDisplayAware private val context: Context,
@Named("Default") private val defaultGridLayout: GridLayout,
@Application private val applicationScope: CoroutineScope,
gridLayoutTypeInteractor: GridLayoutTypeInteractor,
@@ -140,7 +140,7 @@
.combine(configurationInteractor.onAnyConfigurationChange.emitOnStart()) {
tiles,
_ ->
- tiles.fastMap { it.load(applicationContext) }
+ tiles.fastMap { it.load(context) }
}
} else {
emptyFlow()
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/PaginatedGridViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/PaginatedGridViewModel.kt
index e5607eb..bff330b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/PaginatedGridViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/PaginatedGridViewModel.kt
@@ -17,6 +17,7 @@
package com.android.systemui.qs.panels.ui.viewmodel
import androidx.compose.runtime.getValue
+import com.android.systemui.development.ui.viewmodel.BuildNumberViewModel
import com.android.systemui.lifecycle.ExclusiveActivatable
import com.android.systemui.lifecycle.Hydrator
import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager.Companion.LOCATION_QS
@@ -34,6 +35,7 @@
columnsWithMediaViewModelFactory: QSColumnsViewModel.Factory,
paginatedGridInteractor: PaginatedGridInteractor,
inFirstPageViewModel: InFirstPageViewModel,
+ val buildNumberViewModelFactory: BuildNumberViewModel.Factory,
) : IconTilesViewModel by iconTilesViewModel, ExclusiveActivatable() {
private val hydrator = Hydrator("PaginatedGridViewModel")
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/InstalledTilesComponentRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/InstalledTilesComponentRepository.kt
index c5b2737..41cdefd 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/InstalledTilesComponentRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/InstalledTilesComponentRepository.kt
@@ -30,8 +30,8 @@
import com.android.systemui.common.data.repository.PackageChangeRepository
import com.android.systemui.common.shared.model.PackageChangeModel
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.util.kotlin.isComponentActuallyEnabled
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
@@ -54,7 +54,7 @@
class InstalledTilesComponentRepositoryImpl
@Inject
constructor(
- @Application private val applicationContext: Context,
+ @ShadeDisplayAware private val context: Context,
@Background private val backgroundScope: CoroutineScope,
private val packageChangeRepository: PackageChangeRepository
) : InstalledTilesComponentRepository {
@@ -77,10 +77,10 @@
* context.
*/
val packageManager =
- if (applicationContext.userId == userId) {
- applicationContext.packageManager
+ if (context.userId == userId) {
+ context.packageManager
} else {
- applicationContext
+ context
.createContextAsUser(
UserHandle.of(userId),
/* flags */ 0,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/autoaddable/NightDisplayAutoAddable.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/autoaddable/NightDisplayAutoAddable.kt
index 31ea734..e9c91ca 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/autoaddable/NightDisplayAutoAddable.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/autoaddable/NightDisplayAutoAddable.kt
@@ -27,6 +27,7 @@
import com.android.systemui.qs.pipeline.domain.model.AutoAddable
import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.qs.tiles.NightDisplayTile
+import com.android.systemui.shade.ShadeDisplayAware
import javax.inject.Inject
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
@@ -42,7 +43,7 @@
@Inject
constructor(
private val nightDisplayListenerBuilder: NightDisplayListenerModule.Builder,
- context: Context,
+ @ShadeDisplayAware context: Context,
) : AutoAddable {
private val enabled = ColorDisplayManager.isNightDisplayAvailable(context)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java
index 6b654be..fed8b60 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java
@@ -39,6 +39,7 @@
import com.android.systemui.qs.QSUserSwitcherEvent;
import com.android.systemui.qs.user.UserSwitchDialogController;
import com.android.systemui.res.R;
+import com.android.systemui.shade.ShadeDisplayAware;
import com.android.systemui.statusbar.phone.SystemUIDialog;
import com.android.systemui.statusbar.policy.BaseUserSwitcherAdapter;
import com.android.systemui.statusbar.policy.UserSwitcherController;
@@ -95,7 +96,7 @@
}
@Inject
- public Adapter(Context context, UserSwitcherController controller,
+ public Adapter(@ShadeDisplayAware Context context, UserSwitcherController controller,
UiEventLogger uiEventLogger, FalsingManager falsingManager) {
super(controller);
mContext = context;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/DisabledByPolicyInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/DisabledByPolicyInteractor.kt
index 87b89ea..4577527 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/DisabledByPolicyInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/DisabledByPolicyInteractor.kt
@@ -27,6 +27,7 @@
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.qs.tiles.base.interactor.DisabledByPolicyInteractor.PolicyResult
+import com.android.systemui.shade.ShadeDisplayAware
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.withContext
@@ -70,7 +71,7 @@
class DisabledByPolicyInteractorImpl
@Inject
constructor(
- private val context: Context,
+ @ShadeDisplayAware private val context: Context,
private val activityStarter: ActivityStarter,
private val restrictedLockProxy: RestrictedLockProxy,
@Background private val backgroundDispatcher: CoroutineDispatcher,
@@ -105,7 +106,7 @@
/** Mockable proxy for [RestrictedLockUtilsInternal] static methods. */
@VisibleForTesting
-class RestrictedLockProxy @Inject constructor(private val context: Context) {
+class RestrictedLockProxy @Inject constructor(@ShadeDisplayAware private val context: Context) {
@WorkerThread
fun hasBaseUserRestriction(userId: Int, userRestriction: String?): Boolean =
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/di/QSTilesModule.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/di/QSTilesModule.kt
index b766ee0..222fa3e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/di/QSTilesModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/di/QSTilesModule.kt
@@ -28,6 +28,7 @@
import com.android.systemui.qs.tiles.viewmodel.QSTileConfigProvider
import com.android.systemui.qs.tiles.viewmodel.QSTileConfigProviderImpl
import com.android.systemui.qs.tiles.viewmodel.QSTileViewModel
+import com.android.systemui.shade.ShadeDisplayAware
import dagger.Binds
import dagger.Module
import dagger.Provides
@@ -66,6 +67,6 @@
companion object {
- @Provides fun provideTilesTheme(context: Context): Theme = context.theme
+ @Provides fun provideTilesTheme(@ShadeDisplayAware context: Context): Theme = context.theme
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
index 301ab2b..244f024 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
@@ -87,6 +87,7 @@
import com.android.systemui.flags.Flags;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.res.R;
+import com.android.systemui.shade.ShadeDisplayAware;
import com.android.systemui.statusbar.connectivity.AccessPointController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.LocationController;
@@ -240,7 +241,7 @@
}
@Inject
- public InternetDialogController(@NonNull Context context, UiEventLogger uiEventLogger,
+ public InternetDialogController(@ShadeDisplayAware Context context, UiEventLogger uiEventLogger,
ActivityStarter starter, AccessPointController accessPointController,
SubscriptionManager subscriptionManager, TelephonyManager telephonyManager,
@Nullable WifiManager wifiManager, ConnectivityManager connectivityManager,
@@ -1429,7 +1430,7 @@
void makeOverlayToast(int stringId) {
final Resources res = mContext.getResources();
- final SystemUIToast systemUIToast = mToastFactory.createToast(mContext,
+ final SystemUIToast systemUIToast = mToastFactory.createToast(mContext, mContext,
res.getString(stringId), mContext.getPackageName(), UserHandle.myUserId(),
res.getConfiguration().orientation);
if (systemUIToast == null) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegate.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegate.java
index 89b9eee..0ab533b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegate.java
@@ -71,6 +71,7 @@
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.res.R;
+import com.android.systemui.shade.ShadeDisplayAware;
import com.android.systemui.statusbar.phone.SystemUIDialog;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.wifitrackerlib.WifiEntry;
@@ -190,7 +191,7 @@
@AssistedInject
public InternetDialogDelegate(
- Context context,
+ @ShadeDisplayAware Context context,
InternetDialogManager internetDialogManager,
InternetDialogController internetDialogController,
@Assisted(CAN_CONFIG_MOBILE_DATA) boolean canConfigMobileData,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTileDefaultsRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTileDefaultsRepository.kt
index 1546ec2..32fb1d1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTileDefaultsRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTileDefaultsRepository.kt
@@ -26,6 +26,7 @@
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.qs.tiles.impl.custom.data.entity.CustomTileDefaults
import com.android.systemui.qs.tiles.impl.di.QSTileScope
+import com.android.systemui.shade.ShadeDisplayAware
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
@@ -68,7 +69,7 @@
class CustomTileDefaultsRepositoryImpl
@Inject
constructor(
- private val context: Context,
+ @ShadeDisplayAware private val context: Context,
@Application applicationScope: CoroutineScope,
@Background private val backgroundDispatcher: CoroutineDispatcher,
) : CustomTileDefaultsRepository {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTilePackageUpdatesRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTilePackageUpdatesRepository.kt
index 0ebd6f2..cd4938f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTilePackageUpdatesRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTilePackageUpdatesRepository.kt
@@ -28,6 +28,7 @@
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.qs.tiles.impl.di.QSTileScope
+import com.android.systemui.shade.ShadeDisplayAware
import javax.inject.Inject
import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.CoroutineScope
@@ -51,7 +52,7 @@
@Inject
constructor(
private val tileSpec: TileSpec.CustomTileSpec,
- @Application private val context: Context,
+ @ShadeDisplayAware private val context: Context,
@QSTileScope private val tileScope: CoroutineScope,
@Background private val backgroundCoroutineContext: CoroutineContext,
) : CustomTilePackageUpdatesRepository {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/CustomTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/CustomTileMapper.kt
index 60aa4ea..c446865 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/CustomTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/CustomTileMapper.kt
@@ -30,12 +30,16 @@
import com.android.systemui.qs.tiles.impl.custom.domain.entity.CustomTileDataModel
import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
import com.android.systemui.qs.tiles.viewmodel.QSTileState
+import com.android.systemui.shade.ShadeDisplayAware
import javax.inject.Inject
@SysUISingleton
class CustomTileMapper
@Inject
-constructor(private val context: Context, private val uriGrantsManager: IUriGrantsManager) :
+constructor(
+ @ShadeDisplayAware private val context: Context,
+ private val uriGrantsManager: IUriGrantsManager
+) :
QSTileDataToStateMapper<CustomTileDataModel> {
override fun map(config: QSTileConfig, data: CustomTileDataModel): QSTileState {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileUserActionInteractor.kt
index af2bb9d..1153b5c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileUserActionInteractor.kt
@@ -42,6 +42,7 @@
import com.android.systemui.qs.tiles.impl.di.QSTileScope
import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
import com.android.systemui.settings.DisplayTracker
+import com.android.systemui.shade.ShadeDisplayAware
import java.util.concurrent.atomic.AtomicReference
import javax.inject.Inject
import kotlin.coroutines.CoroutineContext
@@ -51,7 +52,7 @@
class CustomTileUserActionInteractor
@Inject
constructor(
- private val context: Context,
+ @ShadeDisplayAware private val context: Context,
private val tileSpec: TileSpec,
private val qsTileLogger: QSTileLogger,
private val windowManager: IWindowManager,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/InternetTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/InternetTileMapper.kt
index fc94585..1a6876d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/InternetTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/InternetTileMapper.kt
@@ -30,6 +30,7 @@
import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
import com.android.systemui.qs.tiles.viewmodel.QSTileState
import com.android.systemui.res.R
+import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.statusbar.pipeline.shared.ui.model.InternetTileIconModel
import javax.inject.Inject
@@ -37,9 +38,9 @@
class InternetTileMapper
@Inject
constructor(
- @Main private val resources: Resources,
+ @ShadeDisplayAware private val resources: Resources,
private val theme: Resources.Theme,
- private val context: Context,
+ @ShadeDisplayAware private val context: Context,
@Main private val handler: Handler,
) : QSTileDataToStateMapper<InternetTileModel> {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileDataInteractor.kt
index 6fe3979..6d10843 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileDataInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileDataInteractor.kt
@@ -28,6 +28,7 @@
import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
import com.android.systemui.qs.tiles.impl.internet.domain.model.InternetTileModel
import com.android.systemui.res.R
+import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.statusbar.pipeline.airplane.data.repository.AirplaneModeRepository
import com.android.systemui.statusbar.pipeline.ethernet.domain.EthernetInteractor
import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractor
@@ -54,7 +55,7 @@
class InternetTileDataInteractor
@Inject
constructor(
- private val context: Context,
+ @ShadeDisplayAware private val context: Context,
@Application private val scope: CoroutineScope,
airplaneModeRepository: AirplaneModeRepository,
private val connectivityRepository: ConnectivityRepository,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractor.kt
index 3e442582..9b2880b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractor.kt
@@ -28,6 +28,7 @@
import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
import com.android.systemui.qs.tiles.impl.modes.domain.model.ModesTileModel
+import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.statusbar.policy.domain.interactor.ZenModeInteractor
import com.android.systemui.statusbar.policy.domain.model.ActiveZenModes
import com.android.systemui.statusbar.policy.domain.model.ZenModeInfo
@@ -42,7 +43,7 @@
class ModesTileDataInteractor
@Inject
constructor(
- val context: Context,
+ @ShadeDisplayAware val context: Context,
val zenModeInteractor: ZenModeInteractor,
@Background val bgDispatcher: CoroutineDispatcher,
) : QSTileDataInteractor<ModesTileModel> {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/interactor/DataSaverTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/interactor/DataSaverTileUserActionInteractor.kt
index 252e3f8..05bdf0a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/interactor/DataSaverTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/interactor/DataSaverTileUserActionInteractor.kt
@@ -32,6 +32,7 @@
import com.android.systemui.qs.tiles.impl.saver.domain.model.DataSaverTileModel
import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
import com.android.systemui.settings.UserFileManager
+import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.statusbar.phone.SystemUIDialog
import com.android.systemui.statusbar.policy.DataSaverController
import javax.inject.Inject
@@ -42,7 +43,7 @@
class DataSaverTileUserActionInteractor
@Inject
constructor(
- @Application private val context: Context,
+ @ShadeDisplayAware private val context: Context,
@Main private val coroutineContext: CoroutineContext,
@Background private val backgroundContext: CoroutineContext,
private val dataSaverController: DataSaverController,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/interactor/UiModeNightTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/interactor/UiModeNightTileDataInteractor.kt
index c928e8a..7af3576 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/interactor/UiModeNightTileDataInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/interactor/UiModeNightTileDataInteractor.kt
@@ -25,6 +25,7 @@
import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
import com.android.systemui.qs.tiles.impl.uimodenight.domain.model.UiModeNightTileModel
+import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.statusbar.policy.BatteryController
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.LocationController
@@ -38,7 +39,7 @@
class UiModeNightTileDataInteractor
@Inject
constructor(
- @Application private val context: Context,
+ @ShadeDisplayAware private val context: Context,
private val configurationController: ConfigurationController,
private val uiModeManager: UiModeManager,
private val batteryController: BatteryController,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt
index 35b1b96..ab3862b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt
@@ -81,7 +81,7 @@
// additional
// guidance on how to auto add your tile
throw UnsupportedOperationException(
- "Turning on tile is not supported now"
+ "Turning on tile is not supported now. Tile spec: $tileSpec"
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/ui/compose/BorderOnFocus.kt b/packages/SystemUI/src/com/android/systemui/qs/ui/compose/BorderOnFocus.kt
new file mode 100644
index 0000000..e6caa0d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/ui/compose/BorderOnFocus.kt
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.ui.compose
+
+import androidx.compose.foundation.shape.CornerSize
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.focus.FocusEventModifierNode
+import androidx.compose.ui.focus.FocusState
+import androidx.compose.ui.geometry.CornerRadius
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.geometry.Rect
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.drawscope.ContentDrawScope
+import androidx.compose.ui.graphics.drawscope.Stroke
+import androidx.compose.ui.node.DrawModifierNode
+import androidx.compose.ui.node.ModifierNodeElement
+import androidx.compose.ui.platform.InspectorInfo
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.dp
+
+/**
+ * Provides a rounded rect border when the element is focused.
+ *
+ * This should be used for elements that are themselves rounded rects.
+ */
+fun Modifier.borderOnFocus(
+ color: Color,
+ cornerSize: CornerSize,
+ strokeWidth: Dp = 3.dp,
+ padding: Dp = 2.dp,
+) = this then BorderOnFocusElement(color, cornerSize, strokeWidth, padding)
+
+private class BorderOnFocusNode(
+ var color: Color,
+ var cornerSize: CornerSize,
+ var strokeWidth: Dp,
+ var padding: Dp,
+) : FocusEventModifierNode, DrawModifierNode, Modifier.Node() {
+
+ private var focused by mutableStateOf(false)
+
+ override fun onFocusEvent(focusState: FocusState) {
+ focused = focusState.isFocused
+ }
+
+ override fun ContentDrawScope.draw() {
+ drawContent()
+ val focusOutline = Rect(Offset.Zero, size).inflate(padding.toPx())
+ if (focused) {
+ drawRoundRect(
+ color = color,
+ topLeft = focusOutline.topLeft,
+ size = focusOutline.size,
+ cornerRadius = CornerRadius(cornerSize.toPx(focusOutline.size, this)),
+ style = Stroke(strokeWidth.toPx()),
+ )
+ }
+ }
+}
+
+private data class BorderOnFocusElement(
+ val color: Color,
+ val cornerSize: CornerSize,
+ val strokeWidth: Dp,
+ val padding: Dp,
+) : ModifierNodeElement<BorderOnFocusNode>() {
+ override fun create(): BorderOnFocusNode {
+ return BorderOnFocusNode(color, cornerSize, strokeWidth, padding)
+ }
+
+ override fun update(node: BorderOnFocusNode) {
+ node.color = color
+ node.cornerSize = cornerSize
+ node.strokeWidth = strokeWidth
+ node.padding = padding
+ }
+
+ override fun InspectorInfo.inspectableProperties() {
+ name = "borderOnFocus"
+ properties["color"] = color
+ properties["cornerSize"] = cornerSize
+ properties["strokeWidth"] = strokeWidth
+ properties["padding"] = padding
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/reardisplay/RearDisplayCoreStartable.kt b/packages/SystemUI/src/com/android/systemui/reardisplay/RearDisplayCoreStartable.kt
new file mode 100644
index 0000000..bc15bbb
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/reardisplay/RearDisplayCoreStartable.kt
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.reardisplay
+
+import android.content.Context
+import android.hardware.devicestate.DeviceStateManager
+import android.hardware.devicestate.feature.flags.Flags
+import androidx.annotation.VisibleForTesting
+import com.android.systemui.CoreStartable
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.display.domain.interactor.RearDisplayStateInteractor
+import com.android.systemui.statusbar.phone.SystemUIDialog
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.map
+
+/**
+ * Provides a {@link com.android.systemui.statusbar.phone.SystemUIDialog} to be shown on the inner
+ * display when the device enters Rear Display Mode, containing an UI affordance to let the user
+ * know that the main content has moved to the outer display, as well as an UI affordance to cancel
+ * the Rear Display Mode.
+ */
+@SysUISingleton
+class RearDisplayCoreStartable
+@Inject
+internal constructor(
+ private val context: Context,
+ private val deviceStateManager: DeviceStateManager,
+ private val rearDisplayStateInteractor: RearDisplayStateInteractor,
+ private val rearDisplayInnerDialogDelegateFactory: RearDisplayInnerDialogDelegate.Factory,
+ @Application private val scope: CoroutineScope,
+) : CoreStartable, AutoCloseable {
+
+ companion object {
+ private const val TAG: String = "RearDisplayCoreStartable"
+ }
+
+ @VisibleForTesting var stateChangeListener: Job? = null
+
+ override fun close() {
+ stateChangeListener?.cancel()
+ }
+
+ override fun start() {
+ if (Flags.deviceStateRdmV2()) {
+ var dialog: SystemUIDialog? = null
+
+ stateChangeListener =
+ rearDisplayStateInteractor.state
+ .map {
+ when (it) {
+ is RearDisplayStateInteractor.State.Enabled -> {
+ val rearDisplayContext =
+ context.createDisplayContext(it.innerDisplay)
+ val delegate =
+ rearDisplayInnerDialogDelegateFactory.create(
+ rearDisplayContext,
+ deviceStateManager::cancelStateRequest,
+ )
+ dialog = delegate.createDialog().apply { show() }
+ }
+
+ is RearDisplayStateInteractor.State.Disabled -> {
+ dialog?.dismiss()
+ dialog = null
+ }
+ }
+ }
+ .launchIn(scope)
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/reardisplay/RearDisplayInnerDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/reardisplay/RearDisplayInnerDialogDelegate.kt
new file mode 100644
index 0000000..2d6181a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/reardisplay/RearDisplayInnerDialogDelegate.kt
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.reardisplay
+
+import android.content.Context
+import android.os.Bundle
+import android.view.View
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.phone.SystemUIDialog
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+
+/**
+ * A {@link com.android.systemui.statusbar.phone.SystemUIDialog.Delegate} providing a dialog which
+ * lets the user know that the Rear Display Mode is active, and that the content has moved to the
+ * outer display.
+ */
+class RearDisplayInnerDialogDelegate
+@AssistedInject
+internal constructor(
+ private val systemUIDialogFactory: SystemUIDialog.Factory,
+ @Assisted private val rearDisplayContext: Context,
+ @Assisted private val onCanceledRunnable: Runnable,
+) : SystemUIDialog.Delegate {
+
+ @AssistedFactory
+ interface Factory {
+ fun create(
+ rearDisplayContext: Context,
+ onCanceledRunnable: Runnable,
+ ): RearDisplayInnerDialogDelegate
+ }
+
+ override fun createDialog(): SystemUIDialog {
+ return systemUIDialogFactory.create(this, rearDisplayContext)
+ }
+
+ override fun onCreate(dialog: SystemUIDialog, savedInstanceState: Bundle?) {
+ dialog.apply {
+ setContentView(R.layout.activity_rear_display_front_screen_on)
+ setCanceledOnTouchOutside(false)
+ requireViewById<View>(R.id.button_cancel).setOnClickListener {
+ onCanceledRunnable.run()
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/reardisplay/RearDisplayModule.kt b/packages/SystemUI/src/com/android/systemui/reardisplay/RearDisplayModule.kt
index 6ab294d..5fb9cb2 100644
--- a/packages/SystemUI/src/com/android/systemui/reardisplay/RearDisplayModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/reardisplay/RearDisplayModule.kt
@@ -41,4 +41,10 @@
fun bindRearDisplayDialogControllerConfigChanges(
impl: RearDisplayDialogController
): ConfigurationListener
+
+ /** Start RearDisplayCoreStartable. */
+ @Binds
+ @IntoMap
+ @ClassKey(RearDisplayCoreStartable::class)
+ abstract fun bindRearDisplayCoreStartable(impl: RearDisplayCoreStartable): CoreStartable
}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneContainerOcclusionInteractor.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneContainerOcclusionInteractor.kt
index 667827a..c96ea03 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneContainerOcclusionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneContainerOcclusionInteractor.kt
@@ -138,6 +138,7 @@
Overlays.QuickSettingsShade -> false
Scenes.Bouncer -> false
Scenes.Communal -> true
+ Scenes.Dream -> false
Scenes.Gone -> true
Scenes.Lockscreen -> true
Scenes.QuickSettings -> false
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/resolver/HomeSceneFamilyResolver.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/resolver/HomeSceneFamilyResolver.kt
index 41a3c8a..b89eb5c 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/resolver/HomeSceneFamilyResolver.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/resolver/HomeSceneFamilyResolver.kt
@@ -23,6 +23,7 @@
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardEnabledInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.scene.shared.model.SceneFamilies
import com.android.systemui.scene.shared.model.Scenes
import dagger.Binds
@@ -46,6 +47,7 @@
constructor(
@Application private val applicationScope: CoroutineScope,
deviceEntryInteractor: DeviceEntryInteractor,
+ keyguardInteractor: KeyguardInteractor,
keyguardEnabledInteractor: KeyguardEnabledInteractor,
) : SceneResolver {
override val targetFamily: SceneKey = SceneFamilies.Home
@@ -56,6 +58,7 @@
deviceEntryInteractor.canSwipeToEnter,
deviceEntryInteractor.isDeviceEntered,
deviceEntryInteractor.isUnlocked,
+ keyguardInteractor.isDreamingWithOverlay,
transform = ::homeScene,
)
.stateIn(
@@ -67,7 +70,8 @@
canSwipeToEnter = deviceEntryInteractor.canSwipeToEnter.value,
isDeviceEntered = deviceEntryInteractor.isDeviceEntered.value,
isUnlocked = deviceEntryInteractor.isUnlocked.value,
- )
+ isDreamingWithOverlay = false,
+ ),
)
override fun includesScene(scene: SceneKey): Boolean = scene in homeScenes
@@ -77,8 +81,11 @@
canSwipeToEnter: Boolean?,
isDeviceEntered: Boolean,
isUnlocked: Boolean,
+ isDreamingWithOverlay: Boolean,
): SceneKey =
when {
+ // Dream can run even if Keyguard is disabled, thus it has the highest priority here.
+ isDreamingWithOverlay -> Scenes.Dream
!isKeyguardEnabled -> Scenes.Gone
canSwipeToEnter == true -> Scenes.Lockscreen
!isDeviceEntered -> Scenes.Lockscreen
@@ -91,6 +98,9 @@
setOf(
Scenes.Gone,
Scenes.Lockscreen,
+ // Dream is a home scene as the dream activity occludes keyguard and can show the
+ // shade on top.
+ Scenes.Dream,
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
index daeaaa5..066fe1b 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
@@ -62,6 +62,7 @@
import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.scene.shared.logger.SceneLogger
import com.android.systemui.scene.shared.model.Overlays
+import com.android.systemui.scene.shared.model.SceneFamilies
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.NotificationShadeWindowController
@@ -217,7 +218,9 @@
sceneInteractor.transitionState.mapNotNull { state ->
when (state) {
is ObservableTransitionState.Idle -> {
- if (state.currentScene != Scenes.Gone) {
+ if (state.currentScene == Scenes.Dream) {
+ false to "dream is showing"
+ } else if (state.currentScene != Scenes.Gone) {
true to "scene is not Gone"
} else if (state.currentOverlays.isNotEmpty()) {
true to "overlay is shown"
@@ -228,21 +231,30 @@
is ObservableTransitionState.Transition -> {
if (state.fromContent == Scenes.Gone) {
true to "scene transitioning away from Gone"
+ } else if (state.fromContent == Scenes.Dream) {
+ true to "scene transitioning away from dream"
} else {
null
}
}
}
},
+ sceneInteractor.transitionState.map { state ->
+ state.isTransitioningFromOrTo(Scenes.Communal) ||
+ state.isIdle(Scenes.Communal)
+ },
headsUpInteractor.isHeadsUpOrAnimatingAway,
occlusionInteractor.invisibleDueToOcclusion,
alternateBouncerInteractor.isVisible,
) {
visibilityForTransitionState,
+ isCommunalShowing,
isHeadsUpOrAnimatingAway,
invisibleDueToOcclusion,
isAlternateBouncerVisible ->
when {
+ isCommunalShowing ->
+ true to "on or transitioning to/from communal"
isHeadsUpOrAnimatingAway -> true to "showing a HUN"
isAlternateBouncerVisible -> true to "showing alternate bouncer"
invisibleDueToOcclusion -> false to "invisible due to occlusion"
@@ -266,6 +278,7 @@
handleSimUnlock()
handleDeviceUnlockStatus()
handlePowerState()
+ handleDreamState()
handleShadeTouchability()
}
@@ -417,8 +430,13 @@
"mechanism: ${deviceUnlockStatus.deviceUnlockSource}"
else -> null
}
- // Not on lockscreen or bouncer, so remain in the current scene.
- else -> null
+ // Not on lockscreen or bouncer, so remain in the current scene but since
+ // unlocked, replace the Lockscreen scene from the bottom of the navigation
+ // back stack with the Gone scene.
+ else -> {
+ replaceLockscreenSceneOnBackStack()
+ null
+ }
}
}
.collect { (targetSceneKey, loggingReason) ->
@@ -427,17 +445,19 @@
}
}
- /** If the [Scenes.Lockscreen] is on the backstack, replaces it with [Scenes.Gone]. */
+ /**
+ * If the [Scenes.Lockscreen] is on the bottom of the navigation backstack, replaces it with
+ * [Scenes.Gone].
+ */
private fun replaceLockscreenSceneOnBackStack() {
sceneBackInteractor.updateBackStack { stack ->
val list = stack.asIterable().toMutableList()
- check(list.last() == Scenes.Lockscreen) {
- "The bottommost/last SceneKey of the back stack isn't" +
- " the Lockscreen scene like expected. The back" +
- " stack is $stack."
+ if (list.lastOrNull() == Scenes.Lockscreen) {
+ list[list.size - 1] = Scenes.Gone
+ sceneStackOf(*list.toTypedArray())
+ } else {
+ stack
}
- list[list.size - 1] = Scenes.Gone
- sceneStackOf(*list.toTypedArray())
}
}
@@ -506,6 +526,31 @@
}
}
+ private fun handleDreamState() {
+ applicationScope.launch {
+ keyguardInteractor.isAbleToDream
+ .sample(sceneInteractor.transitionState, ::Pair)
+ .collect { (isAbleToDream, transitionState) ->
+ if (transitionState.isIdle(Scenes.Communal)) {
+ // The dream is automatically started underneath the hub, don't transition
+ // to dream when this is happening as communal is still visible on top.
+ return@collect
+ }
+ if (isAbleToDream) {
+ switchToScene(
+ targetSceneKey = Scenes.Dream,
+ loggingReason = "dream started",
+ )
+ } else {
+ switchToScene(
+ targetSceneKey = SceneFamilies.Home,
+ loggingReason = "dream stopped",
+ )
+ }
+ }
+ }
+ }
+
private fun handleShadeTouchability() {
applicationScope.launch {
shadeInteractor.isShadeTouchable
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderController.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderController.java
index 3a90d2b..503d0bf 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderController.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderController.java
@@ -235,8 +235,7 @@
if (mBrightnessWarningToast.isToastActive()) {
return;
}
- mBrightnessWarningToast.show(mView.getContext(),
- R.string.quick_settings_brightness_unable_adjust_msg);
+ mBrightnessWarningToast.show(mView.getContext(), resId);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/ui/BrightnessWarningToast.kt b/packages/SystemUI/src/com/android/systemui/settings/brightness/ui/BrightnessWarningToast.kt
index dfbdaa6..40260d0 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/ui/BrightnessWarningToast.kt
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/ui/BrightnessWarningToast.kt
@@ -37,11 +37,14 @@
private var toastView: View? = null
fun show(viewContext: Context, @StringRes resId: Int) {
+ if (isToastActive()) {
+ return
+ }
val res = viewContext.resources
// Show the brightness warning toast with passing the toast inflation required context,
// userId and resId from SystemUI package.
val systemUIToast = toastFactory.createToast(
- viewContext,
+ viewContext, viewContext,
res.getString(resId), viewContext.packageName, viewContext.getUserId(),
res.configuration.orientation
)
@@ -79,13 +82,15 @@
val inAnimator = systemUIToast.inAnimation
inAnimator?.start()
- toastView!!.postDelayed({
+ toastView?.postDelayed({
val outAnimator = systemUIToast.outAnimation
if (outAnimator != null) {
outAnimator.start()
outAnimator.addListener(object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animator: Animator) {
- windowManager.removeViewImmediate(toastView)
+ if (isToastActive()) {
+ windowManager.removeViewImmediate(toastView)
+ }
toastView = null
}
})
@@ -94,7 +99,7 @@
}
fun isToastActive(): Boolean {
- return toastView != null && toastView!!.isAttachedToWindow
+ return toastView?.isAttachedToWindow == true
}
companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index c15c8f9..4ccd2b9 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -2053,9 +2053,6 @@
}
if (mQsController.getExpanded()) {
mQsController.flingQs(0, FLING_COLLAPSE);
- } else if (mBarState == KEYGUARD) {
- mLockscreenShadeTransitionController.goToLockedShade(
- /* expandedView= */null, /* needsQSAnimation= */false);
} else {
expand(true /* animate */);
}
@@ -3116,8 +3113,14 @@
mShadeLog.d("Status Bar was long pressed. Expanding to QS.");
expandToQs();
} else {
- mShadeLog.d("Status Bar was long pressed. Expanding to Notifications.");
- expandToNotifications();
+ if (mBarState == KEYGUARD) {
+ mShadeLog.d("Lockscreen Status Bar was long pressed. Expanding to Notifications.");
+ mLockscreenShadeTransitionController.goToLockedShade(
+ /* expandedView= */null, /* needsQSAnimation= */false);
+ } else {
+ mShadeLog.d("Status Bar was long pressed. Expanding to Notifications.");
+ expandToNotifications();
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt
index 7a18d7c..207439e 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt
@@ -28,6 +28,7 @@
import androidx.constraintlayout.widget.ConstraintSet.TOP
import androidx.lifecycle.lifecycleScope
import com.android.app.tracing.coroutines.launchTraced as launch
+import com.android.systemui.customization.R as customR
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.fragments.FragmentService
@@ -314,7 +315,7 @@
private fun setKeyguardStatusViewConstraints(constraintSet: ConstraintSet) {
val statusViewMarginHorizontal =
- resources.getDimensionPixelSize(R.dimen.status_view_margin_horizontal)
+ resources.getDimensionPixelSize(customR.dimen.status_view_margin_horizontal)
constraintSet.apply {
setMargin(R.id.keyguard_status_view, START, statusViewMarginHorizontal)
setMargin(R.id.keyguard_status_view, END, statusViewMarginHorizontal)
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeDisplayAwareModule.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeDisplayAwareModule.kt
index e15830e..fed4a26 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeDisplayAwareModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeDisplayAwareModule.kt
@@ -30,8 +30,8 @@
import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractorImpl
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.res.R
-import com.android.systemui.shade.data.repository.ShadePositionRepository
-import com.android.systemui.shade.data.repository.ShadePositionRepositoryImpl
+import com.android.systemui.shade.data.repository.ShadeDisplaysRepository
+import com.android.systemui.shade.data.repository.ShadeDisplaysRepositoryImpl
import com.android.systemui.shade.shared.flag.ShadeWindowGoesAround
import com.android.systemui.statusbar.phone.ConfigurationControllerImpl
import com.android.systemui.statusbar.phone.ConfigurationForwarder
@@ -157,16 +157,16 @@
@SysUISingleton
@Provides
- fun provideShadePositionRepository(impl: ShadePositionRepositoryImpl): ShadePositionRepository {
+ fun provideShadePositionRepository(impl: ShadeDisplaysRepositoryImpl): ShadeDisplaysRepository {
ShadeWindowGoesAround.isUnexpectedlyInLegacyMode()
return impl
}
@Provides
@IntoMap
- @ClassKey(ShadePositionRepositoryImpl::class)
+ @ClassKey(ShadeDisplaysRepositoryImpl::class)
fun provideShadePositionRepositoryAsCoreStartable(
- impl: ShadePositionRepositoryImpl
+ impl: ShadeDisplaysRepositoryImpl
): CoreStartable {
return if (ShadeWindowGoesAround.isEnabled) {
impl
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadePrimaryDisplayCommand.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadePrimaryDisplayCommand.kt
index 802fc0e..506b4e9 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadePrimaryDisplayCommand.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadePrimaryDisplayCommand.kt
@@ -17,11 +17,11 @@
package com.android.systemui.shade
import android.view.Display
-import com.android.systemui.shade.data.repository.ShadePositionRepository
+import com.android.systemui.shade.data.repository.ShadeDisplaysRepository
import com.android.systemui.statusbar.commandline.Command
import java.io.PrintWriter
-class ShadePrimaryDisplayCommand(private val positionRepository: ShadePositionRepository) :
+class ShadePrimaryDisplayCommand(private val positionRepository: ShadeDisplaysRepository) :
Command {
override fun execute(pw: PrintWriter, args: List<String>) {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/data/repository/FakeShadePositionRepository.kt b/packages/SystemUI/src/com/android/systemui/shade/data/repository/FakeShadeDisplayRepository.kt
similarity index 94%
rename from packages/SystemUI/src/com/android/systemui/shade/data/repository/FakeShadePositionRepository.kt
rename to packages/SystemUI/src/com/android/systemui/shade/data/repository/FakeShadeDisplayRepository.kt
index 37210b9..71c5658 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/data/repository/FakeShadePositionRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/data/repository/FakeShadeDisplayRepository.kt
@@ -20,7 +20,7 @@
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
-class FakeShadePositionRepository : ShadePositionRepository {
+class FakeShadeDisplayRepository : ShadeDisplaysRepository {
private val _displayId = MutableStateFlow(Display.DEFAULT_DISPLAY)
override fun setDisplayId(displayId: Int) {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadePositionRepository.kt b/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepository.kt
similarity index 94%
rename from packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadePositionRepository.kt
rename to packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepository.kt
index 24c067a..e920aba 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadePositionRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepository.kt
@@ -25,7 +25,7 @@
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
-interface ShadePositionRepository {
+interface ShadeDisplaysRepository {
/** ID of the display which currently hosts the shade */
val displayId: StateFlow<Int>
@@ -41,9 +41,9 @@
/** Source of truth for the display currently holding the shade. */
@SysUISingleton
-class ShadePositionRepositoryImpl
+class ShadeDisplaysRepositoryImpl
@Inject
-constructor(private val commandRegistry: CommandRegistry) : ShadePositionRepository, CoreStartable {
+constructor(private val commandRegistry: CommandRegistry) : ShadeDisplaysRepository, CoreStartable {
private val _displayId = MutableStateFlow(Display.DEFAULT_DISPLAY)
override val displayId: StateFlow<Int>
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeDisplaysInteractor.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeDisplaysInteractor.kt
new file mode 100644
index 0000000..1055dcb
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeDisplaysInteractor.kt
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.shade.domain.interactor
+
+import android.content.Context
+import android.util.Log
+import android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE
+import com.android.app.tracing.coroutines.launchTraced
+import com.android.app.tracing.traceSection
+import com.android.systemui.CoreStartable
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.display.data.repository.DisplayWindowPropertiesRepository
+import com.android.systemui.display.shared.model.DisplayWindowProperties
+import com.android.systemui.scene.ui.view.WindowRootView
+import com.android.systemui.shade.ShadeDisplayAware
+import com.android.systemui.shade.ShadeWindowLayoutParams
+import com.android.systemui.shade.data.repository.ShadeDisplaysRepository
+import com.android.systemui.shade.shared.flag.ShadeWindowGoesAround
+import com.android.systemui.statusbar.phone.ConfigurationForwarder
+import javax.inject.Inject
+import kotlin.coroutines.CoroutineContext
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.withContext
+
+/** Handles Shade window display change when [ShadeDisplaysRepository.displayId] changes. */
+@SysUISingleton
+class ShadeDisplaysInteractor
+@Inject
+constructor(
+ private val shadeRootView: WindowRootView,
+ private val shadePositionRepository: ShadeDisplaysRepository,
+ @ShadeDisplayAware private val shadeContext: Context,
+ private val displayWindowPropertiesRepository: DisplayWindowPropertiesRepository,
+ @Background private val bgScope: CoroutineScope,
+ @ShadeDisplayAware private val configurationForwarder: ConfigurationForwarder,
+ @Main private val mainContext: CoroutineContext,
+) : CoreStartable {
+
+ override fun start() {
+ ShadeWindowGoesAround.isUnexpectedlyInLegacyMode()
+ bgScope.launchTraced(TAG) {
+ shadePositionRepository.displayId.collect { displayId -> moveShadeWindowTo(displayId) }
+ }
+ }
+
+ /** Tries to move the shade. If anything wrong happens, fails gracefully without crashing. */
+ private suspend fun moveShadeWindowTo(destinationDisplayId: Int) {
+ val currentId = shadeRootView.display.displayId
+ if (currentId == destinationDisplayId) {
+ Log.w(TAG, "Trying to move the shade to a display it was already in")
+ return
+ }
+ try {
+ moveShadeWindow(fromId = currentId, toId = destinationDisplayId)
+ } catch (e: IllegalStateException) {
+ Log.e(
+ TAG,
+ "Unable to move the shade window from display $currentId to $destinationDisplayId",
+ e,
+ )
+ }
+ }
+
+ private suspend fun moveShadeWindow(fromId: Int, toId: Int) {
+ val sourceProperties = getDisplayWindowProperties(fromId)
+ val destinationProperties = getDisplayWindowProperties(toId)
+ traceSection({ "MovingShadeWindow from $fromId to $toId" }) {
+ withContext(mainContext) {
+ traceSection("removeView") {
+ sourceProperties.windowManager.removeView(shadeRootView)
+ }
+ traceSection("addView") {
+ destinationProperties.windowManager.addView(
+ shadeRootView,
+ ShadeWindowLayoutParams.create(shadeContext),
+ )
+ }
+ }
+ }
+ traceSection("SecondaryShadeInteractor#onConfigurationChanged") {
+ configurationForwarder.onConfigurationChanged(
+ destinationProperties.context.resources.configuration
+ )
+ }
+ }
+
+ private fun getDisplayWindowProperties(displayId: Int): DisplayWindowProperties {
+ return displayWindowPropertiesRepository.get(displayId, TYPE_NOTIFICATION_SHADE)
+ }
+
+ private companion object {
+ const val TAG = "SecondaryShadeInteractor"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImpl.kt
index 460bfbb..a653ca2 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImpl.kt
@@ -25,7 +25,7 @@
import com.android.systemui.keyguard.shared.model.Edge
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.power.domain.interactor.PowerInteractor
-import com.android.systemui.statusbar.disableflags.data.repository.DisableFlagsRepository
+import com.android.systemui.statusbar.disableflags.domain.interactor.DisableFlagsInteractor
import com.android.systemui.statusbar.phone.DozeParameters
import com.android.systemui.statusbar.policy.data.repository.UserSetupRepository
import com.android.systemui.statusbar.policy.domain.interactor.DeviceProvisioningInteractor
@@ -47,7 +47,7 @@
constructor(
@Application val scope: CoroutineScope,
deviceProvisioningInteractor: DeviceProvisioningInteractor,
- disableFlagsRepository: DisableFlagsRepository,
+ disableFlagsInteractor: DisableFlagsInteractor,
dozeParams: DozeParameters,
keyguardRepository: KeyguardRepository,
keyguardTransitionInteractor: KeyguardTransitionInteractor,
@@ -61,13 +61,13 @@
BaseShadeInteractor by baseShadeInteractor,
ShadeModeInteractor by shadeModeInteractor {
override val isShadeEnabled: StateFlow<Boolean> =
- disableFlagsRepository.disableFlags
+ disableFlagsInteractor.disableFlags
.map { it.isShadeEnabled() }
.flowName("isShadeEnabled")
.stateIn(scope, SharingStarted.Eagerly, initialValue = false)
override val isQsEnabled: StateFlow<Boolean> =
- disableFlagsRepository.disableFlags
+ disableFlagsInteractor.disableFlags
.map { it.isQuickSettingsEnabled() }
.flowName("isQsEnabled")
.stateIn(scope, SharingStarted.Eagerly, initialValue = false)
@@ -114,7 +114,7 @@
override val isExpandToQsEnabled: Flow<Boolean> =
combine(
- disableFlagsRepository.disableFlags,
+ disableFlagsInteractor.disableFlags,
isShadeEnabled,
keyguardRepository.isDozing,
userSetupRepository.isUserSetUp,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
index 33f0c64..6bec86a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
@@ -177,8 +177,6 @@
private float[] mMatrix;
private ColorMatrixColorFilter mMatrixColorFilter;
private Runnable mLayoutRunnable;
- private boolean mDismissed;
- private Runnable mOnDismissListener;
private boolean mIncreasedSize;
private boolean mShowsConversation;
private float mDozeAmount;
@@ -956,21 +954,6 @@
mLayoutRunnable = runnable;
}
- public void setDismissed() {
- mDismissed = true;
- if (mOnDismissListener != null) {
- mOnDismissListener.run();
- }
- }
-
- public boolean isDismissed() {
- return mDismissed;
- }
-
- public void setOnDismissListener(Runnable onDismissListener) {
- mOnDismissListener = onDismissListener;
- }
-
@Override
public void onDarkChanged(ArrayList<Rect> areas, float darkIntensity, int tint) {
int areaTint = getTint(areas, this, tint);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
index da04f6e..b2ca33a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
@@ -705,6 +705,7 @@
final boolean onBouncer = currentScene.equals(Scenes.Bouncer);
final boolean onCommunal = currentScene.equals(Scenes.Communal);
final boolean onGone = currentScene.equals(Scenes.Gone);
+ final boolean onDream = currentScene.equals(Scenes.Dream);
final boolean onLockscreen = currentScene.equals(Scenes.Lockscreen);
final boolean onQuickSettings = currentScene.equals(Scenes.QuickSettings);
final boolean onShade = currentScene.equals(Scenes.Shade);
@@ -765,6 +766,8 @@
// We get here if deviceUnlockStatus.isUnlocked is false but we are no longer on or over
// a keyguardish scene; we want to return SHADE_LOCKED until isUnlocked is also true.
newState = StatusBarState.SHADE_LOCKED;
+ } else if (onDream) {
+ newState = StatusBarState.SHADE_LOCKED;
} else {
throw new IllegalArgumentException(
"unhandled input to calculateStateFromSceneFramework: " + inputLogString);
diff --git a/core/java/android/text/ClientFlags.java b/packages/SystemUI/src/com/android/systemui/statusbar/chips/StatusBarChipLogTags.kt
similarity index 60%
copy from core/java/android/text/ClientFlags.java
copy to packages/SystemUI/src/com/android/systemui/statusbar/chips/StatusBarChipLogTags.kt
index ca88764..6c1d6c5 100644
--- a/core/java/android/text/ClientFlags.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/StatusBarChipLogTags.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,16 +14,13 @@
* limitations under the License.
*/
-package android.text;
+package com.android.systemui.statusbar.chips
-/**
- * An aconfig feature flags that can be accessible from application process without
- * ContentProvider IPCs.
- *
- * When you add new flags, you have to add flag string to {@link TextFlags#TEXT_ACONFIGS_FLAGS}.
- *
- * TODO(nona): Remove this class.
- * @hide
- */
-public class ClientFlags {
+/** Helper class to ensure all tags used in [StatusBarChipsLog] are exactly the same length. */
+object StatusBarChipLogTags {
+ private const val TAG_LENGTH = 20
+
+ fun String.pad(): String {
+ return this.padEnd(TAG_LENGTH)
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/domain/interactor/CallChipInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/domain/interactor/CallChipInteractor.kt
index eaefc11..bb0467f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/domain/interactor/CallChipInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/domain/interactor/CallChipInteractor.kt
@@ -20,6 +20,7 @@
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.core.LogLevel
+import com.android.systemui.statusbar.chips.StatusBarChipLogTags.pad
import com.android.systemui.statusbar.chips.StatusBarChipsLog
import com.android.systemui.statusbar.phone.ongoingcall.data.repository.OngoingCallRepository
import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallModel
@@ -47,6 +48,6 @@
.stateIn(scope, SharingStarted.Lazily, OngoingCallModel.NoCall)
companion object {
- private const val TAG = "OngoingCall"
+ private val TAG = "OngoingCall".pad()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModel.kt
index e825258..b8cdd25 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModel.kt
@@ -28,6 +28,7 @@
import com.android.systemui.log.core.LogLevel
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.res.R
+import com.android.systemui.statusbar.chips.StatusBarChipLogTags.pad
import com.android.systemui.statusbar.chips.StatusBarChipsLog
import com.android.systemui.statusbar.chips.call.domain.interactor.CallChipInteractor
import com.android.systemui.statusbar.chips.ui.model.ColorsModel
@@ -112,7 +113,7 @@
ActivityTransitionAnimator.Controller.fromView(
backgroundView,
InteractionJankMonitor.CUJ_STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP,
- )
+ ),
)
}
}
@@ -121,10 +122,8 @@
private val phoneIcon =
Icon.Resource(
com.android.internal.R.drawable.ic_phone,
- ContentDescription.Resource(
- R.string.ongoing_phone_call_content_description,
- ),
+ ContentDescription.Resource(R.string.ongoing_phone_call_content_description),
)
- private const val TAG = "CallVM"
+ private val TAG = "CallVM".pad()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/casttootherdevice/domain/interactor/MediaRouterChipInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/casttootherdevice/domain/interactor/MediaRouterChipInteractor.kt
index 7c95f1e..b3dbf29 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/casttootherdevice/domain/interactor/MediaRouterChipInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/casttootherdevice/domain/interactor/MediaRouterChipInteractor.kt
@@ -21,6 +21,7 @@
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.core.LogLevel
import com.android.systemui.mediarouter.data.repository.MediaRouterRepository
+import com.android.systemui.statusbar.chips.StatusBarChipLogTags.pad
import com.android.systemui.statusbar.chips.StatusBarChipsLog
import com.android.systemui.statusbar.chips.casttootherdevice.domain.model.MediaRouterCastModel
import com.android.systemui.statusbar.policy.CastDevice
@@ -68,6 +69,6 @@
}
companion object {
- private const val TAG = "MediaRouter"
+ private val TAG = "MediaRouter".pad()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModel.kt
index 1107206..3422337 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModel.kt
@@ -28,6 +28,7 @@
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.core.LogLevel
import com.android.systemui.res.R
+import com.android.systemui.statusbar.chips.StatusBarChipLogTags.pad
import com.android.systemui.statusbar.chips.StatusBarChipsLog
import com.android.systemui.statusbar.chips.casttootherdevice.domain.interactor.MediaRouterChipInteractor
import com.android.systemui.statusbar.chips.casttootherdevice.domain.model.MediaRouterCastModel
@@ -255,6 +256,6 @@
companion object {
@DrawableRes val CAST_TO_OTHER_DEVICE_ICON = R.drawable.ic_cast_connected
- private const val TAG = "CastToOtherVM"
+ private val TAG = "CastToOtherVM".pad()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractor.kt
index 27b2465..af238f6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractor.kt
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.chips.mediaprojection.domain.interactor
import android.content.pm.PackageManager
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.Flags
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
@@ -25,6 +26,7 @@
import com.android.systemui.mediaprojection.MediaProjectionUtils.packageHasCastingCapabilities
import com.android.systemui.mediaprojection.data.model.MediaProjectionState
import com.android.systemui.mediaprojection.data.repository.MediaProjectionRepository
+import com.android.systemui.statusbar.chips.StatusBarChipLogTags.pad
import com.android.systemui.statusbar.chips.StatusBarChipsLog
import com.android.systemui.statusbar.chips.mediaprojection.domain.model.ProjectionChipModel
import javax.inject.Inject
@@ -33,7 +35,6 @@
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
-import com.android.app.tracing.coroutines.launchTraced as launch
/**
* Interactor for media projection events, used to show chips in the status bar for share-to-app and
@@ -108,6 +109,6 @@
}
companion object {
- private const val TAG = "MediaProjection"
+ private val TAG = "MediaProjection".pad()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractor.kt
index e3dc70a..f5952f4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractor.kt
@@ -25,6 +25,7 @@
import com.android.systemui.mediaprojection.data.repository.MediaProjectionRepository
import com.android.systemui.screenrecord.data.model.ScreenRecordModel
import com.android.systemui.screenrecord.data.repository.ScreenRecordRepository
+import com.android.systemui.statusbar.chips.StatusBarChipLogTags.pad
import com.android.systemui.statusbar.chips.StatusBarChipsLog
import com.android.systemui.statusbar.chips.screenrecord.domain.model.ScreenRecordChipModel
import javax.inject.Inject
@@ -143,6 +144,6 @@
}
companion object {
- private const val TAG = "ScreenRecord"
+ private val TAG = "ScreenRecord".pad()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModel.kt
index eb73521..0065593 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModel.kt
@@ -30,6 +30,7 @@
import com.android.systemui.log.core.LogLevel
import com.android.systemui.res.R
import com.android.systemui.screenrecord.data.model.ScreenRecordModel.Starting.Companion.toCountdownSeconds
+import com.android.systemui.statusbar.chips.StatusBarChipLogTags.pad
import com.android.systemui.statusbar.chips.StatusBarChipsLog
import com.android.systemui.statusbar.chips.mediaprojection.ui.view.EndMediaProjectionDialogHelper
import com.android.systemui.statusbar.chips.screenrecord.domain.interactor.ScreenRecordChipInteractor
@@ -84,7 +85,7 @@
Icon.Resource(
ICON,
ContentDescription.Resource(
- R.string.screenrecord_ongoing_screen_only,
+ R.string.screenrecord_ongoing_screen_only
),
)
),
@@ -153,6 +154,6 @@
companion object {
@DrawableRes val ICON = R.drawable.ic_screenrecord
- private const val TAG = "ScreenRecordVM"
+ private val TAG = "ScreenRecordVM".pad()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModel.kt
index 11d077f..2af86a5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModel.kt
@@ -28,6 +28,7 @@
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.core.LogLevel
import com.android.systemui.res.R
+import com.android.systemui.statusbar.chips.StatusBarChipLogTags.pad
import com.android.systemui.statusbar.chips.StatusBarChipsLog
import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractor
import com.android.systemui.statusbar.chips.mediaprojection.domain.model.ProjectionChipModel
@@ -179,6 +180,6 @@
companion object {
@DrawableRes val SHARE_TO_APP_ICON = R.drawable.ic_present_to_all
- private const val TAG = "ShareToAppVM"
+ private val TAG = "ShareToAppVM".pad()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModel.kt
index ed32597..45efc57 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModel.kt
@@ -20,6 +20,7 @@
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.core.LogLevel
+import com.android.systemui.statusbar.chips.StatusBarChipLogTags.pad
import com.android.systemui.statusbar.chips.StatusBarChipsLog
import com.android.systemui.statusbar.chips.call.ui.viewmodel.CallChipViewModel
import com.android.systemui.statusbar.chips.casttootherdevice.ui.viewmodel.CastToOtherDeviceChipViewModel
@@ -347,7 +348,7 @@
}
companion object {
- private const val TAG = "ChipsViewModel"
+ private val TAG = "ChipsViewModel".pad()
private val DEFAULT_INTERNAL_HIDDEN_MODEL =
InternalChipModel.Hidden(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/disableflags/data/repository/DisableFlagsRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/disableflags/data/repository/DisableFlagsRepository.kt
index 9004e5d..aeeb042 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/disableflags/data/repository/DisableFlagsRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/disableflags/data/repository/DisableFlagsRepository.kt
@@ -22,7 +22,7 @@
import com.android.systemui.log.dagger.DisableFlagsRepositoryLog
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.disableflags.DisableFlagsLogger
-import com.android.systemui.statusbar.disableflags.data.model.DisableFlagsModel
+import com.android.systemui.statusbar.disableflags.shared.model.DisableFlagsModel
import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/disableflags/domain/interactor/DisableFlagsInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/disableflags/domain/interactor/DisableFlagsInteractor.kt
new file mode 100644
index 0000000..4f1b978
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/disableflags/domain/interactor/DisableFlagsInteractor.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.disableflags.domain.interactor
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.disableflags.data.repository.DisableFlagsRepository
+import com.android.systemui.statusbar.disableflags.shared.model.DisableFlagsModel
+import javax.inject.Inject
+import kotlinx.coroutines.flow.StateFlow
+
+@SysUISingleton
+class DisableFlagsInteractor @Inject constructor(repository: DisableFlagsRepository) {
+ /** A model of the disable flags last received from [IStatusBar]. */
+ val disableFlags: StateFlow<DisableFlagsModel> = repository.disableFlags
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/disableflags/data/model/DisableFlagsModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/disableflags/shared/model/DisableFlagsModel.kt
similarity index 85%
rename from packages/SystemUI/src/com/android/systemui/statusbar/disableflags/data/model/DisableFlagsModel.kt
rename to packages/SystemUI/src/com/android/systemui/statusbar/disableflags/shared/model/DisableFlagsModel.kt
index ce25cf5..6507237 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/disableflags/data/model/DisableFlagsModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/disableflags/shared/model/DisableFlagsModel.kt
@@ -1,18 +1,20 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of the License at
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
- * Unless required by applicable law or agreed to in writing, software distributed under the
- * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the specific language governing
- * permissions and limitations under the License.
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.disableflags.data.model
+package com.android.systemui.statusbar.disableflags.shared.model
import android.app.StatusBarManager.DISABLE2_NONE
import android.app.StatusBarManager.DISABLE2_NOTIFICATION_SHADE
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/HeadsUpManagerPhone.java
deleted file mode 100644
index 0299ebc..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/HeadsUpManagerPhone.java
+++ /dev/null
@@ -1,772 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.notification;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Region;
-import android.os.Handler;
-import android.util.ArrayMap;
-import android.util.Pools;
-
-import androidx.collection.ArraySet;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.logging.UiEventLogger;
-import com.android.internal.policy.SystemBarUtils;
-import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
-import com.android.systemui.res.R;
-import com.android.systemui.scene.shared.flag.SceneContainerFlag;
-import com.android.systemui.shade.domain.interactor.ShadeInteractor;
-import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.collection.provider.OnReorderingAllowedListener;
-import com.android.systemui.statusbar.notification.collection.provider.OnReorderingBannedListener;
-import com.android.systemui.statusbar.notification.collection.provider.VisualStabilityProvider;
-import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
-import com.android.systemui.statusbar.notification.data.repository.HeadsUpRepository;
-import com.android.systemui.statusbar.notification.data.repository.HeadsUpRowRepository;
-import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
-import com.android.systemui.statusbar.notification.shared.NotificationThrottleHun;
-import com.android.systemui.statusbar.phone.ExpandHeadsUpOnInlineReply;
-import com.android.systemui.statusbar.phone.KeyguardBypassController;
-import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
-import com.android.systemui.statusbar.policy.AnimationStateHandler;
-import com.android.systemui.statusbar.policy.AvalancheController;
-import com.android.systemui.statusbar.policy.BaseHeadsUpManager;
-import com.android.systemui.statusbar.policy.ConfigurationController;
-import com.android.systemui.statusbar.policy.HeadsUpManagerLogger;
-import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
-import com.android.systemui.statusbar.policy.OnHeadsUpPhoneListenerChange;
-import com.android.systemui.util.concurrency.DelayableExecutor;
-import com.android.systemui.util.kotlin.JavaAdapter;
-import com.android.systemui.util.settings.GlobalSettings;
-import com.android.systemui.util.time.SystemClock;
-
-import kotlinx.coroutines.flow.Flow;
-import kotlinx.coroutines.flow.MutableStateFlow;
-import kotlinx.coroutines.flow.StateFlow;
-import kotlinx.coroutines.flow.StateFlowKt;
-
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Objects;
-import java.util.Set;
-import java.util.Stack;
-
-import javax.inject.Inject;
-
-/** A implementation of HeadsUpManager for phone. */
-@SysUISingleton
-public class HeadsUpManagerPhone extends BaseHeadsUpManager implements
- HeadsUpRepository, OnHeadsUpChangedListener {
- private static final String TAG = "HeadsUpManagerPhone";
-
- @VisibleForTesting
- public final int mExtensionTime;
- private final KeyguardBypassController mBypassController;
- private final GroupMembershipManager mGroupMembershipManager;
- private final List<OnHeadsUpPhoneListenerChange> mHeadsUpPhoneListeners = new ArrayList<>();
- private final VisualStabilityProvider mVisualStabilityProvider;
-
- private AvalancheController mAvalancheController;
-
- // TODO(b/328393698) move the topHeadsUpRow logic to an interactor
- private final MutableStateFlow<HeadsUpRowRepository> mTopHeadsUpRow =
- StateFlowKt.MutableStateFlow(null);
- private final MutableStateFlow<Set<HeadsUpRowRepository>> mHeadsUpNotificationRows =
- StateFlowKt.MutableStateFlow(new HashSet<>());
- private final MutableStateFlow<Boolean> mHeadsUpAnimatingAway =
- StateFlowKt.MutableStateFlow(false);
- private boolean mReleaseOnExpandFinish;
- private boolean mTrackingHeadsUp;
- private final HashSet<String> mSwipedOutKeys = new HashSet<>();
- private final HashSet<NotificationEntry> mEntriesToRemoveAfterExpand = new HashSet<>();
- @VisibleForTesting
- public final ArraySet<NotificationEntry> mEntriesToRemoveWhenReorderingAllowed
- = new ArraySet<>();
- private boolean mIsShadeOrQsExpanded;
- private boolean mIsQsExpanded;
- private int mStatusBarState;
- private AnimationStateHandler mAnimationStateHandler;
-
- private int mHeadsUpInset;
-
- // Used for determining the region for touch interaction
- private final Region mTouchableRegion = new Region();
-
- private final Pools.Pool<HeadsUpEntryPhone> mEntryPool = new Pools.Pool<HeadsUpEntryPhone>() {
- private Stack<HeadsUpEntryPhone> mPoolObjects = new Stack<>();
-
- @Override
- public HeadsUpEntryPhone acquire() {
- NotificationThrottleHun.assertInLegacyMode();
- if (!mPoolObjects.isEmpty()) {
- return mPoolObjects.pop();
- }
- return new HeadsUpEntryPhone();
- }
-
- @Override
- public boolean release(@NonNull HeadsUpEntryPhone instance) {
- NotificationThrottleHun.assertInLegacyMode();
- mPoolObjects.push(instance);
- return true;
- }
- };
-
- ///////////////////////////////////////////////////////////////////////////////////////////////
- // Constructor:
- @Inject
- public HeadsUpManagerPhone(
- @NonNull final Context context,
- HeadsUpManagerLogger logger,
- StatusBarStateController statusBarStateController,
- KeyguardBypassController bypassController,
- GroupMembershipManager groupMembershipManager,
- VisualStabilityProvider visualStabilityProvider,
- ConfigurationController configurationController,
- @Main Handler handler,
- GlobalSettings globalSettings,
- SystemClock systemClock,
- @Main DelayableExecutor executor,
- AccessibilityManagerWrapper accessibilityManagerWrapper,
- UiEventLogger uiEventLogger,
- JavaAdapter javaAdapter,
- ShadeInteractor shadeInteractor,
- AvalancheController avalancheController) {
- super(context, logger, handler, globalSettings, systemClock, executor,
- accessibilityManagerWrapper, uiEventLogger, avalancheController);
- Resources resources = mContext.getResources();
- mExtensionTime = resources.getInteger(R.integer.ambient_notification_extension_time);
- statusBarStateController.addCallback(mStatusBarStateListener);
- mBypassController = bypassController;
- mGroupMembershipManager = groupMembershipManager;
- mVisualStabilityProvider = visualStabilityProvider;
- mAvalancheController = avalancheController;
- updateResources();
- configurationController.addCallback(new ConfigurationController.ConfigurationListener() {
- @Override
- public void onDensityOrFontScaleChanged() {
- updateResources();
- }
-
- @Override
- public void onThemeChanged() {
- updateResources();
- }
- });
- javaAdapter.alwaysCollectFlow(shadeInteractor.isAnyExpanded(),
- this::onShadeOrQsExpanded);
- if (SceneContainerFlag.isEnabled()) {
- javaAdapter.alwaysCollectFlow(shadeInteractor.isQsExpanded(),
- this::onQsExpanded);
- }
- if (NotificationThrottleHun.isEnabled()) {
- mVisualStabilityProvider.addPersistentReorderingBannedListener(
- mOnReorderingBannedListener);
- mVisualStabilityProvider.addPersistentReorderingAllowedListener(
- mOnReorderingAllowedListener);
- }
- }
-
- public void setAnimationStateHandler(AnimationStateHandler handler) {
- mAnimationStateHandler = handler;
- }
-
- private void updateResources() {
- Resources resources = mContext.getResources();
- mHeadsUpInset = SystemBarUtils.getStatusBarHeight(mContext)
- + resources.getDimensionPixelSize(R.dimen.heads_up_status_bar_padding);
- }
-
- ///////////////////////////////////////////////////////////////////////////////////////////////
- // Public methods:
-
- /**
- * Add a listener to receive callbacks {@link #setHeadsUpAnimatingAway(boolean)}
- */
- @Override
- public void addHeadsUpPhoneListener(OnHeadsUpPhoneListenerChange listener) {
- mHeadsUpPhoneListeners.add(listener);
- }
-
- /**
- * Gets the touchable region needed for heads up notifications. Returns null if no touchable
- * region is required (ie: no heads up notification currently exists).
- */
- // TODO(b/347007367): With scene container enabled this method may report outdated regions
- @Override
- public @Nullable Region getTouchableRegion() {
- NotificationEntry topEntry = getTopEntry();
-
- // This call could be made in an inconsistent state while the pinnedMode hasn't been
- // updated yet, but callbacks leading out of the headsUp manager, querying it. Let's
- // therefore also check if the topEntry is null.
- if (!hasPinnedHeadsUp() || topEntry == null) {
- return null;
- } else {
- if (topEntry.rowIsChildInGroup()) {
- final NotificationEntry groupSummary =
- mGroupMembershipManager.getGroupSummary(topEntry);
- if (groupSummary != null) {
- topEntry = groupSummary;
- }
- }
- ExpandableNotificationRow topRow = topEntry.getRow();
- int[] tmpArray = new int[2];
- topRow.getLocationOnScreen(tmpArray);
- int minX = tmpArray[0];
- int maxX = tmpArray[0] + topRow.getWidth();
- int height = topRow.getIntrinsicHeight();
- final boolean stretchToTop = tmpArray[1] <= mHeadsUpInset;
- mTouchableRegion.set(minX, stretchToTop ? 0 : tmpArray[1], maxX, tmpArray[1] + height);
- return mTouchableRegion;
- }
- }
-
- /**
- * Decides whether a click is invalid for a notification, i.e it has not been shown long enough
- * that a user might have consciously clicked on it.
- *
- * @param key the key of the touched notification
- * @return whether the touch is invalid and should be discarded
- */
- @Override
- public boolean shouldSwallowClick(@NonNull String key) {
- BaseHeadsUpManager.HeadsUpEntry entry = getHeadsUpEntry(key);
- return entry != null && mSystemClock.elapsedRealtime() < entry.mPostTime;
- }
-
- @Override
- public void releaseAfterExpansion() {
- if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return;
- onExpandingFinished();
- }
-
- public void onExpandingFinished() {
- if (mReleaseOnExpandFinish) {
- releaseAllImmediately();
- mReleaseOnExpandFinish = false;
- } else {
- for (NotificationEntry entry: getAllEntries().toList()) {
- entry.setSeenInShade(true);
- }
- for (NotificationEntry entry : mEntriesToRemoveAfterExpand) {
- if (isHeadsUpEntry(entry.getKey())) {
- // Maybe the heads-up was removed already
- removeEntry(entry.getKey(), "onExpandingFinished");
- }
- }
- }
- mEntriesToRemoveAfterExpand.clear();
- }
-
- /**
- * Sets the tracking-heads-up flag. If the flag is true, HeadsUpManager doesn't remove the entry
- * from the list even after a Heads Up Notification is gone.
- */
- public void setTrackingHeadsUp(boolean trackingHeadsUp) {
- mTrackingHeadsUp = trackingHeadsUp;
- }
-
- private void onShadeOrQsExpanded(Boolean isExpanded) {
- if (isExpanded != mIsShadeOrQsExpanded) {
- mIsShadeOrQsExpanded = isExpanded;
- if (!SceneContainerFlag.isEnabled() && isExpanded) {
- mHeadsUpAnimatingAway.setValue(false);
- }
- }
- }
-
- private void onQsExpanded(Boolean isQsExpanded) {
- if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return;
- if (isQsExpanded != mIsQsExpanded) mIsQsExpanded = isQsExpanded;
- }
-
- /**
- * Set that we are exiting the headsUp pinned mode, but some notifications might still be
- * animating out. This is used to keep the touchable regions in a reasonable state.
- */
- @Override
- public void setHeadsUpAnimatingAway(boolean headsUpAnimatingAway) {
- if (headsUpAnimatingAway != mHeadsUpAnimatingAway.getValue()) {
- for (OnHeadsUpPhoneListenerChange listener : mHeadsUpPhoneListeners) {
- listener.onHeadsUpAnimatingAwayStateChanged(headsUpAnimatingAway);
- }
- mHeadsUpAnimatingAway.setValue(headsUpAnimatingAway);
- }
- }
-
- @Override
- public void unpinAll(boolean userUnPinned) {
- super.unpinAll(userUnPinned);
- }
-
- /**
- * Notifies that a remote input textbox in notification gets active or inactive.
- *
- * @param entry The entry of the target notification.
- * @param remoteInputActive True to notify active, False to notify inactive.
- */
- public void setRemoteInputActive(
- @NonNull NotificationEntry entry, boolean remoteInputActive) {
- HeadsUpEntryPhone headsUpEntry = getHeadsUpEntryPhone(entry.getKey());
- if (headsUpEntry != null && headsUpEntry.mRemoteInputActive != remoteInputActive) {
- headsUpEntry.mRemoteInputActive = remoteInputActive;
- if (ExpandHeadsUpOnInlineReply.isEnabled() && remoteInputActive) {
- headsUpEntry.mRemoteInputActivatedAtLeastOnce = true;
- }
- if (remoteInputActive) {
- headsUpEntry.cancelAutoRemovalCallbacks("setRemoteInputActive(true)");
- } else {
- headsUpEntry.updateEntry(false /* updatePostTime */, "setRemoteInputActive(false)");
- }
- onEntryUpdated(headsUpEntry);
- }
- }
-
- /**
- * Sets whether an entry's guts are exposed and therefore it should stick in the heads up
- * area if it's pinned until it's hidden again.
- */
- public void setGutsShown(@NonNull NotificationEntry entry, boolean gutsShown) {
- HeadsUpEntry headsUpEntry = getHeadsUpEntry(entry.getKey());
- if (!(headsUpEntry instanceof HeadsUpEntryPhone)) return;
- HeadsUpEntryPhone headsUpEntryPhone = (HeadsUpEntryPhone)headsUpEntry;
- if (entry.isRowPinned() || !gutsShown) {
- headsUpEntryPhone.setGutsShownPinned(gutsShown);
- }
- }
-
- /**
- * Extends the lifetime of the currently showing pulsing notification so that the pulse lasts
- * longer.
- */
- public void extendHeadsUp() {
- HeadsUpEntryPhone topEntry = getTopHeadsUpEntryPhone();
- if (topEntry == null) {
- return;
- }
- topEntry.extendPulse();
- }
-
- ///////////////////////////////////////////////////////////////////////////////////////////////
- // HeadsUpManager public methods overrides and overloads:
-
- @Override
- public boolean isTrackingHeadsUp() {
- return mTrackingHeadsUp;
- }
-
- @Override
- public void snooze() {
- super.snooze();
- mReleaseOnExpandFinish = true;
- }
-
- public void addSwipedOutNotification(@NonNull String key) {
- mSwipedOutKeys.add(key);
- }
-
- @Override
- public boolean removeNotification(@NonNull String key, boolean releaseImmediately,
- boolean animate, @NonNull String reason) {
- if (animate) {
- return removeNotification(key, releaseImmediately,
- "removeNotification(animate: true), reason: " + reason);
- } else {
- mAnimationStateHandler.setHeadsUpGoingAwayAnimationsAllowed(false);
- final boolean removed = removeNotification(key, releaseImmediately,
- "removeNotification(animate: false), reason: " + reason);
- mAnimationStateHandler.setHeadsUpGoingAwayAnimationsAllowed(true);
- return removed;
- }
- }
-
- ///////////////////////////////////////////////////////////////////////////////////////////////
- // Dumpable overrides:
-
- @Override
- public void dump(PrintWriter pw, String[] args) {
- pw.println("HeadsUpManagerPhone state:");
- dumpInternal(pw, args);
- }
-
- ///////////////////////////////////////////////////////////////////////////////////////////////
- // OnReorderingAllowedListener:
-
- private final OnReorderingAllowedListener mOnReorderingAllowedListener = () -> {
- if (NotificationThrottleHun.isEnabled()) {
- mAvalancheController.setEnableAtRuntime(true);
- if (mEntriesToRemoveWhenReorderingAllowed.isEmpty()) {
- return;
- }
- }
- mAnimationStateHandler.setHeadsUpGoingAwayAnimationsAllowed(false);
- for (NotificationEntry entry : mEntriesToRemoveWhenReorderingAllowed) {
- if (entry != null && isHeadsUpEntry(entry.getKey())) {
- // Maybe the heads-up was removed already
- removeEntry(entry.getKey(), "mOnReorderingAllowedListener");
- }
- }
- mEntriesToRemoveWhenReorderingAllowed.clear();
- mAnimationStateHandler.setHeadsUpGoingAwayAnimationsAllowed(true);
- };
-
- private final OnReorderingBannedListener mOnReorderingBannedListener = () -> {
- if (mAvalancheController != null) {
- // In open shade the first HUN is pinned, and visual stability logic prevents us from
- // unpinning this first HUN as long as the shade remains open. AvalancheController only
- // shows the next HUN when the currently showing HUN is unpinned, so we must disable
- // throttling here so that the incoming HUN stream is not forever paused. This is reset
- // when reorder becomes allowed.
- mAvalancheController.setEnableAtRuntime(false);
-
- // Note that we cannot do the above when
- // 1) The remove runnable runs because its delay means it may not run before shade close
- // 2) Reordering is allowed again (when shade closes) because the HUN appear animation
- // will have started by then
- }
- };
-
- ///////////////////////////////////////////////////////////////////////////////////////////////
- // HeadsUpManager utility (protected) methods overrides:
-
- @NonNull
- @Override
- protected HeadsUpEntry createHeadsUpEntry(NotificationEntry entry) {
- if (NotificationThrottleHun.isEnabled()) {
- return new HeadsUpEntryPhone(entry);
- } else {
- HeadsUpEntryPhone headsUpEntry = mEntryPool.acquire();
- headsUpEntry.setEntry(entry);
- return headsUpEntry;
- }
- }
-
- @Override
- protected void onEntryAdded(HeadsUpEntry headsUpEntry) {
- super.onEntryAdded(headsUpEntry);
- updateTopHeadsUpFlow();
- updateHeadsUpFlow();
- }
-
- @Override
- protected void onEntryUpdated(HeadsUpEntry headsUpEntry) {
- super.onEntryUpdated(headsUpEntry);
- // no need to update the list here
- updateTopHeadsUpFlow();
- }
-
- @Override
- protected void onEntryRemoved(HeadsUpEntry headsUpEntry) {
- super.onEntryRemoved(headsUpEntry);
- if (!NotificationThrottleHun.isEnabled()) {
- mEntryPool.release((HeadsUpEntryPhone) headsUpEntry);
- }
- updateTopHeadsUpFlow();
- updateHeadsUpFlow();
- if (NotificationThrottleHun.isEnabled()) {
- if (headsUpEntry.mEntry != null) {
- if (mEntriesToRemoveWhenReorderingAllowed.contains(headsUpEntry.mEntry)) {
- mEntriesToRemoveWhenReorderingAllowed.remove(headsUpEntry.mEntry);
- }
- }
- }
- }
-
- private void updateTopHeadsUpFlow() {
- mTopHeadsUpRow.setValue((HeadsUpRowRepository) getTopHeadsUpEntry());
- }
-
- private void updateHeadsUpFlow() {
- mHeadsUpNotificationRows.setValue(new HashSet<>(getHeadsUpEntryPhoneMap().values()));
- }
-
- @Override
- protected boolean shouldHeadsUpBecomePinned(NotificationEntry entry) {
- boolean pin = mStatusBarState == StatusBarState.SHADE && !mIsShadeOrQsExpanded;
- if (SceneContainerFlag.isEnabled()) {
- pin |= mIsQsExpanded;
- }
- if (mBypassController.getBypassEnabled()) {
- pin |= mStatusBarState == StatusBarState.KEYGUARD;
- }
- return pin || super.shouldHeadsUpBecomePinned(entry);
- }
-
- @Override
- protected void dumpInternal(PrintWriter pw, String[] args) {
- super.dumpInternal(pw, args);
- pw.print(" mBarState=");
- pw.println(mStatusBarState);
- pw.print(" mTouchableRegion=");
- pw.println(mTouchableRegion);
- }
-
- ///////////////////////////////////////////////////////////////////////////////////////////////
- // Private utility methods:
-
- @NonNull
- private ArrayMap<String, HeadsUpEntryPhone> getHeadsUpEntryPhoneMap() {
- //noinspection unchecked
- return (ArrayMap<String, HeadsUpEntryPhone>) ((ArrayMap) mHeadsUpEntryMap);
- }
-
- @Nullable
- private HeadsUpEntryPhone getHeadsUpEntryPhone(@NonNull String key) {
- return (HeadsUpEntryPhone) mHeadsUpEntryMap.get(key);
- }
-
- @Nullable
- private HeadsUpEntryPhone getTopHeadsUpEntryPhone() {
- if (SceneContainerFlag.isEnabled()) {
- return (HeadsUpEntryPhone) mTopHeadsUpRow.getValue();
- } else {
- return (HeadsUpEntryPhone) getTopHeadsUpEntry();
- }
- }
-
- @Override
- public boolean canRemoveImmediately(@NonNull String key) {
- if (mSwipedOutKeys.contains(key)) {
- // We always instantly dismiss views being manually swiped out.
- mSwipedOutKeys.remove(key);
- return true;
- }
-
- HeadsUpEntryPhone headsUpEntry = getHeadsUpEntryPhone(key);
- HeadsUpEntryPhone topEntry = getTopHeadsUpEntryPhone();
-
- return headsUpEntry == null || headsUpEntry != topEntry || super.canRemoveImmediately(key);
- }
-
- @Override
- @NonNull
- public Flow<HeadsUpRowRepository> getTopHeadsUpRow() {
- return mTopHeadsUpRow;
- }
-
- @Override
- @NonNull
- public Flow<Set<HeadsUpRowRepository>> getActiveHeadsUpRows() {
- return mHeadsUpNotificationRows;
- }
-
- @Override
- @NonNull
- public StateFlow<Boolean> isHeadsUpAnimatingAway() {
- return mHeadsUpAnimatingAway;
- }
-
- @Override
- public boolean isHeadsUpAnimatingAwayValue() {
- return mHeadsUpAnimatingAway.getValue();
- }
-
- ///////////////////////////////////////////////////////////////////////////////////////////////
- // HeadsUpEntryPhone:
-
- protected class HeadsUpEntryPhone extends BaseHeadsUpManager.HeadsUpEntry implements
- HeadsUpRowRepository {
-
- private boolean mGutsShownPinned;
- private final MutableStateFlow<Boolean> mIsPinned = StateFlowKt.MutableStateFlow(false);
-
- /**
- * If the time this entry has been on was extended
- */
- private boolean extended;
-
- @Override
- public boolean isSticky() {
- return super.isSticky() || mGutsShownPinned;
- }
-
- public HeadsUpEntryPhone() {
- super();
- }
-
- public HeadsUpEntryPhone(NotificationEntry entry) {
- super(entry);
- }
-
- @Override
- @NonNull
- public String getKey() {
- return requireEntry().getKey();
- }
-
- @Override
- @NonNull
- public StateFlow<Boolean> isPinned() {
- return mIsPinned;
- }
-
- @Override
- protected void setRowPinned(boolean pinned) {
- // TODO(b/327624082): replace this super call with a ViewBinder
- super.setRowPinned(pinned);
- mIsPinned.setValue(pinned);
- }
-
- @Override
- protected void setEntry(@androidx.annotation.NonNull NotificationEntry entry,
- @androidx.annotation.Nullable Runnable removeRunnable) {
- super.setEntry(entry, removeRunnable);
-
- if (NotificationThrottleHun.isEnabled()) {
- mEntriesToRemoveWhenReorderingAllowed.add(entry);
- if (!mVisualStabilityProvider.isReorderingAllowed()) {
- entry.setSeenInShade(true);
- }
- }
- }
-
- @Override
- protected Runnable createRemoveRunnable(NotificationEntry entry) {
- return () -> {
- if (!NotificationThrottleHun.isEnabled()
- && !mVisualStabilityProvider.isReorderingAllowed()
- // We don't want to allow reordering while pulsing, but headsup need to
- // time out anyway
- && !entry.showingPulsing()) {
- mEntriesToRemoveWhenReorderingAllowed.add(entry);
- mVisualStabilityProvider.addTemporaryReorderingAllowedListener(
- mOnReorderingAllowedListener);
- } else if (mTrackingHeadsUp) {
- mEntriesToRemoveAfterExpand.add(entry);
- mLogger.logRemoveEntryAfterExpand(entry);
- } else if (mVisualStabilityProvider.isReorderingAllowed()
- || entry.showingPulsing()) {
- removeEntry(entry.getKey(), "createRemoveRunnable");
- }
- };
- }
-
- @Override
- public void updateEntry(boolean updatePostTime, String reason) {
- super.updateEntry(updatePostTime, reason);
-
- if (mEntriesToRemoveAfterExpand.contains(mEntry)) {
- mEntriesToRemoveAfterExpand.remove(mEntry);
- }
- if (!NotificationThrottleHun.isEnabled()) {
- if (mEntriesToRemoveWhenReorderingAllowed.contains(mEntry)) {
- mEntriesToRemoveWhenReorderingAllowed.remove(mEntry);
- }
- }
- }
-
- @Override
- public void setExpanded(boolean expanded) {
- if (this.mExpanded == expanded) {
- return;
- }
-
- this.mExpanded = expanded;
- if (expanded) {
- cancelAutoRemovalCallbacks("setExpanded(true)");
- } else {
- updateEntry(false /* updatePostTime */, "setExpanded(false)");
- }
- }
-
- public void setGutsShownPinned(boolean gutsShownPinned) {
- if (mGutsShownPinned == gutsShownPinned) {
- return;
- }
-
- mGutsShownPinned = gutsShownPinned;
- if (gutsShownPinned) {
- cancelAutoRemovalCallbacks("setGutsShownPinned(true)");
- } else {
- updateEntry(false /* updatePostTime */, "setGutsShownPinned(false)");
- }
- }
-
- @Override
- public void reset() {
- super.reset();
- mGutsShownPinned = false;
- extended = false;
- }
-
- private void extendPulse() {
- if (!extended) {
- extended = true;
- updateEntry(false, "extendPulse()");
- }
- }
-
- @Override
- protected long calculateFinishTime() {
- return super.calculateFinishTime() + (extended ? mExtensionTime : 0);
- }
-
- @Override
- @NonNull
- public Object getElementKey() {
- return requireEntry().getRow();
- }
-
- private NotificationEntry requireEntry() {
- /* check if */ SceneContainerFlag.isUnexpectedlyInLegacyMode();
- return Objects.requireNonNull(mEntry);
- }
- }
-
- private final StateListener mStatusBarStateListener = new StateListener() {
- @Override
- public void onStateChanged(int newState) {
- boolean wasKeyguard = mStatusBarState == StatusBarState.KEYGUARD;
- boolean isKeyguard = newState == StatusBarState.KEYGUARD;
- mStatusBarState = newState;
-
- if (wasKeyguard && !isKeyguard && mBypassController.getBypassEnabled()) {
- ArrayList<String> keysToRemove = new ArrayList<>();
- for (HeadsUpEntry entry : getHeadsUpEntryList()) {
- if (entry.mEntry != null && entry.mEntry.isBubble() && !entry.isSticky()) {
- keysToRemove.add(entry.mEntry.getKey());
- }
- }
- for (String key : keysToRemove) {
- removeEntry(key, "mStatusBarStateListener");
- }
- }
- }
-
- @Override
- public void onDozingChanged(boolean isDozing) {
- if (!isDozing) {
- // Let's make sure all huns we got while dozing time out within the normal timeout
- // duration. Otherwise they could get stuck for a very long time
- for (HeadsUpEntry entry : getHeadsUpEntryList()) {
- entry.updateEntry(true /* updatePostTime */, "onDozingChanged(false)");
- }
- }
- }
- };
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
index 684ce48..42aadd1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
@@ -76,6 +76,7 @@
import com.android.systemui.statusbar.notification.interruption.VisualInterruptionRefactor;
import com.android.systemui.statusbar.notification.logging.NotificationPanelLogger;
import com.android.systemui.statusbar.notification.logging.NotificationPanelLoggerImpl;
+import com.android.systemui.statusbar.notification.logging.dagger.NotificationsLogModule;
import com.android.systemui.statusbar.notification.row.NotificationEntryProcessorFactory;
import com.android.systemui.statusbar.notification.row.NotificationEntryProcessorFactoryLooperImpl;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
@@ -116,6 +117,7 @@
ActivatableNotificationViewModelModule.class,
NotificationMemoryModule.class,
NotificationStatsLoggerModule.class,
+ NotificationsLogModule.class,
})
public interface NotificationsModule {
@Binds
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/NotificationDataLayerModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/NotificationDataLayerModule.kt
index 63c9e8b..cf4fb25 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/NotificationDataLayerModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/NotificationDataLayerModule.kt
@@ -16,16 +16,11 @@
package com.android.systemui.statusbar.notification.data
import com.android.systemui.statusbar.notification.data.repository.HeadsUpRepository
-import com.android.systemui.statusbar.notification.HeadsUpManagerPhone
+import com.android.systemui.statusbar.policy.BaseHeadsUpManager
import dagger.Binds
import dagger.Module
-@Module(
- includes =
- [
- NotificationSettingsRepositoryModule::class,
- ]
-)
+@Module(includes = [NotificationSettingsRepositoryModule::class])
interface NotificationDataLayerModule {
- @Binds fun bindHeadsUpNotificationRepository(impl: HeadsUpManagerPhone): HeadsUpRepository
+ @Binds fun bindHeadsUpNotificationRepository(impl: BaseHeadsUpManager): HeadsUpRepository
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationAlertsInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationAlertsInteractor.kt
index 8079ce5..4ea597a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationAlertsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationAlertsInteractor.kt
@@ -17,17 +17,15 @@
package com.android.systemui.statusbar.notification.domain.interactor
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.statusbar.disableflags.data.repository.DisableFlagsRepository
+import com.android.systemui.statusbar.disableflags.domain.interactor.DisableFlagsInteractor
import javax.inject.Inject
/** Interactor for notification alerting. */
@SysUISingleton
class NotificationAlertsInteractor
@Inject
-constructor(
- private val disableFlagsRepository: DisableFlagsRepository,
-) {
+constructor(private val disableFlagsInteractor: DisableFlagsInteractor) {
/** Returns true if notification alerts are allowed. */
fun areNotificationAlertsEnabled(): Boolean =
- disableFlagsRepository.disableFlags.value.areNotificationAlertsEnabled()
+ disableFlagsInteractor.disableFlags.value.areNotificationAlertsEnabled()
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/dagger/NotificationsLogModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/dagger/NotificationsLogModule.kt
new file mode 100644
index 0000000..d3359d3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/dagger/NotificationsLogModule.kt
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.logging.dagger
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.LogBufferFactory
+import com.android.systemui.log.dagger.NotifInflationLog
+import com.android.systemui.log.dagger.NotifInteractionLog
+import com.android.systemui.log.dagger.NotificationHeadsUpLog
+import com.android.systemui.log.dagger.NotificationInterruptLog
+import com.android.systemui.log.dagger.NotificationLockscreenLog
+import com.android.systemui.log.dagger.NotificationLog
+import com.android.systemui.log.dagger.NotificationRemoteInputLog
+import com.android.systemui.log.dagger.NotificationRenderLog
+import com.android.systemui.log.dagger.NotificationSectionLog
+import com.android.systemui.log.dagger.SensitiveNotificationProtectionLog
+import com.android.systemui.log.dagger.UnseenNotificationLog
+import com.android.systemui.log.dagger.VisualStabilityLog
+import com.android.systemui.statusbar.notification.NotifPipelineFlags
+import com.android.systemui.statusbar.notification.promoted.PromotedNotificationLog
+import com.android.systemui.util.Compile
+import dagger.Module
+import dagger.Provides
+
+@Module
+object NotificationsLogModule {
+ /** Provides a logging buffer for logs related to heads up presentation of notifications. */
+ @Provides
+ @SysUISingleton
+ @NotificationHeadsUpLog
+ fun provideNotificationHeadsUpLogBuffer(factory: LogBufferFactory): LogBuffer {
+ return factory.create("NotifHeadsUpLog", 1000)
+ }
+
+ /** Provides a logging buffer for logs related to inflation of notifications. */
+ @Provides
+ @SysUISingleton
+ @NotifInflationLog
+ fun provideNotifInflationLogBuffer(factory: LogBufferFactory): LogBuffer {
+ return factory.create("NotifInflationLog", 250)
+ }
+
+ /** Provides a logging buffer for all logs related to the data layer of notifications. */
+ @Provides
+ @SysUISingleton
+ @NotifInteractionLog
+ fun provideNotifInteractionLogBuffer(factory: LogBufferFactory): LogBuffer {
+ return factory.create("NotifInteractionLog", 50)
+ }
+
+ /** Provides a logging buffer for notification interruption calculations. */
+ @Provides
+ @SysUISingleton
+ @NotificationInterruptLog
+ fun provideNotificationInterruptLogBuffer(factory: LogBufferFactory): LogBuffer {
+ return factory.create("NotifInterruptLog", 100)
+ }
+
+ /** Provides a logging buffer for all logs related to notifications on the lockscreen. */
+ @Provides
+ @SysUISingleton
+ @NotificationLockscreenLog
+ fun provideNotificationLockScreenLogBuffer(factory: LogBufferFactory): LogBuffer {
+ return factory.create("NotifLockscreenLog", 50, false /* systrace */)
+ }
+
+ /** Provides a logging buffer for all logs related to the data layer of notifications. */
+ @Provides
+ @SysUISingleton
+ @NotificationLog
+ fun provideNotificationsLogBuffer(
+ factory: LogBufferFactory,
+ notifPipelineFlags: NotifPipelineFlags,
+ ): LogBuffer {
+ var maxSize = 1000
+ if (Compile.IS_DEBUG && notifPipelineFlags.isDevLoggingEnabled()) {
+ maxSize *= 10
+ }
+ return factory.create("NotifLog", maxSize, Compile.IS_DEBUG /* systrace */)
+ }
+
+ /** Provides a logging buffer for all logs related to remote input controller. */
+ @Provides
+ @SysUISingleton
+ @NotificationRemoteInputLog
+ fun provideNotificationRemoteInputLogBuffer(factory: LogBufferFactory): LogBuffer {
+ return factory.create("NotifRemoteInputLog", 50, /* maxSize */ false /* systrace */)
+ }
+
+ /** Provides a logging buffer for notification rendering events. */
+ @Provides
+ @SysUISingleton
+ @NotificationRenderLog
+ fun provideNotificationRenderLogBuffer(factory: LogBufferFactory): LogBuffer {
+ return factory.create("NotifRenderLog", 100)
+ }
+
+ /** Provides a logging buffer for all logs related to managing notification sections. */
+ @Provides
+ @SysUISingleton
+ @NotificationSectionLog
+ fun provideNotificationSectionLogBuffer(factory: LogBufferFactory): LogBuffer {
+ return factory.create("NotifSectionLog", 1000, /* maxSize */ false /* systrace */)
+ }
+
+ /** Provides a [LogBuffer] for use by promoted notifications. */
+ @Provides
+ @SysUISingleton
+ @PromotedNotificationLog
+ fun providesPromotedNotificationLog(factory: LogBufferFactory): LogBuffer {
+ return factory.create("PromotedNotifLog", 50)
+ }
+
+ /** */
+ @Provides
+ @SysUISingleton
+ @SensitiveNotificationProtectionLog
+ fun provideSensitiveNotificationProtectionLogBuffer(factory: LogBufferFactory): LogBuffer {
+ return factory.create("SensitiveNotificationProtectionLog", 10)
+ }
+
+ /** Provides a logging buffer for all logs related to unseen notifications. */
+ @Provides
+ @SysUISingleton
+ @UnseenNotificationLog
+ fun provideUnseenNotificationLogBuffer(factory: LogBufferFactory): LogBuffer {
+ return factory.create("UnseenNotifLog", 20, /* maxSize */ false /* systrace */)
+ }
+
+ /** Provides a logging buffer for all logs related to notification visual stability. */
+ @Provides
+ @SysUISingleton
+ @VisualStabilityLog
+ fun provideVisualStabilityLogBuffer(factory: LogBufferFactory): LogBuffer {
+ return factory.create("VisualStabilityLog", 50, /* maxSize */ false /* systrace */)
+ }
+}
diff --git a/core/java/android/text/ClientFlags.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationLog.kt
similarity index 60%
copy from core/java/android/text/ClientFlags.java
copy to packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationLog.kt
index ca88764..f9d9c97 100644
--- a/core/java/android/text/ClientFlags.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationLog.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,16 +14,12 @@
* limitations under the License.
*/
-package android.text;
+package com.android.systemui.statusbar.notification.promoted
-/**
- * An aconfig feature flags that can be accessible from application process without
- * ContentProvider IPCs.
- *
- * When you add new flags, you have to add flag string to {@link TextFlags#TEXT_ACONFIGS_FLAGS}.
- *
- * TODO(nona): Remove this class.
- * @hide
- */
-public class ClientFlags {
-}
+import javax.inject.Qualifier
+
+/** A [com.android.systemui.log.LogBuffer] for use by promoted notifications. */
+@Qualifier
+@MustBeDocumented
+@Retention(AnnotationRetention.RUNTIME)
+annotation class PromotedNotificationLog
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 08d177f..d1de6be 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
@@ -1543,7 +1543,6 @@
setDragController(null);
mGroupParentWhenDismissed = mNotificationParent;
mChildAfterViewWhenDismissed = null;
- mEntry.getIcons().getStatusBarIcon().setDismissed();
if (isChildInGroup()) {
List<ExpandableNotificationRow> notificationChildren =
mNotificationParent.getAttachedChildren();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridConversationNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridConversationNotificationView.java
index 0738a03..1ff0d92 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridConversationNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridConversationNotificationView.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.Flags;
import android.content.Context;
import android.content.res.ColorStateList;
import android.graphics.drawable.Icon;
@@ -93,12 +94,22 @@
}
mConversationSenderName = requireViewById(R.id.conversation_notification_sender);
applyTextColor(mConversationSenderName, mSecondaryTextColor);
- mFacePileSize = getResources()
- .getDimensionPixelSize(R.dimen.conversation_single_line_face_pile_size);
- mFacePileAvatarSize = getResources()
- .getDimensionPixelSize(R.dimen.conversation_single_line_face_pile_avatar_size);
- mSingleAvatarSize = getResources()
- .getDimensionPixelSize(R.dimen.conversation_single_line_avatar_size);
+ if (Flags.notificationsRedesignTemplates()) {
+ mFacePileSize = getResources()
+ .getDimensionPixelSize(R.dimen.notification_2025_single_line_face_pile_size);
+ mFacePileAvatarSize = getResources()
+ .getDimensionPixelSize(
+ R.dimen.notification_2025_single_line_face_pile_avatar_size);
+ mSingleAvatarSize = getResources()
+ .getDimensionPixelSize(R.dimen.notification_2025_single_line_avatar_size);
+ } else {
+ mFacePileSize = getResources()
+ .getDimensionPixelSize(R.dimen.conversation_single_line_face_pile_size);
+ mFacePileAvatarSize = getResources()
+ .getDimensionPixelSize(R.dimen.conversation_single_line_face_pile_avatar_size);
+ mSingleAvatarSize = getResources()
+ .getDimensionPixelSize(R.dimen.conversation_single_line_avatar_size);
+ }
mFacePileProtectionWidth = getResources().getDimensionPixelSize(
R.dimen.conversation_single_line_face_pile_protection_width);
mTransformationHelper.setCustomTransformation(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridGroupManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridGroupManager.java
index 09c0349..e5e559f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridGroupManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridGroupManager.java
@@ -63,9 +63,8 @@
private HybridNotificationView inflateHybridView(View contentView, ViewGroup parent) {
Trace.beginSection("HybridGroupManager#inflateHybridView");
LayoutInflater inflater = LayoutInflater.from(mContext);
- int layout = contentView instanceof ConversationLayout
- ? R.layout.hybrid_conversation_notification
- : R.layout.hybrid_notification;
+ int layout = HybridNotificationView.getLayoutResource(
+ /* isConversation = */ contentView instanceof ConversationLayout);
HybridNotificationView hybrid = (HybridNotificationView)
inflater.inflate(layout, parent, false);
parent.addView(hybrid);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridNotificationView.java
index da8c4dc..61f4e96 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridNotificationView.java
@@ -19,6 +19,7 @@
import static android.app.Notification.COLOR_INVALID;
import android.annotation.Nullable;
+import android.app.Flags;
import android.content.Context;
import android.content.res.TypedArray;
import android.text.TextUtils;
@@ -73,6 +74,25 @@
return mTextView;
}
+ /**
+ * Get layout resource for this view based on {@param isConversation}.
+ */
+ public static int getLayoutResource(boolean isConversation) {
+ if (Flags.notificationsRedesignTemplates()) {
+ if (isConversation) {
+ return R.layout.notification_2025_hybrid_conversation;
+ } else {
+ return R.layout.notification_2025_hybrid;
+ }
+ } else {
+ if (isConversation) {
+ return R.layout.hybrid_conversation_notification;
+ } else {
+ return R.layout.hybrid_notification;
+ }
+ }
+ }
+
@Override
protected void onFinishInflate() {
super.onFinishInflate();
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 9166e7e..786d7d9 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
@@ -573,8 +573,29 @@
return;
}
- mUiEventLogger.log(
- NotificationCompactHeadsUpEvent.NOTIFICATION_COMPACT_HUN_SHOWN);
+ final StatusBarNotification containingRowSbn = getContainingRowSbn();
+ if (containingRowSbn == null) {
+ return;
+ }
+
+ mUiEventLogger.logWithInstanceId(
+ NotificationCompactHeadsUpEvent.NOTIFICATION_COMPACT_HUN_SHOWN,
+ containingRowSbn.getUid(),
+ containingRowSbn.getPackageName(),
+ containingRowSbn.getInstanceId());
+ }
+
+ @Nullable
+ private StatusBarNotification getContainingRowSbn() {
+ if (mContainingNotification == null) {
+ return null;
+ }
+ final NotificationEntry entry = mContainingNotification.getEntry();
+ if (entry == null) {
+ return null;
+ }
+
+ return entry.getSbn();
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt
index 2dcb706..d0c033b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt
@@ -834,6 +834,15 @@
public?.let {
it.layoutInflaterFactory = provider.provide(row, FLAG_CONTENT_VIEW_PUBLIC)
}
+ if (android.app.Flags.notificationsRedesignAppIcons()) {
+ normalGroupHeader?.let {
+ it.layoutInflaterFactory = provider.provide(row, FLAG_GROUP_SUMMARY_HEADER)
+ }
+ minimizedGroupHeader?.let {
+ it.layoutInflaterFactory =
+ provider.provide(row, FLAG_LOW_PRIORITY_GROUP_SUMMARY_HEADER)
+ }
+ }
return this
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/SingleLineViewInflater.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/SingleLineViewInflater.kt
index 4e26ae8..e702f10 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/SingleLineViewInflater.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/SingleLineViewInflater.kt
@@ -412,10 +412,7 @@
traceSection("SingleLineViewInflater#inflateSingleLineView") {
val inflater = LayoutInflater.from(context)
- val layoutRes: Int =
- if (isConversation)
- com.android.systemui.res.R.layout.hybrid_conversation_notification
- else com.android.systemui.res.R.layout.hybrid_notification
+ val layoutRes: Int = HybridNotificationView.getLayoutResource(isConversation)
view = inflater.inflate(layoutRes, /* root= */ null) as HybridNotificationView
if (view == null) {
Log.wtf(TAG, "Single-line view inflation result is null for entry: ${entry.logKey}")
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpModule.kt
index 7919c84..83551e9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpModule.kt
@@ -17,12 +17,12 @@
package com.android.systemui.statusbar.phone
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.statusbar.notification.HeadsUpManagerPhone
+import com.android.systemui.statusbar.policy.BaseHeadsUpManager
import com.android.systemui.statusbar.policy.HeadsUpManager
import dagger.Binds
import dagger.Module
@Module
interface HeadsUpModule {
- @Binds @SysUISingleton fun bindsHeadsUpManager(hum: HeadsUpManagerPhone): HeadsUpManager
+ @Binds @SysUISingleton fun bindsHeadsUpManager(hum: BaseHeadsUpManager): HeadsUpManager
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ManagedProfileControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ManagedProfileControllerImpl.java
index 7ef1e41..5837a49 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ManagedProfileControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ManagedProfileControllerImpl.java
@@ -31,6 +31,7 @@
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.Executor;
+import java.util.function.Consumer;
import javax.inject.Inject;
@@ -63,17 +64,21 @@
@Override
public void addCallback(@NonNull Callback callback) {
- mCallbacks.add(callback);
- if (mCallbacks.size() == 1) {
- setListening(true);
+ synchronized (mCallbacks) {
+ mCallbacks.add(callback);
+ if (mCallbacks.size() == 1) {
+ setListening(true);
+ }
+ callback.onManagedProfileChanged();
}
- callback.onManagedProfileChanged();
}
@Override
public void removeCallback(@NonNull Callback callback) {
- if (mCallbacks.remove(callback) && mCallbacks.size() == 0) {
- setListening(false);
+ synchronized (mCallbacks) {
+ if (mCallbacks.remove(callback) && mCallbacks.size() == 0) {
+ setListening(false);
+ }
}
}
@@ -109,10 +114,7 @@
}
private void notifyManagedProfileRemoved() {
- ArrayList<Callback> copy = new ArrayList<>(mCallbacks);
- for (Callback callback : copy) {
- callback.onManagedProfileRemoved();
- }
+ notifyCallbacks(Callback::onManagedProfileRemoved);
}
public boolean hasActiveProfile() {
@@ -136,6 +138,16 @@
}
}
+ private void notifyCallbacks(Consumer<Callback> method) {
+ ArrayList<Callback> copy;
+ synchronized (mCallbacks) {
+ copy = new ArrayList<>(mCallbacks);
+ }
+ for (Callback callback : copy) {
+ method.accept(callback);
+ }
+ }
+
private void setListening(boolean listening) {
if (mListening == listening) {
return;
@@ -154,19 +166,13 @@
@Override
public void onUserChanged(int newUser, @NonNull Context userContext) {
reloadManagedProfiles();
- ArrayList<Callback> copy = new ArrayList<>(mCallbacks);
- for (Callback callback : copy) {
- callback.onManagedProfileChanged();
- }
+ notifyCallbacks(Callback::onManagedProfileChanged);
}
@Override
public void onProfilesChanged(@NonNull List<UserInfo> profiles) {
reloadManagedProfiles();
- ArrayList<Callback> copy = new ArrayList<>(mCallbacks);
- for (Callback callback : copy) {
- callback.onManagedProfileChanged();
- }
+ notifyCallbacks(Callback::onManagedProfileChanged);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModel.kt
index 13ac321..f3d5139 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModel.kt
@@ -113,34 +113,38 @@
}
private val showIcon =
- canShowIcon
- .flatMapLatest { canShow ->
- if (!canShow) {
- flowOf(false)
- } else {
- combine(
- shouldShowIconForOosAfterHysteresis,
- interactor.connectionState,
- interactor.isWifiActive,
- airplaneModeRepository.isAirplaneMode,
- ) { showForOos, connectionState, isWifiActive, isAirplaneMode ->
- if (isWifiActive || isAirplaneMode) {
- false
- } else {
- showForOos ||
- connectionState == SatelliteConnectionState.On ||
- connectionState == SatelliteConnectionState.Connected
+ if (interactor.isOpportunisticSatelliteIconEnabled) {
+ canShowIcon
+ .flatMapLatest { canShow ->
+ if (!canShow) {
+ flowOf(false)
+ } else {
+ combine(
+ shouldShowIconForOosAfterHysteresis,
+ interactor.connectionState,
+ interactor.isWifiActive,
+ airplaneModeRepository.isAirplaneMode,
+ ) { showForOos, connectionState, isWifiActive, isAirplaneMode ->
+ if (isWifiActive || isAirplaneMode) {
+ false
+ } else {
+ showForOos ||
+ connectionState == SatelliteConnectionState.On ||
+ connectionState == SatelliteConnectionState.Connected
+ }
}
}
}
+ .distinctUntilChanged()
+ .logDiffsForTable(
+ tableLog,
+ columnPrefix = "vm",
+ columnName = COL_VISIBLE,
+ initialValue = false,
+ )
+ } else {
+ flowOf(false)
}
- .distinctUntilChanged()
- .logDiffsForTable(
- tableLog,
- columnPrefix = "vm",
- columnName = COL_VISIBLE,
- initialValue = false,
- )
.stateIn(scope, SharingStarted.WhileSubscribed(), false)
override val icon: StateFlow<Icon?> =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/domain/interactor/CollapsedStatusBarInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/domain/interactor/CollapsedStatusBarInteractor.kt
index 9164da7..b2a0272 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/domain/interactor/CollapsedStatusBarInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/domain/interactor/CollapsedStatusBarInteractor.kt
@@ -17,7 +17,7 @@
package com.android.systemui.statusbar.pipeline.shared.domain.interactor
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.statusbar.disableflags.data.repository.DisableFlagsRepository
+import com.android.systemui.statusbar.disableflags.domain.interactor.DisableFlagsInteractor
import com.android.systemui.statusbar.pipeline.shared.domain.model.StatusBarDisableFlagsVisibilityModel
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
@@ -30,13 +30,13 @@
@SysUISingleton
class CollapsedStatusBarInteractor
@Inject
-constructor(disableFlagsRepository: DisableFlagsRepository) {
+constructor(DisableFlagsInteractor: DisableFlagsInteractor) {
/**
* The visibilities of various status bar child views, based only on the information we received
* from disable flags.
*/
val visibilityViaDisableFlags: Flow<StatusBarDisableFlagsVisibilityModel> =
- disableFlagsRepository.disableFlags.map {
+ DisableFlagsInteractor.disableFlags.map {
StatusBarDisableFlagsVisibilityModel(
isClockAllowed = it.isClockEnabled,
areNotificationIconsAllowed = it.areNotificationIconsEnabled,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImpl.kt
index c7b6be3..a72c83e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImpl.kt
@@ -92,8 +92,7 @@
private var wifiPickerTracker: WifiPickerTracker? = null
- @VisibleForTesting
- val selectedUserContext: Flow<Context> =
+ private val selectedUserContext: Flow<Context> =
userRepository.selectedUserInfo.map {
applicationContext.createContextAsUser(UserHandle.of(it.id), /* flags= */ 0)
}
@@ -112,8 +111,7 @@
selectedUserContext
.flatMapLatest { currentContext
-> // flatMapLatest because when selectedUserContext emits a new value, we want
- // to
- // re-create a whole flow
+ // to re-create a whole flow
callbackFlow {
val callback =
object : WifiPickerTracker.WifiPickerTrackerCallback {
@@ -132,20 +130,16 @@
.map { it.toWifiNetworkModel() }
// [WifiPickerTracker.connectedWifiEntry] will return the
- // same
- // instance but with updated internals. For example, when
- // its
- // validation status changes from false to true, the same
- // instance is re-used but with the validated field updated.
+ // same instance but with updated internals. For example,
+ // when its validation status changes from false to true,
+ // the same instance is re-used but with the validated
+ // field updated.
//
// Because it's the same instance, the flow won't re-emit
- // the
- // value (even though the internals have changed). So, we
- // need
- // to transform it into our internal model immediately.
- // [toWifiNetworkModel] always returns a new instance, so
- // the
- // flow is guaranteed to emit.
+ // the value (even though the internals have changed). So,
+ // we need to transform it into our internal model
+ // immediately. [toWifiNetworkModel] always returns a new
+ // instance, so the flow is guaranteed to emit.
send(
newPrimaryNetwork =
connectedEntry?.toPrimaryWifiNetworkModel()
@@ -235,20 +229,15 @@
.map { it.toWifiNetworkModel() }
// [WifiPickerTracker.connectedWifiEntry] will return the same
- // instance
- // but with updated internals. For example, when its validation
- // status
- // changes from false to true, the same instance is re-used but
- // with the
- // validated field updated.
+ // instance but with updated internals. For example, when its
+ // validation status changes from false to true, the same
+ // instance is re-used but with the validated field updated.
//
// Because it's the same instance, the flow won't re-emit the
- // value
- // (even though the internals have changed). So, we need to
- // transform it
- // into our internal model immediately. [toWifiNetworkModel]
- // always
- // returns a new instance, so the flow is guaranteed to emit.
+ // value (even though the internals have changed). So, we need
+ // to transform it into our internal model immediately.
+ // [toWifiNetworkModel] always returns a new instance, so the
+ // flow is guaranteed to emit.
send(
newPrimaryNetwork =
connectedEntry?.toPrimaryWifiNetworkModel()
@@ -292,12 +281,10 @@
.create(applicationContext, lifecycle, callback, "WifiRepository")
.apply {
// By default, [WifiPickerTracker] will scan to see all
- // available wifi
- // networks in the area. Because SysUI only needs to display the
- // **connected** network, we don't need scans to be running (and
- // in fact,
- // running scans is costly and should be avoided whenever
- // possible).
+ // available wifi networks in the area. Because SysUI only
+ // needs to display the **connected** network, we don't
+ // need scans to be running (and in fact, running scans is
+ // costly and should be avoided whenever possible).
this?.disableScanning()
}
// The lifecycle must be STARTED in order for the callback to receive
@@ -382,16 +369,11 @@
private fun MergedCarrierEntry.convertCarrierMergedToModel(): WifiNetworkModel {
// WifiEntry instance values aren't guaranteed to be stable between method calls
- // because
- // WifiPickerTracker is continuously updating the same object. Save the level in a
- // local
- // variable so that checking the level validity here guarantees that the level will
- // still be
- // valid when we create the `WifiNetworkModel.Active` instance later. Otherwise, the
- // level
- // could be valid here but become invalid later, and `WifiNetworkModel.Active` will
- // throw
- // an exception. See b/362384551.
+ // because WifiPickerTracker is continuously updating the same object. Save the
+ // level in a local variable so that checking the level validity here guarantees
+ // that the level will still be valid when we create the `WifiNetworkModel.Active`
+ // instance later. Otherwise, the level could be valid here but become invalid
+ // later, and `WifiNetworkModel.Active` will throw an exception. See b/362384551.
return WifiNetworkModel.CarrierMerged.of(
subscriptionId = this.subscriptionId,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java
index 4284c19..f6f567f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java
@@ -16,33 +16,48 @@
package com.android.systemui.statusbar.policy;
-import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_HEADS_UP;
-
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.Notification;
import android.content.Context;
import android.content.res.Resources;
import android.database.ContentObserver;
+import android.graphics.Region;
import android.os.Handler;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
+import android.util.Pools;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEvent;
import com.android.internal.logging.UiEventLogger;
+import com.android.internal.policy.SystemBarUtils;
import com.android.systemui.EventLogTags;
+import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.res.R;
+import com.android.systemui.scene.shared.flag.SceneContainerFlag;
+import com.android.systemui.shade.domain.interactor.ShadeInteractor;
+import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
+import com.android.systemui.statusbar.notification.collection.provider.OnReorderingAllowedListener;
+import com.android.systemui.statusbar.notification.collection.provider.OnReorderingBannedListener;
+import com.android.systemui.statusbar.notification.collection.provider.VisualStabilityProvider;
+import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
+import com.android.systemui.statusbar.notification.data.repository.HeadsUpRepository;
+import com.android.systemui.statusbar.notification.data.repository.HeadsUpRowRepository;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.shared.NotificationThrottleHun;
import com.android.systemui.statusbar.phone.ExpandHeadsUpOnInlineReply;
+import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.util.ListenerSet;
import com.android.systemui.util.concurrency.DelayableExecutor;
+import com.android.systemui.util.kotlin.JavaAdapter;
import com.android.systemui.util.settings.GlobalSettings;
import com.android.systemui.util.time.SystemClock;
@@ -50,14 +65,27 @@
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.HashSet;
import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.Stack;
import java.util.stream.Stream;
+import javax.inject.Inject;
+
+import kotlinx.coroutines.flow.Flow;
+import kotlinx.coroutines.flow.MutableStateFlow;
+import kotlinx.coroutines.flow.StateFlow;
+import kotlinx.coroutines.flow.StateFlowKt;
+
/**
* A manager which handles heads up notifications which is a special mode where
* they simply peek from the top of the screen.
*/
-public abstract class BaseHeadsUpManager implements HeadsUpManager {
+@SysUISingleton
+public class BaseHeadsUpManager
+ implements HeadsUpManager, HeadsUpRepository, OnHeadsUpChangedListener {
private static final String TAG = "BaseHeadsUpManager";
private static final String SETTING_HEADS_UP_SNOOZE_LENGTH_MS = "heads_up_snooze_length_ms";
@@ -74,7 +102,11 @@
private final AccessibilityManagerWrapper mAccessibilityMgr;
private final UiEventLogger mUiEventLogger;
- private final AvalancheController mAvalancheController;
+ private AvalancheController mAvalancheController;
+ private final KeyguardBypassController mBypassController;
+ private final GroupMembershipManager mGroupMembershipManager;
+ private final List<OnHeadsUpPhoneListenerChange> mHeadsUpPhoneListeners = new ArrayList<>();
+ private final VisualStabilityProvider mVisualStabilityProvider;
protected final SystemClock mSystemClock;
protected final ArrayMap<String, HeadsUpEntry> mHeadsUpEntryMap = new ArrayMap<>();
@@ -84,6 +116,53 @@
protected int mAutoDismissTime;
protected DelayableExecutor mExecutor;
+ @VisibleForTesting
+ public final int mExtensionTime;
+
+ // TODO(b/328393698) move the topHeadsUpRow logic to an interactor
+ private final MutableStateFlow<HeadsUpRowRepository> mTopHeadsUpRow =
+ StateFlowKt.MutableStateFlow(null);
+ private final MutableStateFlow<Set<HeadsUpRowRepository>> mHeadsUpNotificationRows =
+ StateFlowKt.MutableStateFlow(new HashSet<>());
+ private final MutableStateFlow<Boolean> mHeadsUpAnimatingAway =
+ StateFlowKt.MutableStateFlow(false);
+ private final HashSet<String> mSwipedOutKeys = new HashSet<>();
+ private final HashSet<NotificationEntry> mEntriesToRemoveAfterExpand = new HashSet<>();
+ @VisibleForTesting
+ public final ArraySet<NotificationEntry> mEntriesToRemoveWhenReorderingAllowed
+ = new ArraySet<>();
+
+ private boolean mReleaseOnExpandFinish;
+ private boolean mTrackingHeadsUp;
+ private boolean mIsShadeOrQsExpanded;
+ private boolean mIsQsExpanded;
+ private int mStatusBarState;
+ private AnimationStateHandler mAnimationStateHandler;
+ private int mHeadsUpInset;
+
+ // Used for determining the region for touch interaction
+ private final Region mTouchableRegion = new Region();
+
+ private final Pools.Pool<HeadsUpEntry> mEntryPool = new Pools.Pool<>() {
+ private final Stack<HeadsUpEntry> mPoolObjects = new Stack<>();
+
+ @Override
+ public HeadsUpEntry acquire() {
+ NotificationThrottleHun.assertInLegacyMode();
+ if (!mPoolObjects.isEmpty()) {
+ return mPoolObjects.pop();
+ }
+ return new HeadsUpEntry();
+ }
+
+ @Override
+ public boolean release(@NonNull HeadsUpEntry instance) {
+ NotificationThrottleHun.assertInLegacyMode();
+ mPoolObjects.push(instance);
+ return true;
+ }
+ };
+
/**
* Enum entry for notification peek logged from this class.
*/
@@ -100,14 +179,23 @@
}
}
- public BaseHeadsUpManager(@NonNull final Context context,
+ @Inject
+ public BaseHeadsUpManager(
+ @NonNull final Context context,
HeadsUpManagerLogger logger,
+ StatusBarStateController statusBarStateController,
+ KeyguardBypassController bypassController,
+ GroupMembershipManager groupMembershipManager,
+ VisualStabilityProvider visualStabilityProvider,
+ ConfigurationController configurationController,
@Main Handler handler,
GlobalSettings globalSettings,
SystemClock systemClock,
@Main DelayableExecutor executor,
AccessibilityManagerWrapper accessibilityManagerWrapper,
UiEventLogger uiEventLogger,
+ JavaAdapter javaAdapter,
+ ShadeInteractor shadeInteractor,
AvalancheController avalancheController) {
mLogger = logger;
mExecutor = executor;
@@ -117,12 +205,16 @@
mUiEventLogger = uiEventLogger;
mAvalancheController = avalancheController;
mAvalancheController.setBaseEntryMapStr(this::getEntryMapStr);
+ mBypassController = bypassController;
+ mGroupMembershipManager = groupMembershipManager;
+ mVisualStabilityProvider = visualStabilityProvider;
Resources resources = context.getResources();
mMinimumDisplayTime = NotificationThrottleHun.isEnabled()
? 500 : resources.getInteger(R.integer.heads_up_notification_minimum_time);
mStickyForSomeTimeAutoDismissTime = resources.getInteger(
R.integer.sticky_heads_up_notification_time);
mAutoDismissTime = resources.getInteger(R.integer.heads_up_notification_decay);
+ mExtensionTime = resources.getInteger(R.integer.ambient_notification_extension_time);
mTouchAcceptanceDelay = resources.getInteger(R.integer.touch_acceptance_delay);
mSnoozedPackages = new ArrayMap<>();
int defaultSnoozeLengthMs =
@@ -145,11 +237,38 @@
globalSettings.getUriFor(SETTING_HEADS_UP_SNOOZE_LENGTH_MS),
/* notifyForDescendants = */ false,
settingsObserver);
+
+ statusBarStateController.addCallback(mStatusBarStateListener);
+ updateResources();
+ configurationController.addCallback(new ConfigurationController.ConfigurationListener() {
+ @Override
+ public void onDensityOrFontScaleChanged() {
+ updateResources();
+ }
+
+ @Override
+ public void onThemeChanged() {
+ updateResources();
+ }
+ });
+ javaAdapter.alwaysCollectFlow(shadeInteractor.isAnyExpanded(),
+ this::onShadeOrQsExpanded);
+ if (SceneContainerFlag.isEnabled()) {
+ javaAdapter.alwaysCollectFlow(shadeInteractor.isQsExpanded(),
+ this::onQsExpanded);
+ }
+ if (NotificationThrottleHun.isEnabled()) {
+ mVisualStabilityProvider.addPersistentReorderingBannedListener(
+ mOnReorderingBannedListener);
+ mVisualStabilityProvider.addPersistentReorderingAllowedListener(
+ mOnReorderingAllowedListener);
+ }
}
/**
* Adds an OnHeadUpChangedListener to observe events.
*/
+ @Override
public void addListener(@NonNull OnHeadsUpChangedListener listener) {
mListeners.addIfAbsent(listener);
}
@@ -157,11 +276,31 @@
/**
* Removes the OnHeadUpChangedListener from the observer list.
*/
+ @Override
public void removeListener(@NonNull OnHeadsUpChangedListener listener) {
mListeners.remove(listener);
}
/**
+ * Add a listener to receive callbacks {@link #setHeadsUpAnimatingAway(boolean)}
+ */
+ @Override
+ public void addHeadsUpPhoneListener(@NonNull OnHeadsUpPhoneListenerChange listener) {
+ mHeadsUpPhoneListeners.add(listener);
+ }
+
+ @Override
+ public void setAnimationStateHandler(@NonNull AnimationStateHandler handler) {
+ mAnimationStateHandler = handler;
+ }
+
+ private void updateResources() {
+ Resources resources = mContext.getResources();
+ mHeadsUpInset = SystemBarUtils.getStatusBarHeight(mContext)
+ + resources.getDimensionPixelSize(R.dimen.heads_up_status_bar_padding);
+ }
+
+ /**
* Called when posting a new notification that should appear on screen.
* Adds the notification to be managed.
* @param entry entry to show
@@ -188,14 +327,24 @@
mAvalancheController.update(headsUpEntry, runnable, "showNotification");
}
- /**
- * Try to remove the notification. May not succeed if the notification has not been shown long
- * enough and needs to be kept around.
- * @param key the key of the notification to remove
- * @param releaseImmediately force a remove regardless of earliest removal time
- * @param reason reason for removing the notification
- * @return true if notification is removed, false otherwise
- */
+ @Override
+ public boolean removeNotification(
+ @NonNull String key,
+ boolean releaseImmediately,
+ boolean animate,
+ @NonNull String reason) {
+ if (animate) {
+ return removeNotification(key, releaseImmediately,
+ "removeNotification(animate: true), reason: " + reason);
+ } else {
+ mAnimationStateHandler.setHeadsUpGoingAwayAnimationsAllowed(false);
+ final boolean removed = removeNotification(key, releaseImmediately,
+ "removeNotification(animate: false), reason: " + reason);
+ mAnimationStateHandler.setHeadsUpGoingAwayAnimationsAllowed(true);
+ return removed;
+ }
+ }
+
@Override
public boolean removeNotification(@NotNull String key, boolean releaseImmediately,
@NonNull String reason) {
@@ -261,6 +410,42 @@
}
}
+ @Override
+ public void setTrackingHeadsUp(boolean trackingHeadsUp) {
+ mTrackingHeadsUp = trackingHeadsUp;
+ }
+
+ @Override
+ public boolean shouldSwallowClick(@NonNull String key) {
+ BaseHeadsUpManager.HeadsUpEntry entry = getHeadsUpEntry(key);
+ return entry != null && mSystemClock.elapsedRealtime() < entry.mPostTime;
+ }
+
+ @Override
+ public void releaseAfterExpansion() {
+ if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return;
+ onExpandingFinished();
+ }
+
+ @Override
+ public void onExpandingFinished() {
+ if (mReleaseOnExpandFinish) {
+ releaseAllImmediately();
+ mReleaseOnExpandFinish = false;
+ } else {
+ for (NotificationEntry entry : getAllEntries().toList()) {
+ entry.setSeenInShade(true);
+ }
+ for (NotificationEntry entry : mEntriesToRemoveAfterExpand) {
+ if (isHeadsUpEntry(entry.getKey())) {
+ // Maybe the heads-up was removed already
+ removeEntry(entry.getKey(), "onExpandingFinished");
+ }
+ }
+ }
+ mEntriesToRemoveAfterExpand.clear();
+ }
+
/**
* Clears all managed notifications.
*/
@@ -339,10 +524,19 @@
return 0;
}
+ @VisibleForTesting
protected boolean shouldHeadsUpBecomePinned(@NonNull NotificationEntry entry) {
- if (entry == null) {
- return false;
+ boolean pin = mStatusBarState == StatusBarState.SHADE && !mIsShadeOrQsExpanded;
+ if (SceneContainerFlag.isEnabled()) {
+ pin |= mIsQsExpanded;
}
+ if (mBypassController.getBypassEnabled()) {
+ pin |= mStatusBarState == StatusBarState.KEYGUARD;
+ }
+ if (pin) {
+ return true;
+ }
+
final HeadsUpEntry headsUpEntry = getHeadsUpEntry(entry.getKey());
if (headsUpEntry == null) {
// This should not happen since shouldHeadsUpBecomePinned is always called after adding
@@ -392,10 +586,6 @@
}
}
- public @InflationFlag int getContentFlag() {
- return FLAG_CONTENT_VIEW_HEADS_UP;
- }
-
/**
* Manager-specific logic that should occur when an entry is added.
* @param headsUpEntry entry added
@@ -410,6 +600,8 @@
for (OnHeadsUpChangedListener listener : mListeners) {
listener.onHeadsUpStateChanged(entry, true);
}
+ updateTopHeadsUpFlow();
+ updateHeadsUpFlow();
}
/**
@@ -466,11 +658,60 @@
for (OnHeadsUpChangedListener listener : mListeners) {
listener.onHeadsUpStateChanged(entry, false);
}
+ if (!NotificationThrottleHun.isEnabled()) {
+ mEntryPool.release(headsUpEntry);
+ }
+ updateTopHeadsUpFlow();
+ updateHeadsUpFlow();
+ if (NotificationThrottleHun.isEnabled()) {
+ if (headsUpEntry.mEntry != null) {
+ if (mEntriesToRemoveWhenReorderingAllowed.contains(headsUpEntry.mEntry)) {
+ mEntriesToRemoveWhenReorderingAllowed.remove(headsUpEntry.mEntry);
+ }
+ }
+ }
+ }
+
+ private void updateTopHeadsUpFlow() {
+ mTopHeadsUpRow.setValue((HeadsUpRowRepository) getTopHeadsUpEntry());
+ }
+
+ private void updateHeadsUpFlow() {
+ mHeadsUpNotificationRows.setValue(new HashSet<>(getHeadsUpEntryPhoneMap().values()));
+ }
+
+ @Override
+ @NonNull
+ public Flow<HeadsUpRowRepository> getTopHeadsUpRow() {
+ return mTopHeadsUpRow;
+ }
+
+ @Override
+ @NonNull
+ public Flow<Set<HeadsUpRowRepository>> getActiveHeadsUpRows() {
+ return mHeadsUpNotificationRows;
+ }
+
+ @Override
+ @NonNull
+ public StateFlow<Boolean> isHeadsUpAnimatingAway() {
+ return mHeadsUpAnimatingAway;
+ }
+
+ @Override
+ public boolean isHeadsUpAnimatingAwayValue() {
+ return mHeadsUpAnimatingAway.getValue();
+ }
+
+ @NonNull
+ private ArrayMap<String, HeadsUpEntry> getHeadsUpEntryPhoneMap() {
+ return mHeadsUpEntryMap;
}
/**
* Called to notify the listeners that the HUN animating away animation has ended.
*/
+ @Override
public void onEntryAnimatingAwayEnded(@NonNull NotificationEntry entry) {
for (OnHeadsUpChangedListener listener : mListeners) {
listener.onHeadsUpAnimatingAwayEnded(entry);
@@ -484,6 +725,8 @@
* @param headsUpEntry entry updated
*/
protected void onEntryUpdated(HeadsUpEntry headsUpEntry) {
+ // no need to update the list here
+ updateTopHeadsUpFlow();
}
protected void updatePinnedMode() {
@@ -521,6 +764,7 @@
/**
* Snoozes all current Heads Up Notifications.
*/
+ @Override
public void snooze() {
List<String> keySet = new ArrayList<>(mHeadsUpEntryMap.keySet());
keySet.addAll(mAvalancheController.getWaitingKeys());
@@ -534,6 +778,7 @@
mLogger.logPackageSnoozed(snoozeKey);
mSnoozedPackages.put(snoozeKey, mSystemClock.elapsedRealtime() + mSnoozeLengthMs);
}
+ mReleaseOnExpandFinish = true;
}
@NonNull
@@ -541,6 +786,11 @@
return user + "," + packageName;
}
+ @Override
+ public void addSwipedOutNotification(@NonNull String key) {
+ mSwipedOutKeys.add(key);
+ }
+
@Nullable
protected HeadsUpEntry getHeadsUpEntry(@NonNull String key) {
if (mHeadsUpEntryMap.containsKey(key)) {
@@ -597,6 +847,59 @@
}
@Override
+ public @Nullable Region getTouchableRegion() {
+ NotificationEntry topEntry = getTopEntry();
+
+ // This call could be made in an inconsistent state while the pinnedMode hasn't been
+ // updated yet, but callbacks leading out of the headsUp manager, querying it. Let's
+ // therefore also check if the topEntry is null.
+ if (!hasPinnedHeadsUp() || topEntry == null) {
+ return null;
+ } else {
+ if (topEntry.rowIsChildInGroup()) {
+ final NotificationEntry groupSummary =
+ mGroupMembershipManager.getGroupSummary(topEntry);
+ if (groupSummary != null) {
+ topEntry = groupSummary;
+ }
+ }
+ ExpandableNotificationRow topRow = topEntry.getRow();
+ int[] tmpArray = new int[2];
+ topRow.getLocationOnScreen(tmpArray);
+ int minX = tmpArray[0];
+ int maxX = tmpArray[0] + topRow.getWidth();
+ int height = topRow.getIntrinsicHeight();
+ final boolean stretchToTop = tmpArray[1] <= mHeadsUpInset;
+ mTouchableRegion.set(minX, stretchToTop ? 0 : tmpArray[1], maxX, tmpArray[1] + height);
+ return mTouchableRegion;
+ }
+ }
+
+ @Override
+ public void setHeadsUpAnimatingAway(boolean headsUpAnimatingAway) {
+ if (headsUpAnimatingAway != mHeadsUpAnimatingAway.getValue()) {
+ for (OnHeadsUpPhoneListenerChange listener : mHeadsUpPhoneListeners) {
+ listener.onHeadsUpAnimatingAwayStateChanged(headsUpAnimatingAway);
+ }
+ mHeadsUpAnimatingAway.setValue(headsUpAnimatingAway);
+ }
+ }
+
+ private void onShadeOrQsExpanded(Boolean isExpanded) {
+ if (isExpanded != mIsShadeOrQsExpanded) {
+ mIsShadeOrQsExpanded = isExpanded;
+ if (!SceneContainerFlag.isEnabled() && isExpanded) {
+ mHeadsUpAnimatingAway.setValue(false);
+ }
+ }
+ }
+
+ private void onQsExpanded(Boolean isQsExpanded) {
+ if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return;
+ if (isQsExpanded != mIsQsExpanded) mIsQsExpanded = isQsExpanded;
+ }
+
+ @Override
public void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
pw.println("HeadsUpManager state:");
dumpInternal(pw, args);
@@ -616,6 +919,10 @@
pw.print(" "); pw.print(mSnoozedPackages.valueAt(i));
pw.print(", "); pw.println(mSnoozedPackages.keyAt(i));
}
+ pw.print(" mBarState=");
+ pw.println(mStatusBarState);
+ pw.print(" mTouchableRegion=");
+ pw.println(mTouchableRegion);
}
/**
@@ -639,6 +946,7 @@
* Unpins all pinned Heads Up Notifications.
* @param userUnPinned The unpinned action is trigger by user real operation.
*/
+ @Override
public void unpinAll(boolean userUnPinned) {
for (String key : mHeadsUpEntryMap.keySet()) {
HeadsUpEntry headsUpEntry = getHeadsUpEntry(key);
@@ -662,13 +970,59 @@
}
}
- /**
- * Returns the value of the tracking-heads-up flag. See the doc of {@code setTrackingHeadsUp} as
- * well.
- */
+ @Override
+ public void setRemoteInputActive(
+ @NonNull NotificationEntry entry, boolean remoteInputActive) {
+ HeadsUpEntry headsUpEntry = getHeadsUpEntryPhone(entry.getKey());
+ if (headsUpEntry != null && headsUpEntry.mRemoteInputActive != remoteInputActive) {
+ headsUpEntry.mRemoteInputActive = remoteInputActive;
+ if (ExpandHeadsUpOnInlineReply.isEnabled() && remoteInputActive) {
+ headsUpEntry.mRemoteInputActivatedAtLeastOnce = true;
+ }
+ if (remoteInputActive) {
+ headsUpEntry.cancelAutoRemovalCallbacks("setRemoteInputActive(true)");
+ } else {
+ headsUpEntry.updateEntry(false /* updatePostTime */, "setRemoteInputActive(false)");
+ }
+ onEntryUpdated(headsUpEntry);
+ }
+ }
+
+ @Nullable
+ private HeadsUpEntry getHeadsUpEntryPhone(@NonNull String key) {
+ return mHeadsUpEntryMap.get(key);
+ }
+
+ @Override
+ public void setGutsShown(@NonNull NotificationEntry entry, boolean gutsShown) {
+ HeadsUpEntry headsUpEntry = getHeadsUpEntry(entry.getKey());
+ if (headsUpEntry == null) return;
+ if (entry.isRowPinned() || !gutsShown) {
+ headsUpEntry.setGutsShownPinned(gutsShown);
+ }
+ }
+
+ @Override
+ public void extendHeadsUp() {
+ HeadsUpEntry topEntry = getTopHeadsUpEntryPhone();
+ if (topEntry == null) {
+ return;
+ }
+ topEntry.extendPulse();
+ }
+
+ @Nullable
+ private HeadsUpEntry getTopHeadsUpEntryPhone() {
+ if (SceneContainerFlag.isEnabled()) {
+ return (HeadsUpEntry) mTopHeadsUpRow.getValue();
+ } else {
+ return getTopHeadsUpEntry();
+ }
+ }
+
+ @Override
public boolean isTrackingHeadsUp() {
- // Might be implemented in subclass.
- return false;
+ return mTrackingHeadsUp;
}
/**
@@ -724,11 +1078,23 @@
*/
@Override
public boolean canRemoveImmediately(@NonNull String key) {
- HeadsUpEntry headsUpEntry = getHeadsUpEntry(key);
- if (headsUpEntry != null && headsUpEntry.mUserActionMayIndirectlyRemove) {
+ if (mSwipedOutKeys.contains(key)) {
+ // We always instantly dismiss views being manually swiped out.
+ mSwipedOutKeys.remove(key);
return true;
}
- return headsUpEntry == null || headsUpEntry.wasShownLongEnough()
+
+ HeadsUpEntry headsUpEntry = getHeadsUpEntryPhone(key);
+ HeadsUpEntry topEntry = getTopHeadsUpEntryPhone();
+
+ if (headsUpEntry == null || headsUpEntry != topEntry) {
+ return true;
+ }
+
+ if (headsUpEntry.mUserActionMayIndirectlyRemove) {
+ return true;
+ }
+ return headsUpEntry.wasShownLongEnough()
|| (headsUpEntry.mEntry != null && headsUpEntry.mEntry.isRowDismissed());
}
@@ -747,7 +1113,13 @@
@NonNull
protected HeadsUpEntry createHeadsUpEntry(NotificationEntry entry) {
- return new HeadsUpEntry(entry);
+ if (NotificationThrottleHun.isEnabled()) {
+ return new HeadsUpEntry(entry);
+ } else {
+ HeadsUpEntry headsUpEntry = mEntryPool.acquire();
+ headsUpEntry.setEntry(entry);
+ return headsUpEntry;
+ }
}
/**
@@ -763,12 +1135,79 @@
&& Notification.CATEGORY_CALL.equals(n.category));
}
+ private final OnReorderingAllowedListener mOnReorderingAllowedListener = () -> {
+ if (NotificationThrottleHun.isEnabled()) {
+ mAvalancheController.setEnableAtRuntime(true);
+ if (mEntriesToRemoveWhenReorderingAllowed.isEmpty()) {
+ return;
+ }
+ }
+ mAnimationStateHandler.setHeadsUpGoingAwayAnimationsAllowed(false);
+ for (NotificationEntry entry : mEntriesToRemoveWhenReorderingAllowed) {
+ if (entry != null && isHeadsUpEntry(entry.getKey())) {
+ // Maybe the heads-up was removed already
+ removeEntry(entry.getKey(), "mOnReorderingAllowedListener");
+ }
+ }
+ mEntriesToRemoveWhenReorderingAllowed.clear();
+ mAnimationStateHandler.setHeadsUpGoingAwayAnimationsAllowed(true);
+ };
+
+ private final OnReorderingBannedListener mOnReorderingBannedListener = () -> {
+ if (mAvalancheController != null) {
+ // In open shade the first HUN is pinned, and visual stability logic prevents us from
+ // unpinning this first HUN as long as the shade remains open. AvalancheController only
+ // shows the next HUN when the currently showing HUN is unpinned, so we must disable
+ // throttling here so that the incoming HUN stream is not forever paused. This is reset
+ // when reorder becomes allowed.
+ mAvalancheController.setEnableAtRuntime(false);
+
+ // Note that we cannot do the above when
+ // 1) The remove runnable runs because its delay means it may not run before shade close
+ // 2) Reordering is allowed again (when shade closes) because the HUN appear animation
+ // will have started by then
+ }
+ };
+
+ private final StatusBarStateController.StateListener
+ mStatusBarStateListener = new StatusBarStateController.StateListener() {
+ @Override
+ public void onStateChanged(int newState) {
+ boolean wasKeyguard = mStatusBarState == StatusBarState.KEYGUARD;
+ boolean isKeyguard = newState == StatusBarState.KEYGUARD;
+ mStatusBarState = newState;
+
+ if (wasKeyguard && !isKeyguard && mBypassController.getBypassEnabled()) {
+ ArrayList<String> keysToRemove = new ArrayList<>();
+ for (HeadsUpEntry entry : getHeadsUpEntryList()) {
+ if (entry.mEntry != null && entry.mEntry.isBubble() && !entry.isSticky()) {
+ keysToRemove.add(entry.mEntry.getKey());
+ }
+ }
+ for (String key : keysToRemove) {
+ removeEntry(key, "mStatusBarStateListener");
+ }
+ }
+ }
+
+ @Override
+ public void onDozingChanged(boolean isDozing) {
+ if (!isDozing) {
+ // Let's make sure all huns we got while dozing time out within the normal timeout
+ // duration. Otherwise they could get stuck for a very long time
+ for (HeadsUpEntry entry : getHeadsUpEntryList()) {
+ entry.updateEntry(true /* updatePostTime */, "onDozingChanged(false)");
+ }
+ }
+ }
+ };
+
/**
* This represents a notification and how long it is in a heads up mode. It also manages its
* lifecycle automatically when created. This class is public because it is exposed by methods
* of AvalancheController that take it as param.
*/
- public class HeadsUpEntry implements Comparable<HeadsUpEntry> {
+ public class HeadsUpEntry implements Comparable<HeadsUpEntry>, HeadsUpRowRepository {
public boolean mRemoteInputActivatedAtLeastOnce;
public boolean mRemoteInputActive;
public boolean mUserActionMayIndirectlyRemove;
@@ -784,6 +1223,14 @@
@Nullable private Runnable mCancelRemoveRunnable;
+ private boolean mGutsShownPinned;
+ private final MutableStateFlow<Boolean> mIsPinned = StateFlowKt.MutableStateFlow(false);
+
+ /**
+ * If the time this entry has been on was extended
+ */
+ private boolean extended;
+
public HeadsUpEntry() {
NotificationThrottleHun.assertInLegacyMode();
}
@@ -794,19 +1241,50 @@
setEntry(entry, createRemoveRunnable(entry));
}
+ @Override
+ @NonNull
+ public String getKey() {
+ return requireEntry().getKey();
+ }
+
+ @Override
+ @NonNull
+ public Object getElementKey() {
+ return requireEntry().getRow();
+ }
+
+ private NotificationEntry requireEntry() {
+ /* check if */ SceneContainerFlag.isUnexpectedlyInLegacyMode();
+ return Objects.requireNonNull(mEntry);
+ }
+
+ @Override
+ @NonNull
+ public StateFlow<Boolean> isPinned() {
+ return mIsPinned;
+ }
+
/** Attach a NotificationEntry. */
public void setEntry(@NonNull final NotificationEntry entry) {
NotificationThrottleHun.assertInLegacyMode();
setEntry(entry, createRemoveRunnable(entry));
}
- protected void setEntry(@NonNull final NotificationEntry entry,
+ protected void setEntry(
+ @NonNull final NotificationEntry entry,
@Nullable Runnable removeRunnable) {
mEntry = entry;
mRemoveRunnable = removeRunnable;
mPostTime = calculatePostTime();
updateEntry(true /* updatePostTime */, "setEntry");
+
+ if (NotificationThrottleHun.isEnabled()) {
+ mEntriesToRemoveWhenReorderingAllowed.add(entry);
+ if (!mVisualStabilityProvider.isReorderingAllowed()) {
+ entry.setSeenInShade(true);
+ }
+ }
}
protected boolean isRowPinned() {
@@ -815,6 +1293,7 @@
protected void setRowPinned(boolean pinned) {
if (mEntry != null) mEntry.setRowPinned(pinned);
+ mIsPinned.setValue(pinned);
}
/**
@@ -870,6 +1349,22 @@
// Notify the manager, that the posted time has changed.
onEntryUpdated(this);
+
+ if (mEntriesToRemoveAfterExpand.contains(mEntry)) {
+ mEntriesToRemoveAfterExpand.remove(mEntry);
+ }
+ if (!NotificationThrottleHun.isEnabled()) {
+ if (mEntriesToRemoveWhenReorderingAllowed.contains(mEntry)) {
+ mEntriesToRemoveWhenReorderingAllowed.remove(mEntry);
+ }
+ }
+ }
+
+ private void extendPulse() {
+ if (!extended) {
+ extended = true;
+ updateEntry(false, "extendPulse()");
+ }
}
/**
@@ -878,6 +1373,8 @@
* @return true if the notification is sticky
*/
public boolean isSticky() {
+ if (mGutsShownPinned) return true;
+
if (mEntry == null) return false;
if (ExpandHeadsUpOnInlineReply.isEnabled()) {
@@ -989,7 +1486,29 @@
}
public void setExpanded(boolean expanded) {
+ if (this.mExpanded == expanded) {
+ return;
+ }
+
this.mExpanded = expanded;
+ if (expanded) {
+ cancelAutoRemovalCallbacks("setExpanded(true)");
+ } else {
+ updateEntry(false /* updatePostTime */, "setExpanded(false)");
+ }
+ }
+
+ public void setGutsShownPinned(boolean gutsShownPinned) {
+ if (mGutsShownPinned == gutsShownPinned) {
+ return;
+ }
+
+ mGutsShownPinned = gutsShownPinned;
+ if (gutsShownPinned) {
+ cancelAutoRemovalCallbacks("setGutsShownPinned(true)");
+ } else {
+ updateEntry(false /* updatePostTime */, "setGutsShownPinned(false)");
+ }
}
public void reset() {
@@ -999,6 +1518,8 @@
mRemoveRunnable = null;
mExpanded = false;
mRemoteInputActive = false;
+ mGutsShownPinned = false;
+ extended = false;
}
/**
@@ -1074,7 +1595,23 @@
/** Creates a runnable to remove this notification from the alerting entries. */
protected Runnable createRemoveRunnable(NotificationEntry entry) {
- return () -> removeEntry(entry.getKey(), "createRemoveRunnable");
+ return () -> {
+ if (!NotificationThrottleHun.isEnabled()
+ && !mVisualStabilityProvider.isReorderingAllowed()
+ // We don't want to allow reordering while pulsing, but headsup need to
+ // time out anyway
+ && !entry.showingPulsing()) {
+ mEntriesToRemoveWhenReorderingAllowed.add(entry);
+ mVisualStabilityProvider.addTemporaryReorderingAllowedListener(
+ mOnReorderingAllowedListener);
+ } else if (mTrackingHeadsUp) {
+ mEntriesToRemoveAfterExpand.add(entry);
+ mLogger.logRemoveEntryAfterExpand(entry);
+ } else if (mVisualStabilityProvider.isReorderingAllowed()
+ || entry.showingPulsing()) {
+ removeEntry(entry.getKey(), "createRemoveRunnable");
+ }
+ };
}
/**
@@ -1098,7 +1635,7 @@
requestedTimeOutMs = mAvalancheController.getDurationMs(this, mAutoDismissTime);
}
final long duration = getRecommendedHeadsUpTimeoutMs(requestedTimeOutMs);
- return mPostTime + duration;
+ return mPostTime + duration + (extended ? mExtensionTime : 0);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.kt
index 04fe6b3..b37194b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.kt
@@ -59,6 +59,7 @@
* Gets the touchable region needed for heads up notifications. Returns null if no touchable
* region is required (ie: no heads up notification currently exists).
*/
+ // TODO(b/347007367): With scene container enabled this method may report outdated regions
fun getTouchableRegion(): Region?
/**
@@ -83,6 +84,10 @@
/** Returns whether the entry is (pinned and expanded) or (has an active remote input). */
fun isSticky(key: String?): Boolean
+ /**
+ * Returns the value of the tracking-heads-up flag. See the doc of {@code setTrackingHeadsUp} as
+ * well.
+ */
fun isTrackingHeadsUp(): Boolean
fun onExpandingFinished()
@@ -115,7 +120,7 @@
key: String,
releaseImmediately: Boolean,
animate: Boolean,
- reason: String
+ reason: String,
): Boolean
/** Clears all managed notifications. */
@@ -149,6 +154,10 @@
*/
fun setRemoteInputActive(entry: NotificationEntry, remoteInputActive: Boolean)
+ /**
+ * Sets the tracking-heads-up flag. If the flag is true, HeadsUpManager doesn't remove the entry
+ * from the list even after a Heads Up Notification is gone.
+ */
fun setTrackingHeadsUp(tracking: Boolean)
/** Sets the current user. */
@@ -260,7 +269,7 @@
key: String,
releaseImmediately: Boolean,
animate: Boolean,
- reason: String
+ reason: String,
) = false
override fun setAnimationStateHandler(handler: AnimationStateHandler) {}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.kt
index 584cd3b..1e043ec 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.kt
@@ -36,6 +36,9 @@
/** Adds the status bar view to the window manager. */
fun attach()
+ /** Called when work should stop and resources should be released. */
+ fun stop()
+
/** Adds the given view to the status bar window view. */
fun addViewToWindow(view: View, layoutParams: ViewGroup.LayoutParams)
@@ -78,7 +81,7 @@
*/
fun setOngoingProcessRequiresStatusBarVisible(visible: Boolean)
- interface Factory {
+ fun interface Factory {
fun create(
context: Context,
viewCaptureAwareWindowManager: ViewCaptureAwareWindowManager,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowControllerImpl.java
index 6953bbf..811a2ec 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowControllerImpl.java
@@ -51,10 +51,12 @@
import com.android.internal.policy.SystemBarUtils;
import com.android.systemui.animation.ActivityTransitionAnimator;
import com.android.systemui.animation.DelegateTransitionAnimatorController;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.fragments.FragmentHostManager;
import com.android.systemui.fragments.FragmentService;
import com.android.systemui.res.R;
import com.android.systemui.statusbar.core.StatusBarConnectedDisplays;
+import com.android.systemui.statusbar.core.StatusBarRootModernization;
import com.android.systemui.statusbar.data.repository.StatusBarConfigurationController;
import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider;
import com.android.systemui.statusbar.window.StatusBarWindowModule.InternalWindowViewInflater;
@@ -66,6 +68,7 @@
import dagger.assisted.AssistedInject;
import java.util.Optional;
+import java.util.concurrent.Executor;
/**
* Encapsulates all logic for the status bar window state management.
@@ -79,6 +82,7 @@
private final StatusBarConfigurationController mStatusBarConfigurationController;
private final IWindowManager mIWindowManager;
private final StatusBarContentInsetsProvider mContentInsetsProvider;
+ private final Executor mMainExecutor;
private int mBarHeight = -1;
private final State mCurrentState = new State();
private boolean mIsAttached;
@@ -101,12 +105,14 @@
IWindowManager iWindowManager,
@Assisted StatusBarContentInsetsProvider contentInsetsProvider,
FragmentService fragmentService,
- Optional<UnfoldTransitionProgressProvider> unfoldTransitionProgressProvider) {
+ Optional<UnfoldTransitionProgressProvider> unfoldTransitionProgressProvider,
+ @Main Executor mainExecutor) {
mContext = context;
mWindowManager = viewCaptureAwareWindowManager;
mStatusBarConfigurationController = statusBarConfigurationController;
mIWindowManager = iWindowManager;
mContentInsetsProvider = contentInsetsProvider;
+ mMainExecutor = mainExecutor;
mStatusBarWindowView = statusBarWindowViewInflater.inflate(context);
mFragmentService = fragmentService;
mLaunchAnimationContainer = mStatusBarWindowView.findViewById(
@@ -167,6 +173,19 @@
}
@Override
+ public void stop() {
+ StatusBarConnectedDisplays.assertInNewMode();
+
+ mWindowManager.removeView(mStatusBarWindowView);
+
+ if (StatusBarRootModernization.isEnabled()) {
+ return;
+ }
+ // Fragment transactions need to happen on the main thread.
+ mMainExecutor.execute(() -> mFragmentService.removeAndDestroy(mStatusBarWindowView));
+ }
+
+ @Override
public void addViewToWindow(@NonNull View view, @NonNull ViewGroup.LayoutParams layoutParams) {
mStatusBarWindowView.addView(view, layoutParams);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowControllerStore.kt b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowControllerStore.kt
index 051d463..7403161 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowControllerStore.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowControllerStore.kt
@@ -70,6 +70,10 @@
)
}
+ override suspend fun onDisplayRemovalAction(instance: StatusBarWindowController) {
+ instance.stop()
+ }
+
override val instanceClass = StatusBarWindowController::class.java
}
diff --git a/packages/SystemUI/src/com/android/systemui/toast/SystemUIToast.java b/packages/SystemUI/src/com/android/systemui/toast/SystemUIToast.java
index d97cae2..d367455 100644
--- a/packages/SystemUI/src/com/android/systemui/toast/SystemUIToast.java
+++ b/packages/SystemUI/src/com/android/systemui/toast/SystemUIToast.java
@@ -50,6 +50,7 @@
public class SystemUIToast implements ToastPlugin.Toast {
static final String TAG = "SystemUIToast";
final Context mContext;
+ final Context mDisplayContext;
final CharSequence mText;
final ToastPlugin.Toast mPluginToast;
@@ -68,17 +69,18 @@
@Nullable private final Animator mInAnimator;
@Nullable private final Animator mOutAnimator;
- SystemUIToast(LayoutInflater layoutInflater, Context context, CharSequence text,
- String packageName, int userId, int orientation) {
- this(layoutInflater, context, text, null, packageName, userId,
+ SystemUIToast(LayoutInflater layoutInflater, Context applicationContext, Context displayContext,
+ CharSequence text, String packageName, int userId, int orientation) {
+ this(layoutInflater, applicationContext, displayContext, text, null, packageName, userId,
orientation);
}
- SystemUIToast(LayoutInflater layoutInflater, Context context, CharSequence text,
- ToastPlugin.Toast pluginToast, String packageName, @UserIdInt int userId,
- int orientation) {
+ SystemUIToast(LayoutInflater layoutInflater, Context applicationContext, Context displayContext,
+ CharSequence text, ToastPlugin.Toast pluginToast, String packageName,
+ @UserIdInt int userId, int orientation) {
mLayoutInflater = layoutInflater;
- mContext = context;
+ mContext = applicationContext;
+ mDisplayContext = displayContext;
mText = text;
mPluginToast = pluginToast;
mPackageName = packageName;
@@ -221,9 +223,9 @@
mPluginToast.onOrientationChange(orientation);
}
- mDefaultY = mContext.getResources().getDimensionPixelSize(R.dimen.toast_y_offset);
+ mDefaultY = mDisplayContext.getResources().getDimensionPixelSize(R.dimen.toast_y_offset);
mDefaultGravity =
- mContext.getResources().getInteger(R.integer.config_toastDefaultGravity);
+ mDisplayContext.getResources().getInteger(R.integer.config_toastDefaultGravity);
}
private Animator createInAnimator() {
diff --git a/packages/SystemUI/src/com/android/systemui/toast/ToastFactory.java b/packages/SystemUI/src/com/android/systemui/toast/ToastFactory.java
index 9ae6674..388d4bd 100644
--- a/packages/SystemUI/src/com/android/systemui/toast/ToastFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/toast/ToastFactory.java
@@ -65,15 +65,16 @@
/**
* Create a toast to be shown by ToastUI.
*/
- public SystemUIToast createToast(Context context, CharSequence text, String packageName,
- int userId, int orientation) {
- LayoutInflater layoutInflater = LayoutInflater.from(context);
+ public SystemUIToast createToast(Context applicationContext, Context displayContext,
+ CharSequence text, String packageName, int userId, int orientation) {
+ LayoutInflater layoutInflater = LayoutInflater.from(displayContext);
if (isPluginAvailable()) {
- return new SystemUIToast(layoutInflater, context, text, mPlugin.createToast(text,
- packageName, userId), packageName, userId, orientation);
+ return new SystemUIToast(layoutInflater, applicationContext, displayContext, text,
+ mPlugin.createToast(text, packageName, userId), packageName, userId,
+ orientation);
}
- return new SystemUIToast(layoutInflater, context, text, packageName, userId,
- orientation);
+ return new SystemUIToast(layoutInflater, applicationContext, displayContext, text,
+ packageName, userId, orientation);
}
private boolean isPluginAvailable() {
diff --git a/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java b/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java
index 32a4f12..12f73b8 100644
--- a/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java
+++ b/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java
@@ -135,8 +135,8 @@
return;
}
Context displayContext = context.createDisplayContext(display);
- mToast = mToastFactory.createToast(displayContext /* sysuiContext */, text, packageName,
- userHandle.getIdentifier(), mOrientation);
+ mToast = mToastFactory.createToast(mContext, displayContext /* sysuiContext */, text,
+ packageName, userHandle.getIdentifier(), mOrientation);
if (mToast.getInAnimation() != null) {
mToast.getInAnimation().start();
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/TutorialSelectionScreen.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/TutorialSelectionScreen.kt
index d371acf..66a900b 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/TutorialSelectionScreen.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/TutorialSelectionScreen.kt
@@ -18,6 +18,7 @@
import android.content.res.Configuration
import androidx.compose.foundation.background
+import androidx.compose.foundation.focusable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
@@ -36,8 +37,12 @@
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
+import androidx.compose.ui.focus.FocusRequester
+import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.input.pointer.pointerInteropFilter
@@ -49,6 +54,7 @@
import com.android.systemui.res.R
import com.android.systemui.touchpad.tutorial.ui.gesture.isFourFingerTouchpadSwipe
import com.android.systemui.touchpad.tutorial.ui.gesture.isThreeFingerTouchpadSwipe
+import com.android.systemui.touchpad.tutorial.ui.viewmodel.Screen
@Composable
fun TutorialSelectionScreen(
@@ -56,6 +62,7 @@
onHomeTutorialClicked: () -> Unit,
onRecentAppsTutorialClicked: () -> Unit,
onDoneButtonClicked: () -> Unit,
+ lastSelectedScreen: Screen,
) {
Column(
verticalArrangement = Arrangement.Center,
@@ -80,6 +87,7 @@
onHomeTutorialClicked = onHomeTutorialClicked,
onRecentAppsTutorialClicked = onRecentAppsTutorialClicked,
modifier = Modifier.weight(1f).padding(60.dp),
+ lastSelectedScreen,
)
}
else -> {
@@ -88,6 +96,7 @@
onHomeTutorialClicked = onHomeTutorialClicked,
onRecentAppsTutorialClicked = onRecentAppsTutorialClicked,
modifier = Modifier.weight(1f).padding(60.dp),
+ lastSelectedScreen,
)
}
}
@@ -105,6 +114,7 @@
onHomeTutorialClicked: () -> Unit,
onRecentAppsTutorialClicked: () -> Unit,
modifier: Modifier = Modifier,
+ lastSelectedScreen: Screen,
) {
Row(
horizontalArrangement = Arrangement.spacedBy(20.dp),
@@ -116,6 +126,7 @@
onHomeTutorialClicked,
onRecentAppsTutorialClicked,
modifier = Modifier.weight(1f).fillMaxSize(),
+ lastSelectedScreen,
)
}
}
@@ -126,6 +137,7 @@
onHomeTutorialClicked: () -> Unit,
onRecentAppsTutorialClicked: () -> Unit,
modifier: Modifier = Modifier,
+ lastSelectedScreen: Screen,
) {
Column(
verticalArrangement = Arrangement.spacedBy(20.dp),
@@ -137,6 +149,7 @@
onHomeTutorialClicked,
onRecentAppsTutorialClicked,
modifier = Modifier.weight(1f).fillMaxSize(),
+ lastSelectedScreen,
)
}
}
@@ -147,14 +160,26 @@
onHomeTutorialClicked: () -> Unit,
onRecentAppsTutorialClicked: () -> Unit,
modifier: Modifier = Modifier,
+ lastSelectedScreen: Screen,
) {
+ val homeFocusRequester = remember { FocusRequester() }
+ val backFocusRequester = remember { FocusRequester() }
+ val recentAppsFocusRequester = remember { FocusRequester() }
+ LaunchedEffect(Unit) {
+ when (lastSelectedScreen) {
+ Screen.HOME_GESTURE -> homeFocusRequester.requestFocus()
+ Screen.BACK_GESTURE -> backFocusRequester.requestFocus()
+ Screen.RECENT_APPS_GESTURE -> recentAppsFocusRequester.requestFocus()
+ else -> {} // No-Op.
+ }
+ }
TutorialButton(
text = stringResource(R.string.touchpad_tutorial_home_gesture_button),
icon = ImageVector.vectorResource(id = R.drawable.touchpad_tutorial_home_icon),
iconColor = MaterialTheme.colorScheme.onPrimary,
onClick = onHomeTutorialClicked,
backgroundColor = MaterialTheme.colorScheme.primary,
- modifier = modifier,
+ modifier = modifier.focusRequester(homeFocusRequester).focusable(),
)
TutorialButton(
text = stringResource(R.string.touchpad_tutorial_back_gesture_button),
@@ -162,7 +187,7 @@
iconColor = MaterialTheme.colorScheme.onTertiary,
onClick = onBackTutorialClicked,
backgroundColor = MaterialTheme.colorScheme.tertiary,
- modifier = modifier,
+ modifier = modifier.focusRequester(backFocusRequester).focusable(),
)
TutorialButton(
text = stringResource(R.string.touchpad_tutorial_recent_apps_gesture_button),
@@ -170,7 +195,7 @@
iconColor = MaterialTheme.colorScheme.onSecondary,
onClick = onRecentAppsTutorialClicked,
backgroundColor = MaterialTheme.colorScheme.secondary,
- modifier = modifier,
+ modifier = modifier.focusRequester(recentAppsFocusRequester).focusable(),
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/view/TouchpadTutorialActivity.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/view/TouchpadTutorialActivity.kt
index e1f7bd5..6662fc5 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/view/TouchpadTutorialActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/view/TouchpadTutorialActivity.kt
@@ -24,12 +24,16 @@
import androidx.activity.viewModels
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
import androidx.lifecycle.Lifecycle.State.STARTED
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.compose.theme.PlatformTheme
import com.android.systemui.inputdevice.tutorial.InputDeviceTutorialLogger
import com.android.systemui.inputdevice.tutorial.InputDeviceTutorialLogger.TutorialContext
import com.android.systemui.inputdevice.tutorial.KeyboardTouchpadTutorialMetricsLogger
+import com.android.systemui.res.R
import com.android.systemui.touchpad.tutorial.ui.composable.BackGestureTutorialScreen
import com.android.systemui.touchpad.tutorial.ui.composable.HomeGestureTutorialScreen
import com.android.systemui.touchpad.tutorial.ui.composable.RecentAppsGestureTutorialScreen
@@ -54,6 +58,7 @@
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
+ setTitle(getString(R.string.launch_touchpad_tutorial_notification_content))
setContent {
PlatformTheme { TouchpadTutorialScreen(vm, closeTutorial = ::finishTutorial) }
}
@@ -82,13 +87,24 @@
@Composable
fun TouchpadTutorialScreen(vm: TouchpadTutorialViewModel, closeTutorial: () -> Unit) {
val activeScreen by vm.screen.collectAsStateWithLifecycle(STARTED)
+ var lastSelectedScreen by remember { mutableStateOf(TUTORIAL_SELECTION) }
when (activeScreen) {
TUTORIAL_SELECTION ->
TutorialSelectionScreen(
- onBackTutorialClicked = { vm.goTo(BACK_GESTURE) },
- onHomeTutorialClicked = { vm.goTo(HOME_GESTURE) },
- onRecentAppsTutorialClicked = { vm.goTo(RECENT_APPS_GESTURE) },
+ onBackTutorialClicked = {
+ lastSelectedScreen = BACK_GESTURE
+ vm.goTo(BACK_GESTURE)
+ },
+ onHomeTutorialClicked = {
+ lastSelectedScreen = HOME_GESTURE
+ vm.goTo(HOME_GESTURE)
+ },
+ onRecentAppsTutorialClicked = {
+ lastSelectedScreen = RECENT_APPS_GESTURE
+ vm.goTo(RECENT_APPS_GESTURE)
+ },
onDoneButtonClicked = closeTutorial,
+ lastSelectedScreen,
)
BACK_GESTURE ->
BackGestureTutorialScreen(
diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/JavaAdapter.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/JavaAdapter.kt
index 63a5b3f..fb09ab4 100644
--- a/packages/SystemUI/src/com/android/systemui/util/kotlin/JavaAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/JavaAdapter.kt
@@ -28,6 +28,7 @@
import javax.inject.Inject
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext
+import kotlin.coroutines.cancellation.CancellationException
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.DisposableHandle
import kotlinx.coroutines.Job
@@ -64,6 +65,28 @@
): StateFlow<T> {
return flow.stateIn(scope, started, initialValue)
}
+
+ /** Call suspend functions from Java */
+ fun <T, R> callSuspend(
+ suspendFunction: suspend (T) -> R,
+ arg: T,
+ onSuccess: (R) -> Unit,
+ onCancel: (CancellationException) -> Unit,
+ onFailure: (Throwable) -> Unit,
+ ): Job =
+ scope.launch {
+ val result =
+ try {
+ suspendFunction(arg)
+ } catch (ex: CancellationException) {
+ onCancel(ex)
+ return@launch
+ } catch (ex: Throwable) {
+ onFailure(ex)
+ return@launch
+ }
+ onSuccess(result)
+ }
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/SysUICoroutinesModule.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/SysUICoroutinesModule.kt
index 2a9b1b9..e5c1e7d 100644
--- a/packages/SystemUI/src/com/android/systemui/util/kotlin/SysUICoroutinesModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/SysUICoroutinesModule.kt
@@ -17,14 +17,16 @@
package com.android.systemui.util.kotlin
import android.os.Handler
+import com.android.systemui.coroutines.newTracingContext
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.dagger.qualifiers.Tracing
import com.android.systemui.dagger.qualifiers.UiBackground
import com.android.systemui.util.settings.SettingsSingleThreadBackground
import dagger.Module
import dagger.Provides
+import java.util.concurrent.Executor
+import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.DelicateCoroutinesApi
@@ -33,8 +35,6 @@
import kotlinx.coroutines.asCoroutineDispatcher
import kotlinx.coroutines.newFixedThreadPoolContext
import kotlinx.coroutines.plus
-import java.util.concurrent.Executor
-import kotlin.coroutines.CoroutineContext
private const val LIMIT_BACKGROUND_DISPATCHER_THREADS = true
@@ -62,7 +62,7 @@
@Background
@Deprecated(
"Use @Background CoroutineContext instead",
- ReplaceWith("bgCoroutineContext()", "kotlin.coroutines.CoroutineContext")
+ ReplaceWith("bgCoroutineContext()", "kotlin.coroutines.CoroutineContext"),
)
fun bgDispatcher(): CoroutineDispatcher {
return if (LIMIT_BACKGROUND_DISPATCHER_THREADS) {
@@ -73,7 +73,7 @@
// code on those.
newFixedThreadPoolContext(
nThreads = Runtime.getRuntime().availableProcessors(),
- name = "SystemUIBg"
+ name = "SystemUIBg",
)
} else {
Dispatchers.IO
@@ -89,10 +89,17 @@
}
@Provides
+ @SysUISingleton
+ @SettingsSingleThreadBackground
+ fun settingsScope(@Background bgDispatcher: CoroutineDispatcher): CoroutineScope {
+ return CoroutineScope(bgDispatcher + newTracingContext("SettingsProxy"))
+ }
+
+ @Provides
@Background
@SysUISingleton
fun bgCoroutineContext(
- @Background bgCoroutineDispatcher: CoroutineDispatcher,
+ @Background bgCoroutineDispatcher: CoroutineDispatcher
): CoroutineContext {
return bgCoroutineDispatcher
}
@@ -103,7 +110,7 @@
@UiBackground
@Deprecated(
"Use @UiBackground CoroutineContext instead",
- ReplaceWith("uiBgCoroutineContext()", "kotlin.coroutines.CoroutineContext")
+ ReplaceWith("uiBgCoroutineContext()", "kotlin.coroutines.CoroutineContext"),
)
fun uiBgDispatcher(@UiBackground uiBgExecutor: Executor): CoroutineDispatcher =
uiBgExecutor.asCoroutineDispatcher()
@@ -112,7 +119,7 @@
@UiBackground
@SysUISingleton
fun uiBgCoroutineContext(
- @UiBackground uiBgCoroutineDispatcher: CoroutineDispatcher,
+ @UiBackground uiBgCoroutineDispatcher: CoroutineDispatcher
): CoroutineContext {
return uiBgCoroutineDispatcher
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
index b705872..68bffeef 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
@@ -18,8 +18,6 @@
import static android.media.AudioManager.RINGER_MODE_NORMAL;
-import static com.android.settingslib.flags.Flags.volumeDialogAudioSharingFix;
-
import android.app.ActivityManager;
import android.app.KeyguardManager;
import android.app.NotificationManager;
@@ -79,9 +77,13 @@
import com.android.systemui.util.concurrency.ThreadFactory;
import com.android.systemui.util.kotlin.JavaAdapter;
import com.android.systemui.volume.domain.interactor.AudioSharingInteractor;
+import com.android.systemui.volume.shared.VolumeLogger;
import dalvik.annotation.optimization.NeverCompile;
+import kotlin.Unit;
+import kotlin.jvm.functions.Function1;
+
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;
@@ -155,6 +157,7 @@
private final VibratorHelper mVibrator;
private final AudioSharingInteractor mAudioSharingInteractor;
private final JavaAdapter mJavaAdapter;
+ private final VolumeLogger mVolumeLogger;
private final boolean mHasVibrator;
private boolean mShowA11yStream;
private boolean mShowVolumeDialog;
@@ -202,7 +205,8 @@
UserTracker userTracker,
DumpManager dumpManager,
AudioSharingInteractor audioSharingInteractor,
- JavaAdapter javaAdapter
+ JavaAdapter javaAdapter,
+ VolumeLogger volumeLogger
) {
mContext = context.getApplicationContext();
mPackageManager = packageManager;
@@ -216,6 +220,7 @@
mMediaSessions = createMediaSessions(mContext, mWorkerLooper, mMediaSessionsCallbacksW);
mAudioSharingInteractor = audioSharingInteractor;
mJavaAdapter = javaAdapter;
+ mVolumeLogger = volumeLogger;
mAudio = audioManager;
mNoMan = notificationManager;
mObserver = new SettingObserver(mWorker);
@@ -293,15 +298,28 @@
} catch (SecurityException e) {
Log.w(TAG, "No access to media sessions", e);
}
- if (volumeDialogAudioSharingFix()) {
- Slog.d(TAG, "Start collect volume changes in audio sharing");
- mJavaAdapter.alwaysCollectFlow(
- mAudioSharingInteractor.getVolume(),
- this::handleAudioSharingStreamVolumeChanges);
- mJavaAdapter.alwaysCollectFlow(
- mAudioSharingInteractor.isInAudioSharing(),
- inSharing -> mInAudioSharing = inSharing);
- }
+ Function1<Throwable, Unit> errorCallback = (ex) -> {
+ mVolumeLogger.onAudioSharingAvailabilityRequestedError("register()",
+ ex.getMessage());
+ return null;
+ };
+ var unused =
+ mJavaAdapter.<Context, Boolean>callSuspend(
+ mAudioSharingInteractor::audioSharingVolumeBarAvailable, mContext,
+ result -> {
+ if (result) {
+ Slog.d(TAG, "Start collect volume changes in audio sharing");
+ mJavaAdapter.alwaysCollectFlow(
+ mAudioSharingInteractor.getVolume(),
+ volume -> handleAudioSharingStreamVolumeChanges(volume));
+ mJavaAdapter.alwaysCollectFlow(
+ mAudioSharingInteractor.isInAudioSharing(),
+ inSharing -> mInAudioSharing = inSharing);
+ }
+ return null;
+ },
+ errorCallback,
+ errorCallback);
}
public void setVolumePolicy(VolumePolicy policy) {
@@ -588,18 +606,34 @@
mState.activeStream = activeStream;
Events.writeEvent(Events.EVENT_ACTIVE_STREAM_CHANGED, activeStream);
if (D.BUG) Log.d(TAG, "updateActiveStreamW " + activeStream);
- final int s =
- activeStream
- < (volumeDialogAudioSharingFix()
- ? DYNAMIC_STREAM_BROADCAST
- : DYNAMIC_STREAM_REMOTE_START_INDEX)
- ? activeStream
- : -1;
- if (D.BUG) Log.d(TAG, "forceVolumeControlStream " + s);
- mAudio.forceVolumeControlStream(s);
+ Function1<Throwable, Unit> errorCallback = (ex) -> {
+ mVolumeLogger.onAudioSharingAvailabilityRequestedError(
+ "updateActiveStreamW",
+ ex.getMessage());
+ forceVolumeControlStreamW(activeStream, false);
+ return null;
+ };
+ var unused =
+ mJavaAdapter.<Context, Boolean>callSuspend(
+ mAudioSharingInteractor::audioSharingVolumeBarAvailable, mContext,
+ result -> {
+ forceVolumeControlStreamW(activeStream, result);
+ return null;
+ },
+ errorCallback,
+ errorCallback);
return true;
}
+ private void forceVolumeControlStreamW(int activeStream,
+ boolean audioSharingVolumeBarAvailable) {
+ final int dynamicStartIdx = audioSharingVolumeBarAvailable ? DYNAMIC_STREAM_BROADCAST
+ : DYNAMIC_STREAM_REMOTE_START_INDEX;
+ final int s = activeStream < dynamicStartIdx ? activeStream : -1;
+ if (D.BUG) Log.d(TAG, "forceVolumeControlStream " + s);
+ mWorker.post(() -> mAudio.forceVolumeControlStream(s));
+ }
+
private StreamState streamStateW(int stream) {
StreamState ss = mState.states.get(stream);
if (ss == null) {
@@ -773,10 +807,26 @@
}
private void onSetStreamVolumeW(int stream, int level) {
- if (D.BUG) Log.d(TAG, "onSetStreamVolume " + stream + " level=" + level);
- if (volumeDialogAudioSharingFix() && stream == DYNAMIC_STREAM_BROADCAST) {
- Slog.d(TAG, "onSetStreamVolumeW set broadcast stream level = " + level);
- mAudioSharingInteractor.setStreamVolume(level);
+ if (D.BUG) Log.d(TAG, "onSetStreamVolumeW " + stream + " level=" + level);
+
+ if (stream == DYNAMIC_STREAM_BROADCAST) {
+ Function1<Throwable, Unit> errorCallback = (ex) -> {
+ mVolumeLogger.onAudioSharingAvailabilityRequestedError(
+ "onSetStreamVolumeW",
+ ex.getMessage());
+ return null;
+ };
+ var unused =
+ mJavaAdapter.<Context, Boolean>callSuspend(
+ mAudioSharingInteractor::audioSharingVolumeBarAvailable, mContext,
+ result -> {
+ if (result) {
+ mAudioSharingInteractor.setStreamVolume(level);
+ }
+ return null;
+ },
+ errorCallback,
+ errorCallback);
return;
}
if (stream >= DYNAMIC_STREAM_REMOTE_START_INDEX) {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index bd4c463..a4e46f6 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -34,6 +34,7 @@
import static com.android.internal.jank.InteractionJankMonitor.CUJ_VOLUME_CONTROL;
import static com.android.internal.jank.InteractionJankMonitor.Configuration.Builder;
+import static com.android.settingslib.flags.Flags.audioSharingDeveloperOption;
import static com.android.settingslib.flags.Flags.volumeDialogAudioSharingFix;
import static com.android.systemui.volume.Events.DISMISS_REASON_POSTURE_CHANGED;
import static com.android.systemui.volume.Events.DISMISS_REASON_SETTINGS_CLICKED;
@@ -121,9 +122,9 @@
import com.android.systemui.Prefs;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.haptics.slider.HapticSlider;
+import com.android.systemui.haptics.slider.HapticSliderPlugin;
import com.android.systemui.haptics.slider.HapticSliderViewBinder;
import com.android.systemui.haptics.slider.SeekableSliderTrackerConfig;
-import com.android.systemui.haptics.slider.HapticSliderPlugin;
import com.android.systemui.haptics.slider.SliderHapticFeedbackConfig;
import com.android.systemui.media.dialog.MediaOutputDialogManager;
import com.android.systemui.plugins.VolumeDialog;
@@ -1685,10 +1686,10 @@
}
// Always show the stream for audio sharing if it exists.
- if (volumeDialogAudioSharingFix()
+ if ((volumeDialogAudioSharingFix() || audioSharingDeveloperOption())
&& row.ss != null
&& mContext.getString(R.string.audio_sharing_description)
- .equals(row.ss.remoteLabel)) {
+ .equals(row.ss.remoteLabel)) {
return true;
}
@@ -1894,9 +1895,9 @@
if (!ss.dynamic) continue;
mDynamic.put(stream, true);
if (findRow(stream) == null) {
- if (volumeDialogAudioSharingFix()
- && mContext.getString(R.string.audio_sharing_description)
- .equals(ss.remoteLabel)) {
+ if ((volumeDialogAudioSharingFix() || audioSharingDeveloperOption())
+ && (mContext.getString(R.string.audio_sharing_description)
+ .equals(ss.remoteLabel))) {
addRow(
stream,
R.drawable.ic_volume_media_bt,
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java
index 536403c..3ce1bde 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java
@@ -16,8 +16,6 @@
package com.android.systemui.volume;
-import static com.android.settingslib.flags.Flags.volumeDialogAudioSharingFix;
-
import android.content.Context;
import android.content.res.Configuration;
import android.util.Log;
@@ -28,7 +26,12 @@
import com.android.systemui.qs.tiles.DndTile;
import com.android.systemui.res.R;
import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.util.kotlin.JavaAdapter;
import com.android.systemui.volume.domain.interactor.AudioSharingInteractor;
+import com.android.systemui.volume.shared.VolumeLogger;
+
+import kotlin.Unit;
+import kotlin.jvm.functions.Function1;
import java.io.PrintWriter;
@@ -44,16 +47,22 @@
private VolumeDialogComponent mVolumeComponent;
private AudioSharingInteractor mAudioSharingInteractor;
private AudioRepository mAudioRepository;
+ private JavaAdapter mJavaAdapter;
+ private VolumeLogger mVolumeLogger;
@Inject
public VolumeUI(Context context,
VolumeDialogComponent volumeDialogComponent,
AudioRepository audioRepository,
- AudioSharingInteractor audioSharingInteractor) {
+ AudioSharingInteractor audioSharingInteractor,
+ JavaAdapter javaAdapter,
+ VolumeLogger volumeLogger) {
mContext = context;
mVolumeComponent = volumeDialogComponent;
mAudioRepository = audioRepository;
mAudioSharingInteractor = audioSharingInteractor;
+ mJavaAdapter = javaAdapter;
+ mVolumeLogger = volumeLogger;
}
@Override
@@ -67,9 +76,22 @@
mVolumeComponent.setEnableDialogs(enableVolumeUi, enableSafetyWarning);
setDefaultVolumeController();
- if (volumeDialogAudioSharingFix()) {
- mAudioSharingInteractor.handlePrimaryGroupChange();
- }
+ Function1<Throwable, Unit> errorCallback = (ex) -> {
+ mVolumeLogger.onAudioSharingAvailabilityRequestedError("start()",
+ ex.getMessage());
+ return null;
+ };
+ var unused =
+ mJavaAdapter.<Context, Boolean>callSuspend(
+ mAudioSharingInteractor::audioSharingVolumeBarAvailable, mContext,
+ result -> {
+ if (result) {
+ mAudioSharingInteractor.handlePrimaryGroupChange();
+ }
+ return null;
+ },
+ errorCallback,
+ errorCallback);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dagger/AudioModule.kt b/packages/SystemUI/src/com/android/systemui/volume/dagger/AudioModule.kt
index d5b8597..d71ddb3 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dagger/AudioModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dagger/AudioModule.kt
@@ -86,7 +86,10 @@
@Background coroutineContext: CoroutineContext,
volumeLogger: VolumeLogger,
): AudioSharingRepository =
- if (Flags.enableLeAudioSharing() && localBluetoothManager != null) {
+ if (
+ (Flags.enableLeAudioSharing() || Flags.audioSharingDeveloperOption()) &&
+ localBluetoothManager != null
+ ) {
AudioSharingRepositoryImpl(
contentResolver,
localBluetoothManager,
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dagger/AudioSharingModule.kt b/packages/SystemUI/src/com/android/systemui/volume/dagger/AudioSharingModule.kt
index 1c80887..f6f1e8d 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dagger/AudioSharingModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dagger/AudioSharingModule.kt
@@ -36,7 +36,7 @@
impl: Lazy<AudioSharingInteractorImpl>,
emptyImpl: Lazy<AudioSharingInteractorEmptyImpl>,
): AudioSharingInteractor =
- if (Flags.volumeDialogAudioSharingFix()) {
+ if (Flags.volumeDialogAudioSharingFix() || Flags.audioSharingDeveloperOption()) {
impl.get()
} else {
emptyImpl.get()
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/binder/VolumeDialogRingerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/binder/VolumeDialogRingerViewBinder.kt
index 6816d35..c4b028d 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/binder/VolumeDialogRingerViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/binder/VolumeDialogRingerViewBinder.kt
@@ -49,7 +49,7 @@
val drawerContainer = requireViewById<View>(R.id.volume_drawer_container)
val selectedButtonView =
requireViewById<ImageButton>(R.id.volume_new_ringer_active_button)
- val volumeDialogView = requireViewById<ViewGroup>(R.id.volume_dialog)
+ val volumeDialogBackgroundView = requireViewById<View>(R.id.volume_dialog_background)
repeatWhenAttached {
viewModel(
traceName = "VolumeDialogRingerViewBinder",
@@ -71,19 +71,17 @@
is RingerDrawerState.Initial -> {
drawerContainer.visibility = View.GONE
selectedButtonView.visibility = View.VISIBLE
- volumeDialogView.setBackgroundResource(
+ volumeDialogBackgroundView.setBackgroundResource(
R.drawable.volume_dialog_background
)
}
-
is RingerDrawerState.Closed -> {
drawerContainer.visibility = View.GONE
selectedButtonView.visibility = View.VISIBLE
- volumeDialogView.setBackgroundResource(
+ volumeDialogBackgroundView.setBackgroundResource(
R.drawable.volume_dialog_background
)
}
-
is RingerDrawerState.Open -> {
drawerContainer.visibility = View.VISIBLE
selectedButtonView.visibility = View.GONE
@@ -91,17 +89,16 @@
uiModel.currentButtonIndex !=
uiModel.availableButtons.size - 1
) {
- volumeDialogView.setBackgroundResource(
+ volumeDialogBackgroundView.setBackgroundResource(
R.drawable.volume_dialog_background_small_radius
)
}
}
}
}
-
is RingerViewModelState.Unavailable -> {
drawerAndRingerContainer.visibility = View.GONE
- volumeDialogView.setBackgroundResource(
+ volumeDialogBackgroundView.setBackgroundResource(
R.drawable.volume_dialog_background
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSlidersInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSlidersInteractor.kt
index 7af4258..c904ac5 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSlidersInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSlidersInteractor.kt
@@ -35,6 +35,7 @@
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.runningReduce
import kotlinx.coroutines.flow.stateIn
private const val DEFAULT_STREAM = AudioManager.STREAM_MUSIC
@@ -54,13 +55,17 @@
volumeDialogStateInteractor.volumeDialogState
.filter { it.streamModels.isNotEmpty() }
.map { stateModel ->
- stateModel.streamModels.values
- .filter { streamModel -> shouldShowSliders(stateModel, streamModel) }
- .sortedWith(streamsSorter)
+ val sliderTypes =
+ stateModel.streamModels.values
+ .filter { streamModel -> shouldShowSliders(stateModel, streamModel) }
+ .sortedWith(streamsSorter)
+ .map { model -> model.toType() }
+ LinkedHashSet(sliderTypes)
}
- .map { models ->
- val sliderTypes: List<VolumeDialogSliderType> =
- models.map { model -> model.toType() }
+ .runningReduce { sliderTypes, newSliderTypes ->
+ newSliderTypes.apply { addAll(sliderTypes) }
+ }
+ .map { sliderTypes ->
VolumeDialogSlidersModel(
slider = sliderTypes.first(),
floatingSliders = sliderTypes.drop(1),
@@ -84,7 +89,7 @@
// Always show the stream for audio sharing if it exists.
if (
- Flags.volumeDialogAudioSharingFix() &&
+ (Flags.volumeDialogAudioSharingFix() || Flags.audioSharingDeveloperOption()) &&
streamModel.stream == VolumeDialogControllerImpl.DYNAMIC_STREAM_BROADCAST
) {
return true
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSlidersViewBinder.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSlidersViewBinder.kt
index a17c1e5..9078f82 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSlidersViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSlidersViewBinder.kt
@@ -38,9 +38,10 @@
fun bind(view: View) {
with(view) {
- val volumeDialog: View = requireViewById(R.id.volume_dialog)
val floatingSlidersContainer: ViewGroup =
requireViewById(R.id.volume_dialog_floating_sliders_container)
+ val mainSliderContainer: View =
+ requireViewById(R.id.volume_dialog_main_slider_container)
repeatWhenAttached {
viewModel(
traceName = "VolumeDialogSlidersViewBinder",
@@ -49,7 +50,7 @@
) { viewModel ->
viewModel.sliders
.onEach { uiModel ->
- uiModel.sliderComponent.sliderViewBinder().bind(volumeDialog)
+ uiModel.sliderComponent.sliderViewBinder().bind(mainSliderContainer)
val floatingSliderViewBinders = uiModel.floatingSliderComponent
floatingSlidersContainer.ensureChildCount(
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogViewBinder.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogViewBinder.kt
index d9a945c..f6c1743 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogViewBinder.kt
@@ -19,13 +19,11 @@
import android.app.Dialog
import android.graphics.Rect
import android.graphics.Region
-import android.view.Gravity
import android.view.View
import android.view.ViewGroup
import android.view.ViewTreeObserver
import android.view.ViewTreeObserver.InternalInsetsInfo
-import android.widget.FrameLayout
-import androidx.annotation.GravityInt
+import androidx.constraintlayout.motion.widget.MotionLayout
import com.android.internal.view.RotationPolicy
import com.android.systemui.lifecycle.WindowLifecycleState
import com.android.systemui.lifecycle.repeatWhenAttached
@@ -41,7 +39,6 @@
import com.android.systemui.volume.dialog.ui.VolumeDialogResources
import com.android.systemui.volume.dialog.ui.utils.JankListenerFactory
import com.android.systemui.volume.dialog.ui.utils.suspendAnimate
-import com.android.systemui.volume.dialog.ui.viewmodel.VolumeDialogGravityViewModel
import com.android.systemui.volume.dialog.ui.viewmodel.VolumeDialogViewModel
import com.android.systemui.volume.dialog.utils.VolumeTracer
import javax.inject.Inject
@@ -53,6 +50,7 @@
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.mapLatest
import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.flow.scan
import kotlinx.coroutines.launch
import kotlinx.coroutines.suspendCancellableCoroutine
@@ -63,7 +61,6 @@
@Inject
constructor(
private val volumeResources: VolumeDialogResources,
- private val gravityViewModel: VolumeDialogGravityViewModel,
private val dialogViewModelFactory: VolumeDialogViewModel.Factory,
private val jankListenerFactory: JankListenerFactory,
private val tracer: VolumeTracer,
@@ -74,21 +71,23 @@
fun bind(dialog: Dialog) {
// Root view of the Volume Dialog.
- val root: ViewGroup = dialog.requireViewById(R.id.volume_dialog_root)
- // Volume Dialog container view that contains the dialog itself without the floating sliders
- val container: View = root.requireViewById(R.id.volume_dialog_container)
- container.alpha = 0f
- container.repeatWhenAttached {
+ val root: MotionLayout = dialog.requireViewById(R.id.volume_dialog_root)
+ root.alpha = 0f
+ root.repeatWhenAttached {
root.viewModel(
traceName = "VolumeDialogViewBinder",
minWindowLifecycleState = WindowLifecycleState.ATTACHED,
factory = { dialogViewModelFactory.create() },
) { viewModel ->
- animateVisibility(container, dialog, viewModel.dialogVisibilityModel)
+ animateVisibility(root, dialog, viewModel.dialogVisibilityModel)
viewModel.dialogTitle.onEach { dialog.window?.setTitle(it) }.launchIn(this)
- gravityViewModel.dialogGravity
- .onEach { container.setLayoutGravity(it) }
+ viewModel.motionState
+ .scan(0) { acc, motionState ->
+ // don't animate the initial state
+ root.transitionToState(motionState, animate = acc != 0)
+ acc + 1
+ }
.launchIn(this)
launch { root.viewTreeObserver.computeInternalInsetsListener(root) }
@@ -130,15 +129,13 @@
.launchIn(this)
}
- private suspend fun calculateTranslationX(view: View): Float? {
+ private fun calculateTranslationX(view: View): Float? {
return if (view.display.rotation == RotationPolicy.NATURAL_ROTATION) {
- val dialogGravity = gravityViewModel.dialogGravity.first()
- val isGravityLeft = (dialogGravity and Gravity.LEFT) == Gravity.LEFT
- if (isGravityLeft) {
+ if (view.isLayoutRtl) {
-1
} else {
1
- } * view.width / 2.0f
+ } * view.width / 2f
} else {
null
}
@@ -211,10 +208,11 @@
getBoundsInWindow(boundsRect, false)
}
- private fun View.setLayoutGravity(@GravityInt newGravity: Int) {
- val frameLayoutParams =
- layoutParams as? FrameLayout.LayoutParams
- ?: error("View must be a child of a FrameLayout")
- layoutParams = frameLayoutParams.apply { gravity = newGravity }
+ private fun MotionLayout.transitionToState(newState: Int, animate: Boolean) {
+ if (animate) {
+ transitionToState(newState)
+ } else {
+ jumpToState(newState)
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogGravityViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogGravityViewModel.kt
deleted file mode 100644
index 112afb1..0000000
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogGravityViewModel.kt
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.volume.dialog.ui.viewmodel
-
-import android.content.Context
-import android.content.res.Configuration
-import android.view.Gravity
-import androidx.annotation.GravityInt
-import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.dagger.qualifiers.UiBackground
-import com.android.systemui.res.R
-import com.android.systemui.statusbar.policy.ConfigurationController
-import com.android.systemui.statusbar.policy.DevicePostureController
-import com.android.systemui.statusbar.policy.devicePosture
-import com.android.systemui.statusbar.policy.onConfigChanged
-import com.android.systemui.volume.dialog.dagger.scope.VolumeDialog
-import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogScope
-import javax.inject.Inject
-import kotlin.coroutines.CoroutineContext
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.SharingStarted
-import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.onEach
-import kotlinx.coroutines.flow.stateIn
-import kotlinx.coroutines.withContext
-
-/** Exposes dialog [GravityInt] for use in the UI layer. */
-@VolumeDialogScope
-class VolumeDialogGravityViewModel
-@Inject
-constructor(
- @Application private val context: Context,
- @VolumeDialog private val coroutineScope: CoroutineScope,
- @UiBackground private val uiBackgroundCoroutineContext: CoroutineContext,
- configurationController: ConfigurationController,
- private val devicePostureController: DevicePostureController,
-) {
-
- @GravityInt private var originalGravity: Int = context.getAbsoluteGravity()
-
- val dialogGravity: Flow<Int> =
- combine(
- devicePostureController.devicePosture(),
- configurationController.onConfigChanged.onEach { onConfigurationChanged() },
- ) { devicePosture, configuration ->
- context.calculateGravity(devicePosture, configuration)
- }
- .stateIn(
- scope = coroutineScope,
- started = SharingStarted.Eagerly,
- context.calculateGravity(),
- )
-
- private suspend fun onConfigurationChanged() {
- withContext(uiBackgroundCoroutineContext) { originalGravity = context.getAbsoluteGravity() }
- }
-
- @GravityInt
- private fun Context.calculateGravity(
- devicePosture: Int = devicePostureController.devicePosture,
- config: Configuration = resources.configuration,
- ): Int {
- val isLandscape = config.orientation == Configuration.ORIENTATION_LANDSCAPE
- val isHalfOpen = devicePosture == DevicePostureController.DEVICE_POSTURE_HALF_OPENED
- val gravity =
- if (isLandscape && isHalfOpen) {
- originalGravity or Gravity.TOP
- } else {
- originalGravity
- }
- return getAbsoluteGravity(gravity)
- }
-}
-
-@GravityInt
-private fun Context.getAbsoluteGravity(
- gravity: Int = resources.getInteger(R.integer.volume_dialog_gravity)
-): Int = with(resources) { Gravity.getAbsoluteGravity(gravity, configuration.layoutDirection) }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogViewModel.kt
index 869a6a2..0352799 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogViewModel.kt
@@ -17,7 +17,12 @@
package com.android.systemui.volume.dialog.ui.viewmodel
import android.content.Context
+import android.content.res.Configuration
import com.android.systemui.res.R
+import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.statusbar.policy.DevicePostureController
+import com.android.systemui.statusbar.policy.devicePosture
+import com.android.systemui.statusbar.policy.onConfigChanged
import com.android.systemui.volume.dialog.domain.interactor.VolumeDialogStateInteractor
import com.android.systemui.volume.dialog.domain.interactor.VolumeDialogVisibilityInteractor
import com.android.systemui.volume.dialog.shared.model.VolumeDialogStateModel
@@ -32,6 +37,7 @@
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onStart
/** Provides a state for the Volume Dialog. */
@OptIn(ExperimentalCoroutinesApi::class)
@@ -42,8 +48,23 @@
dialogVisibilityInteractor: VolumeDialogVisibilityInteractor,
volumeDialogSlidersInteractor: VolumeDialogSlidersInteractor,
volumeDialogStateInteractor: VolumeDialogStateInteractor,
+ devicePostureController: DevicePostureController,
+ configurationController: ConfigurationController,
) {
+ val motionState: Flow<Int> =
+ combine(
+ devicePostureController.devicePosture(),
+ configurationController.onConfigChanged.onStart {
+ emit(context.resources.configuration)
+ },
+ ) { devicePosture, configuration ->
+ if (shouldOffsetVolumeDialog(devicePosture, configuration)) {
+ R.id.volume_dialog_half_folded_constraint_set
+ } else {
+ R.id.volume_dialog_constraint_set
+ }
+ }
val dialogVisibilityModel: Flow<VolumeDialogVisibilityModel> =
dialogVisibilityInteractor.dialogVisibility
val dialogTitle: Flow<String> =
@@ -57,6 +78,13 @@
}
.filterNotNull()
+ /** @return true when the foldable device screen curve is in the way of the volume dialog */
+ private fun shouldOffsetVolumeDialog(devicePosture: Int, config: Configuration): Boolean {
+ val isLandscape = config.orientation == Configuration.ORIENTATION_LANDSCAPE
+ val isHalfOpen = devicePosture == DevicePostureController.DEVICE_POSTURE_HALF_OPENED
+ return isLandscape && isHalfOpen
+ }
+
@AssistedFactory
interface Factory {
fun create(): VolumeDialogViewModel
diff --git a/packages/SystemUI/src/com/android/systemui/volume/domain/interactor/AudioSharingInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/domain/interactor/AudioSharingInteractor.kt
index 293be94..7da041e 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/domain/interactor/AudioSharingInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/domain/interactor/AudioSharingInteractor.kt
@@ -17,8 +17,12 @@
package com.android.systemui.volume.domain.interactor
import android.bluetooth.BluetoothCsipSetCoordinator
+import android.content.Context
import android.media.AudioManager.STREAM_MUSIC
import androidx.annotation.IntRange
+import com.android.app.tracing.coroutines.launchTraced as launch
+import com.android.settingslib.bluetooth.BluetoothUtils
+import com.android.settingslib.flags.Flags
import com.android.settingslib.volume.data.repository.AudioSharingRepository
import com.android.settingslib.volume.data.repository.AudioSharingRepository.Companion.AUDIO_SHARING_VOLUME_MAX
import com.android.settingslib.volume.data.repository.AudioSharingRepository.Companion.AUDIO_SHARING_VOLUME_MIN
@@ -38,7 +42,6 @@
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
-import com.android.app.tracing.coroutines.launchTraced as launch
import kotlinx.coroutines.withContext
interface AudioSharingInteractor {
@@ -54,6 +57,8 @@
/** Audio sharing secondary headset max volume. */
val volumeMax: Int
+ suspend fun audioSharingVolumeBarAvailable(@Application context: Context): Boolean
+
/** Set the volume of the secondary headset in audio sharing. */
fun setStreamVolume(
@IntRange(from = AUDIO_SHARING_VOLUME_MIN.toLong(), to = AUDIO_SHARING_VOLUME_MAX.toLong())
@@ -78,7 +83,7 @@
@Application private val coroutineScope: CoroutineScope,
@Background private val backgroundCoroutineContext: CoroutineContext,
private val audioVolumeInteractor: AudioVolumeInteractor,
- private val audioSharingRepository: AudioSharingRepository
+ private val audioSharingRepository: AudioSharingRepository,
) : AudioSharingInteractor {
override val isInAudioSharing: Flow<Boolean> = audioSharingRepository.inAudioSharing
@@ -95,6 +100,12 @@
override val volumeMax: Int = AUDIO_SHARING_VOLUME_MAX
+ override suspend fun audioSharingVolumeBarAvailable(@Application context: Context): Boolean =
+ withContext(backgroundCoroutineContext) {
+ (Flags.volumeDialogAudioSharingFix() && BluetoothUtils.isAudioSharingEnabled()) ||
+ BluetoothUtils.isAudioSharingPreviewEnabled(context.contentResolver)
+ }
+
override fun setStreamVolume(
@IntRange(from = AUDIO_SHARING_VOLUME_MIN.toLong(), to = AUDIO_SHARING_VOLUME_MAX.toLong())
level: Int
@@ -141,6 +152,9 @@
override val volumeMin: Int = EMPTY_VOLUME
override val volumeMax: Int = EMPTY_VOLUME
+ override suspend fun audioSharingVolumeBarAvailable(@Application context: Context): Boolean =
+ false
+
override fun setStreamVolume(
@IntRange(from = AUDIO_SHARING_VOLUME_MIN.toLong(), to = AUDIO_SHARING_VOLUME_MAX.toLong())
level: Int
diff --git a/packages/SystemUI/src/com/android/systemui/volume/shared/VolumeLogger.kt b/packages/SystemUI/src/com/android/systemui/volume/shared/VolumeLogger.kt
index d6b159e..3847df6 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/shared/VolumeLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/shared/VolumeLogger.kt
@@ -41,7 +41,7 @@
str1 = audioStream.toString()
int1 = volume
},
- { "Set volume: stream=$str1 volume=$int1" }
+ { "Set volume: stream=$str1 volume=$int1" },
)
}
@@ -53,7 +53,7 @@
str1 = audioStream.toString()
int1 = model.volume
},
- { "Volume update received: stream=$str1 volume=$int1" }
+ { "Volume update received: stream=$str1 volume=$int1" },
)
}
@@ -62,7 +62,7 @@
TAG,
LogLevel.DEBUG,
{ bool1 = state },
- { "Audio sharing state update: state=$bool1" }
+ { "Audio sharing state update: state=$bool1" },
)
}
@@ -71,7 +71,7 @@
TAG,
LogLevel.DEBUG,
{ int1 = groupId },
- { "Secondary group id in audio sharing update: groupId=$int1" }
+ { "Secondary group id in audio sharing update: groupId=$int1" },
)
}
@@ -80,11 +80,23 @@
TAG,
LogLevel.DEBUG,
{ str1 = map.toString() },
- { "Volume map update: map=$str1" }
+ { "Volume map update: map=$str1" },
)
}
override fun onSetDeviceVolumeRequested(volume: Int) {
logBuffer.log(TAG, LogLevel.DEBUG, { int1 = volume }, { "Set device volume: volume=$int1" })
}
+
+ override fun onAudioSharingAvailabilityRequestedError(requestFrom: String, e: String) {
+ logBuffer.log(
+ TAG,
+ LogLevel.WARNING,
+ {
+ str1 = requestFrom
+ str1 = e
+ },
+ { "$str1, fail to check audio sharing availability: e=$str2" },
+ )
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
index 65b6273..2aa6e7b 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
@@ -105,8 +105,7 @@
private val mainExecutor = ImmediateExecutor()
private lateinit var repository: FakeKeyguardRepository
- private val messageBuffer = LogcatOnlyMessageBuffer(LogLevel.DEBUG)
- private val clockBuffers = ClockMessageBuffers(messageBuffer, messageBuffer, messageBuffer)
+ private val clockBuffers = ClockMessageBuffers(LogcatOnlyMessageBuffer(LogLevel.DEBUG))
private lateinit var underTest: ClockEventController
@Mock private lateinit var broadcastDispatcher: BroadcastDispatcher
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityTransitionAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityTransitionAnimatorTest.kt
index a940bc9..425aad2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityTransitionAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityTransitionAnimatorTest.kt
@@ -16,10 +16,12 @@
import android.view.RemoteAnimationTarget
import android.view.SurfaceControl
import android.view.ViewGroup
+import android.view.WindowManager.TRANSIT_NONE
import android.widget.FrameLayout
import android.widget.LinearLayout
import android.window.RemoteTransition
import android.window.TransitionFilter
+import android.window.WindowAnimationState
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
@@ -34,6 +36,10 @@
import junit.framework.AssertionFailedError
import kotlin.concurrent.thread
import kotlin.test.assertEquals
+import kotlin.time.Duration.Companion.seconds
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.withTimeout
import org.junit.After
import org.junit.Assert.assertThrows
import org.junit.Before
@@ -258,7 +264,6 @@
@DisableFlags(Flags.FLAG_RETURN_ANIMATION_FRAMEWORK_LONG_LIVED)
@Test
fun doesNotRegisterLongLivedTransitionIfFlagIsDisabled() {
-
val controller =
object : DelegateTransitionAnimatorController(controller) {
override val transitionCookie =
@@ -273,7 +278,6 @@
@EnableFlags(Flags.FLAG_RETURN_ANIMATION_FRAMEWORK_LONG_LIVED)
@Test
fun doesNotRegisterLongLivedTransitionIfMissingRequiredProperties() {
-
// No TransitionCookie
val controllerWithoutCookie =
object : DelegateTransitionAnimatorController(controller) {
@@ -348,7 +352,7 @@
fun doesNotStartIfAnimationIsCancelled() {
val runner = activityTransitionAnimator.createRunner(controller)
runner.onAnimationCancelled()
- runner.onAnimationStart(0, emptyArray(), emptyArray(), emptyArray(), iCallback)
+ runner.onAnimationStart(TRANSIT_NONE, emptyArray(), emptyArray(), emptyArray(), iCallback)
waitForIdleSync()
verify(controller).onTransitionAnimationCancelled()
@@ -361,7 +365,7 @@
@Test
fun cancelsIfNoOpeningWindowIsFound() {
val runner = activityTransitionAnimator.createRunner(controller)
- runner.onAnimationStart(0, emptyArray(), emptyArray(), emptyArray(), iCallback)
+ runner.onAnimationStart(TRANSIT_NONE, emptyArray(), emptyArray(), emptyArray(), iCallback)
waitForIdleSync()
verify(controller).onTransitionAnimationCancelled()
@@ -374,7 +378,13 @@
@Test
fun startsAnimationIfWindowIsOpening() {
val runner = activityTransitionAnimator.createRunner(controller)
- runner.onAnimationStart(0, arrayOf(fakeWindow()), emptyArray(), emptyArray(), iCallback)
+ runner.onAnimationStart(
+ TRANSIT_NONE,
+ arrayOf(fakeWindow()),
+ emptyArray(),
+ emptyArray(),
+ iCallback,
+ )
waitForIdleSync()
verify(listener).onTransitionAnimationStart()
verify(controller).onTransitionAnimationStart(anyBoolean())
@@ -387,6 +397,113 @@
}
}
+ @DisableFlags(
+ Flags.FLAG_RETURN_ANIMATION_FRAMEWORK_LIBRARY,
+ Flags.FLAG_RETURN_ANIMATION_FRAMEWORK_LONG_LIVED,
+ )
+ @Test
+ fun creatingRunnerWithLazyInitializationThrows_whenTheFlagsAreDisabled() {
+ assertThrows(IllegalStateException::class.java) {
+ activityTransitionAnimator.createRunner(controller, initializeLazily = true)
+ }
+ }
+
+ @EnableFlags(
+ Flags.FLAG_RETURN_ANIMATION_FRAMEWORK_LIBRARY,
+ Flags.FLAG_RETURN_ANIMATION_FRAMEWORK_LONG_LIVED,
+ )
+ @Test
+ fun runnerCreatesDelegateLazily_whenPostingTimeouts() {
+ val runner = activityTransitionAnimator.createRunner(controller, initializeLazily = true)
+ assertNull(runner.delegate)
+ runner.postTimeouts()
+ assertNotNull(runner.delegate)
+ }
+
+ @EnableFlags(
+ Flags.FLAG_RETURN_ANIMATION_FRAMEWORK_LIBRARY,
+ Flags.FLAG_RETURN_ANIMATION_FRAMEWORK_LONG_LIVED,
+ )
+ @Test
+ fun runnerCreatesDelegateLazily_onAnimationStart() {
+ val runner = activityTransitionAnimator.createRunner(controller, initializeLazily = true)
+ assertNull(runner.delegate)
+
+ // The delegate is cleaned up after execution (which happens in another thread), so what we
+ // do instead is check if it becomes non-null at any point with a 1 second timeout. This
+ // will tell us that takeOverWithAnimation() triggered the lazy initialization.
+ var delegateInitialized = false
+ runBlocking {
+ val initChecker = launch {
+ withTimeout(1.seconds) {
+ while (runner.delegate == null) continue
+ delegateInitialized = true
+ }
+ }
+ runner.onAnimationStart(
+ TRANSIT_NONE,
+ arrayOf(fakeWindow()),
+ emptyArray(),
+ emptyArray(),
+ iCallback,
+ )
+ initChecker.join()
+ }
+ assertTrue(delegateInitialized)
+ }
+
+ @EnableFlags(
+ Flags.FLAG_RETURN_ANIMATION_FRAMEWORK_LIBRARY,
+ Flags.FLAG_RETURN_ANIMATION_FRAMEWORK_LONG_LIVED,
+ )
+ @Test
+ fun runnerCreatesDelegateLazily_onAnimationTakeover() {
+ val runner = activityTransitionAnimator.createRunner(controller, initializeLazily = true)
+ assertNull(runner.delegate)
+
+ // The delegate is cleaned up after execution (which happens in another thread), so what we
+ // do instead is check if it becomes non-null at any point with a 1 second timeout. This
+ // will tell us that takeOverWithAnimation() triggered the lazy initialization.
+ var delegateInitialized = false
+ runBlocking {
+ val initChecker = launch {
+ withTimeout(1.seconds) {
+ while (runner.delegate == null) continue
+ delegateInitialized = true
+ }
+ }
+ runner.takeOverAnimation(
+ arrayOf(fakeWindow()),
+ arrayOf(WindowAnimationState()),
+ SurfaceControl.Transaction(),
+ iCallback,
+ )
+ initChecker.join()
+ }
+ assertTrue(delegateInitialized)
+ }
+
+ @DisableFlags(
+ Flags.FLAG_RETURN_ANIMATION_FRAMEWORK_LIBRARY,
+ Flags.FLAG_RETURN_ANIMATION_FRAMEWORK_LONG_LIVED,
+ )
+ @Test
+ fun animationTakeoverThrows_whenTheFlagsAreDisabled() {
+ val runner = activityTransitionAnimator.createRunner(controller, initializeLazily = false)
+ assertThrows(IllegalStateException::class.java) {
+ runner.takeOverAnimation(
+ arrayOf(fakeWindow()),
+ emptyArray(),
+ SurfaceControl.Transaction(),
+ iCallback,
+ )
+ }
+ }
+
+ @DisableFlags(
+ Flags.FLAG_RETURN_ANIMATION_FRAMEWORK_LIBRARY,
+ Flags.FLAG_RETURN_ANIMATION_FRAMEWORK_LONG_LIVED,
+ )
@Test
fun disposeRunner_delegateDereferenced() {
val runner = activityTransitionAnimator.createRunner(controller)
@@ -409,7 +526,7 @@
false,
Rect(),
Rect(),
- 0,
+ 1,
Point(),
Rect(),
bounds,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/data/backup/CommunalBackupHelperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/communal/data/backup/CommunalBackupHelperTest.kt
index 6e9b24f..4ca84c58 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/communal/data/backup/CommunalBackupHelperTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/data/backup/CommunalBackupHelperTest.kt
@@ -37,7 +37,6 @@
import java.io.File
import java.io.FileInputStream
import java.io.FileOutputStream
-import org.junit.After
import org.junit.Before
import org.junit.Rule
import org.junit.Test
@@ -63,22 +62,18 @@
Room.inMemoryDatabaseBuilder(context, CommunalDatabase::class.java)
.allowMainThreadQueries()
.build()
+ onTeardown { database.close() }
CommunalDatabase.setInstance(database)
dao = database.communalWidgetDao()
backupUtils = CommunalBackupUtils(context)
backupDataFile = File(context.cacheDir, "backup_data_file")
+ onTeardown { backupDataFile.delete() }
underTest = CommunalBackupHelper(UserHandle.SYSTEM, backupUtils)
}
- @After
- fun teardown() {
- backupDataFile.delete()
- database.close()
- }
-
@Test
@EnableFlags(Flags.FLAG_COMMUNAL_HUB)
fun backupAndRestoreCommunalHub() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutCustomizationViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutCustomizationViewModelTest.kt
index f8d8481..a9b6dd1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutCustomizationViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutCustomizationViewModelTest.kt
@@ -26,6 +26,7 @@
import com.android.systemui.keyboard.shortcut.shortcutCustomizationViewModelFactory
import com.android.systemui.keyboard.shortcut.ui.model.ShortcutCustomizationUiState
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testCase
import com.android.systemui.kosmos.testScope
import com.android.systemui.res.R
import com.google.common.truth.Truth.assertThat
@@ -37,7 +38,7 @@
@RunWith(AndroidJUnit4::class)
class ShortcutCustomizationViewModelTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = Kosmos().also { it.testCase = this }
private val testScope = kosmos.testScope
private val viewModel = kosmos.shortcutCustomizationViewModelFactory.create()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModelTest.kt
index 7383faf..feae901 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModelTest.kt
@@ -278,7 +278,11 @@
testScope.runTest {
fakeSystemSource.setGroups(
groupWithShortcutLabels("first Foo shortcut1", "first bar shortcut1"),
- groupWithShortcutLabels("second foO shortcut2", "second bar shortcut2"),
+ groupWithShortcutLabels(
+ "second foO shortcut2",
+ "second bar shortcut2",
+ groupLabel = SECOND_SIMPLE_GROUP_LABEL,
+ ),
)
fakeMultiTaskingSource.setGroups(
groupWithShortcutLabels("third FoO shortcut1", "third bar shortcut1")
@@ -298,7 +302,10 @@
ShortcutCategory(
System,
subCategoryWithShortcutLabels("first Foo shortcut1"),
- subCategoryWithShortcutLabels("second foO shortcut2"),
+ subCategoryWithShortcutLabels(
+ "second foO shortcut2",
+ subCategoryLabel = SECOND_SIMPLE_GROUP_LABEL,
+ ),
),
),
ShortcutCategoryUi(
@@ -380,16 +387,23 @@
assertThat(activeUiState.defaultSelectedCategory).isInstanceOf(CurrentApp::class.java)
}
- private fun groupWithShortcutLabels(vararg shortcutLabels: String) =
- KeyboardShortcutGroup(SIMPLE_GROUP_LABEL, shortcutLabels.map { simpleShortcutInfo(it) })
- .apply { packageName = "test.package.name" }
+ private fun groupWithShortcutLabels(
+ vararg shortcutLabels: String,
+ groupLabel: String = FIRST_SIMPLE_GROUP_LABEL,
+ ) =
+ KeyboardShortcutGroup(groupLabel, shortcutLabels.map { simpleShortcutInfo(it) }).apply {
+ packageName = "test.package.name"
+ }
private fun simpleShortcutInfo(label: String) =
KeyboardShortcutInfo(label, KeyEvent.KEYCODE_A, KeyEvent.META_CTRL_ON)
- private fun subCategoryWithShortcutLabels(vararg shortcutLabels: String) =
+ private fun subCategoryWithShortcutLabels(
+ vararg shortcutLabels: String,
+ subCategoryLabel: String = FIRST_SIMPLE_GROUP_LABEL,
+ ) =
ShortcutSubCategory(
- label = SIMPLE_GROUP_LABEL,
+ label = subCategoryLabel,
shortcuts = shortcutLabels.map { simpleShortcut(it) },
)
@@ -402,6 +416,7 @@
}
companion object {
- private const val SIMPLE_GROUP_LABEL = "simple group"
+ private const val FIRST_SIMPLE_GROUP_LABEL = "simple group 1"
+ private const val SECOND_SIMPLE_GROUP_LABEL = "simple group 2"
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
index e1845a1..7e85dd5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
@@ -213,7 +213,7 @@
)
val keyguardTouchHandlingInteractor =
KeyguardTouchHandlingInteractor(
- appContext = mContext,
+ context = mContext,
scope = testScope.backgroundScope,
transitionInteractor = kosmos.keyguardTransitionInteractor,
repository = repository,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateControllerTest.java
index 0b9c06f..5ada2f3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateControllerTest.java
@@ -222,7 +222,7 @@
when(SubscriptionManager.getDefaultDataSubscriptionId()).thenReturn(SUB_ID);
SubscriptionInfo info = mock(SubscriptionInfo.class);
when(mSubscriptionManager.getActiveSubscriptionInfo(SUB_ID)).thenReturn(info);
- when(mToastFactory.createToast(any(), anyString(), anyString(), anyInt(), anyInt()))
+ when(mToastFactory.createToast(any(), any(), anyString(), anyString(), anyInt(), anyInt()))
.thenReturn(mSystemUIToast);
when(mSystemUIToast.getView()).thenReturn(mToastView);
when(mSystemUIToast.getGravity()).thenReturn(GRAVITY_FLAGS);
@@ -275,8 +275,8 @@
mInternetDialogController.connectCarrierNetwork();
verify(mMergedCarrierEntry).connect(null /* callback */, false /* showToast */);
- verify(mToastFactory).createToast(any(), eq(TOAST_MESSAGE_STRING), anyString(), anyInt(),
- anyInt());
+ verify(mToastFactory).createToast(any(), any(), eq(TOAST_MESSAGE_STRING), anyString(),
+ anyInt(), anyInt());
}
@Test
@@ -288,7 +288,7 @@
mInternetDialogController.connectCarrierNetwork();
verify(mMergedCarrierEntry, never()).connect(null /* callback */, false /* showToast */);
- verify(mToastFactory, never()).createToast(any(), anyString(), anyString(), anyInt(),
+ verify(mToastFactory, never()).createToast(any(), any(), anyString(), anyString(), anyInt(),
anyInt());
}
@@ -302,7 +302,7 @@
mInternetDialogController.connectCarrierNetwork();
verify(mMergedCarrierEntry, never()).connect(null /* callback */, false /* showToast */);
- verify(mToastFactory, never()).createToast(any(), anyString(), anyString(), anyInt(),
+ verify(mToastFactory, never()).createToast(any(), any(), anyString(), anyString(), anyInt(),
anyInt());
}
@@ -321,7 +321,7 @@
mInternetDialogController.connectCarrierNetwork();
verify(mMergedCarrierEntry, never()).connect(null /* callback */, false /* showToast */);
- verify(mToastFactory, never()).createToast(any(), anyString(), anyString(), anyInt(),
+ verify(mToastFactory, never()).createToast(any(), any(), anyString(), anyString(), anyInt(),
anyInt());
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/reardisplay/RearDisplayCoreStartableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/reardisplay/RearDisplayCoreStartableTest.kt
new file mode 100644
index 0000000..c8faa81
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/reardisplay/RearDisplayCoreStartableTest.kt
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.reardisplay
+
+import android.hardware.devicestate.feature.flags.Flags.FLAG_DEVICE_STATE_RDM_V2
+import android.hardware.display.rearDisplay
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
+import android.view.Display
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.deviceStateManager
+import com.android.systemui.display.domain.interactor.RearDisplayStateInteractor
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.runTest
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
+import com.android.systemui.rearDisplayInnerDialogDelegateFactory
+import com.android.systemui.statusbar.phone.SystemUIDialog
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableSharedFlow
+import org.junit.Before
+import org.junit.Test
+import org.mockito.kotlin.any
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.never
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
+
+/** atest SystemUITests:com.android.systemui.reardisplay.RearDisplayCoreStartableTest */
+@SmallTest
+@kotlinx.coroutines.ExperimentalCoroutinesApi
+class RearDisplayCoreStartableTest : SysuiTestCase() {
+
+ private val kosmos = testKosmos().useUnconfinedTestDispatcher()
+ private val mockDelegate: RearDisplayInnerDialogDelegate = mock()
+ private val mockDialog: SystemUIDialog = mock()
+
+ private val fakeRearDisplayStateInteractor = FakeRearDisplayStateInteractor(kosmos)
+ private val impl =
+ RearDisplayCoreStartable(
+ mContext,
+ kosmos.deviceStateManager,
+ fakeRearDisplayStateInteractor,
+ kosmos.rearDisplayInnerDialogDelegateFactory,
+ kosmos.testScope,
+ )
+
+ @Before
+ fun setup() {
+ whenever(kosmos.rearDisplay.flags).thenReturn(Display.FLAG_REAR)
+ whenever(kosmos.rearDisplay.displayAdjustments)
+ .thenReturn(mContext.display.displayAdjustments)
+ whenever(kosmos.rearDisplayInnerDialogDelegateFactory.create(any(), any()))
+ .thenReturn(mockDelegate)
+ whenever(mockDelegate.createDialog()).thenReturn(mockDialog)
+ }
+
+ @Test
+ @DisableFlags(FLAG_DEVICE_STATE_RDM_V2)
+ fun testWhenFlagDisabled() =
+ kosmos.runTest {
+ impl.use {
+ it.start()
+ assertThat(impl.stateChangeListener).isNull()
+ }
+ }
+
+ @Test
+ @EnableFlags(FLAG_DEVICE_STATE_RDM_V2)
+ fun testShowAndDismissDialog() =
+ kosmos.runTest {
+ impl.use {
+ it.start()
+ fakeRearDisplayStateInteractor.emitRearDisplay()
+ verify(mockDialog).show()
+ verify(mockDialog, never()).dismiss()
+
+ fakeRearDisplayStateInteractor.emitDisabled()
+ verify(mockDialog).dismiss()
+ }
+ }
+
+ private class FakeRearDisplayStateInteractor(private val kosmos: Kosmos) :
+ RearDisplayStateInteractor {
+ private val stateFlow = MutableSharedFlow<RearDisplayStateInteractor.State>()
+
+ suspend fun emitRearDisplay() =
+ stateFlow.emit(RearDisplayStateInteractor.State.Enabled(kosmos.rearDisplay))
+
+ suspend fun emitDisabled() = stateFlow.emit(RearDisplayStateInteractor.State.Disabled)
+
+ override val state: Flow<RearDisplayStateInteractor.State>
+ get() = stateFlow
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/reardisplay/RearDisplayInnerDialogDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/reardisplay/RearDisplayInnerDialogDelegateTest.kt
new file mode 100644
index 0000000..6058880
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/reardisplay/RearDisplayInnerDialogDelegateTest.kt
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.reardisplay
+
+import android.testing.TestableLooper
+import android.view.View
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.phone.systemUIDialogDotFactory
+import com.android.systemui.testKosmos
+import junit.framework.Assert.assertFalse
+import junit.framework.Assert.assertTrue
+import org.junit.Test
+import org.mockito.Mockito.verify
+import org.mockito.kotlin.mock
+
+/** atest SystemUITests:com.android.systemui.reardisplay.RearDisplayInnerDialogDelegateTest */
+@SmallTest
+@TestableLooper.RunWithLooper
+class RearDisplayInnerDialogDelegateTest : SysuiTestCase() {
+
+ private val kosmos = testKosmos()
+
+ @Test
+ fun testShowAndDismissDialog() {
+ val dialogDelegate =
+ RearDisplayInnerDialogDelegate(kosmos.systemUIDialogDotFactory, mContext) {}
+
+ val dialog = dialogDelegate.createDialog()
+ dialog.show()
+ assertTrue(dialog.isShowing)
+
+ dialog.dismiss()
+ assertFalse(dialog.isShowing)
+ }
+
+ @Test
+ fun testCancel() {
+ val mockCallback = mock<Runnable>()
+ RearDisplayInnerDialogDelegate(kosmos.systemUIDialogDotFactory, mContext) {
+ mockCallback.run()
+ }
+ .createDialog()
+ .apply {
+ show()
+ findViewById<View>(R.id.button_cancel).performClick()
+ verify(mockCallback).run()
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextViewTest.kt
index 8bd8b72..2812bd3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextViewTest.kt
@@ -15,11 +15,16 @@
*/
package systemui.shared.clocks.view
+import android.graphics.Typeface
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.plugins.clocks.ClockMessageBuffers
+import com.android.systemui.plugins.clocks.ClockSettings
+import com.android.systemui.shared.clocks.ClockContext
import com.android.systemui.shared.clocks.FontTextStyle
import com.android.systemui.shared.clocks.LogUtil
+import com.android.systemui.shared.clocks.TypefaceCache
import com.android.systemui.shared.clocks.view.SimpleDigitalClockTextView
import org.junit.Assert.assertEquals
import org.junit.Before
@@ -38,7 +43,23 @@
@Before
fun setup() {
- underTest = SimpleDigitalClockTextView(context, messageBuffer)
+ underTest =
+ SimpleDigitalClockTextView(
+ ClockContext(
+ context,
+ context.resources,
+ ClockSettings(),
+ TypefaceCache(messageBuffer) {
+ // TODO(b/364680873): Move constant to config_clockFontFamily when shipping
+ return@TypefaceCache Typeface.create(
+ "google-sans-flex-clock",
+ Typeface.NORMAL,
+ )
+ },
+ ClockMessageBuffers(messageBuffer),
+ messageBuffer,
+ )
+ )
underTest.textStyle = FontTextStyle()
underTest.aodStyle = FontTextStyle()
underTest.text = "0"
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
index a8618eb..3a46d03 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
@@ -20,9 +20,8 @@
import com.android.systemui.shade.data.repository.shadeRepository
import com.android.systemui.shade.domain.interactor.ShadeLockscreenInteractor
import com.android.systemui.shade.domain.interactor.shadeInteractor
-import com.android.systemui.statusbar.disableflags.data.model.DisableFlagsModel
-import com.android.systemui.statusbar.disableflags.data.repository.disableFlagsRepository
import com.android.systemui.statusbar.disableflags.data.repository.fakeDisableFlagsRepository
+import com.android.systemui.statusbar.disableflags.shared.model.DisableFlagsModel
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.row.NotificationTestHelper
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout
@@ -31,7 +30,6 @@
import com.android.systemui.statusbar.phone.KeyguardBypassController
import com.android.systemui.statusbar.phone.ScrimController
import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController
-import com.android.systemui.statusbar.policy.configurationController
import com.android.systemui.statusbar.policy.fakeConfigurationController
import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.any
@@ -144,7 +142,7 @@
context = context,
configurationController = configurationController,
dumpManager = mock(),
- splitShadeStateController = ResourcesSplitShadeStateController()
+ splitShadeStateController = ResourcesSplitShadeStateController(),
),
keyguardTransitionControllerFactory = { notificationPanelController ->
LockscreenShadeKeyguardTransitionController(
@@ -153,7 +151,7 @@
context = context,
configurationController = configurationController,
dumpManager = mock(),
- splitShadeStateController = ResourcesSplitShadeStateController()
+ splitShadeStateController = ResourcesSplitShadeStateController(),
)
},
depthController = depthController,
@@ -171,7 +169,7 @@
splitShadeStateController = ResourcesSplitShadeStateController(),
shadeLockscreenInteractorLazy = { shadeLockscreenInteractor },
naturalScrollingSettingObserver = naturalScrollingSettingObserver,
- lazyQSSceneAdapter = { qsSceneAdapter }
+ lazyQSSceneAdapter = { qsSceneAdapter },
)
transitionController.addCallback(transitionControllerCallback)
@@ -229,7 +227,7 @@
verify(statusbarStateController).setState(StatusBarState.SHADE_LOCKED)
assertFalse(
"Waking to shade locked when not dozing",
- transitionController.isWakingToShadeLocked
+ transitionController.isWakingToShadeLocked,
)
}
@@ -247,9 +245,7 @@
fun testDontGoWhenShadeDisabled() =
testScope.runTest {
disableFlagsRepository.disableFlags.value =
- DisableFlagsModel(
- disable2 = DISABLE2_NOTIFICATION_SHADE,
- )
+ DisableFlagsModel(disable2 = DISABLE2_NOTIFICATION_SHADE)
testScope.runCurrent()
transitionController.goToLockedShade(null)
verify(statusbarStateController, never()).setState(anyInt())
@@ -454,7 +450,7 @@
val distance = 10
context.orCreateTestableResources.addOverride(
R.dimen.lockscreen_shade_scrim_transition_distance,
- distance
+ distance,
)
configurationController.notifyConfigurationChanged()
@@ -463,7 +459,7 @@
verify(scrimController)
.transitionToFullShadeProgress(
progress = eq(0.5f),
- lockScreenNotificationsProgress = anyFloat()
+ lockScreenNotificationsProgress = anyFloat(),
)
}
@@ -474,11 +470,11 @@
val delay = 10
context.orCreateTestableResources.addOverride(
R.dimen.lockscreen_shade_notifications_scrim_transition_distance,
- distance
+ distance,
)
context.orCreateTestableResources.addOverride(
R.dimen.lockscreen_shade_notifications_scrim_transition_delay,
- delay
+ delay,
)
configurationController.notifyConfigurationChanged()
@@ -487,7 +483,7 @@
verify(scrimController)
.transitionToFullShadeProgress(
progress = anyFloat(),
- lockScreenNotificationsProgress = eq(0.1f)
+ lockScreenNotificationsProgress = eq(0.1f),
)
}
@@ -498,11 +494,11 @@
val delay = 50
context.orCreateTestableResources.addOverride(
R.dimen.lockscreen_shade_notifications_scrim_transition_distance,
- distance
+ distance,
)
context.orCreateTestableResources.addOverride(
R.dimen.lockscreen_shade_notifications_scrim_transition_delay,
- delay
+ delay,
)
configurationController.notifyConfigurationChanged()
@@ -511,7 +507,7 @@
verify(scrimController)
.transitionToFullShadeProgress(
progress = anyFloat(),
- lockScreenNotificationsProgress = eq(0f)
+ lockScreenNotificationsProgress = eq(0f),
)
}
@@ -522,11 +518,11 @@
val delay = 50
context.orCreateTestableResources.addOverride(
R.dimen.lockscreen_shade_notifications_scrim_transition_distance,
- distance
+ distance,
)
context.orCreateTestableResources.addOverride(
R.dimen.lockscreen_shade_notifications_scrim_transition_delay,
- delay
+ delay,
)
configurationController.notifyConfigurationChanged()
@@ -535,7 +531,7 @@
verify(scrimController)
.transitionToFullShadeProgress(
progress = anyFloat(),
- lockScreenNotificationsProgress = eq(1f)
+ lockScreenNotificationsProgress = eq(1f),
)
}
@@ -627,7 +623,7 @@
*/
private fun ScrimController.transitionToFullShadeProgress(
progress: Float,
- lockScreenNotificationsProgress: Float
+ lockScreenNotificationsProgress: Float,
) {
setTransitionToFullShadeProgress(progress, lockScreenNotificationsProgress)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
index 856333e..aad8b4b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -2707,6 +2707,44 @@
eq(BubbleLogger.Event.BUBBLE_BAR_EXPANDED));
}
+ @EnableFlags(FLAG_ENABLE_BUBBLE_BAR)
+ @Test
+ public void testEventLogging_bubbleBar_openOverflow() {
+ mBubbleProperties.mIsBubbleBarEnabled = true;
+ mPositioner.setIsLargeScreen(true);
+ FakeBubbleStateListener bubbleStateListener = new FakeBubbleStateListener();
+ mBubbleController.registerBubbleStateListener(bubbleStateListener);
+
+ mEntryListener.onEntryAdded(mRow);
+
+ clearInvocations(mBubbleLogger);
+ mBubbleController.expandStackAndSelectBubbleFromLauncher(BubbleOverflow.KEY, 0);
+ verify(mBubbleLogger).log(BubbleLogger.Event.BUBBLE_BAR_OVERFLOW_SELECTED);
+ verifyNoMoreInteractions(mBubbleLogger);
+ }
+
+ @EnableFlags(FLAG_ENABLE_BUBBLE_BAR)
+ @Test
+ public void testEventLogging_bubbleBar_fromOverflowToBar() {
+ mBubbleProperties.mIsBubbleBarEnabled = true;
+ mPositioner.setIsLargeScreen(true);
+ FakeBubbleStateListener bubbleStateListener = new FakeBubbleStateListener();
+ mBubbleController.registerBubbleStateListener(bubbleStateListener);
+
+ mEntryListener.onEntryAdded(mRow);
+
+ // Dismiss the bubble so it's in the overflow
+ mBubbleController.removeBubble(
+ mRow.getKey(), Bubbles.DISMISS_USER_GESTURE);
+ Bubble overflowBubble = mBubbleData.getOverflowBubbleWithKey(mRow.getKey());
+ assertThat(overflowBubble).isNotNull();
+
+ // Promote overflow bubble and check that it is logged
+ mBubbleController.promoteBubbleFromOverflow(overflowBubble);
+ verify(mBubbleLogger).log(eqBubbleWithKey(overflowBubble.getKey()),
+ eq(BubbleLogger.Event.BUBBLE_BAR_OVERFLOW_REMOVE_BACK_TO_BAR));
+ }
+
/** Creates a bubble using the userId and package. */
private Bubble createBubble(int userId, String pkg) {
final UserHandle userHandle = new UserHandle(userId);
diff --git a/core/java/android/text/ClientFlags.java b/packages/SystemUI/tests/utils/src/android/content/ClipboardManagerKosmos.kt
similarity index 60%
copy from core/java/android/text/ClientFlags.java
copy to packages/SystemUI/tests/utils/src/android/content/ClipboardManagerKosmos.kt
index ca88764..379c008 100644
--- a/core/java/android/text/ClientFlags.java
+++ b/packages/SystemUI/tests/utils/src/android/content/ClipboardManagerKosmos.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,16 +14,9 @@
* limitations under the License.
*/
-package android.text;
+package android.content
-/**
- * An aconfig feature flags that can be accessible from application process without
- * ContentProvider IPCs.
- *
- * When you add new flags, you have to add flag string to {@link TextFlags#TEXT_ACONFIGS_FLAGS}.
- *
- * TODO(nona): Remove this class.
- * @hide
- */
-public class ClientFlags {
-}
+import com.android.systemui.kosmos.Kosmos
+import org.mockito.kotlin.mock
+
+val Kosmos.clipboardManager by Kosmos.Fixture { mock<ClipboardManager>() }
diff --git a/packages/SystemUI/tests/utils/src/android/hardware/display/DisplayManagerKosmos.kt b/packages/SystemUI/tests/utils/src/android/hardware/display/DisplayManagerKosmos.kt
index 796ec94..45dcb28 100644
--- a/packages/SystemUI/tests/utils/src/android/hardware/display/DisplayManagerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/android/hardware/display/DisplayManagerKosmos.kt
@@ -16,7 +16,12 @@
package android.hardware.display
+import android.view.Display
import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.util.mockito.mock
+import org.mockito.kotlin.mock
val Kosmos.displayManager by Kosmos.Fixture { mock<DisplayManager>() }
+
+val Kosmos.defaultDisplay: Display by Kosmos.Fixture { mock<Display>() }
+
+val Kosmos.rearDisplay: Display by Kosmos.Fixture { mock<Display>() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/DeviceStateManagerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/DeviceStateManagerKosmos.kt
index 9c55820..b8a095e 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/DeviceStateManagerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/DeviceStateManagerKosmos.kt
@@ -18,6 +18,7 @@
import android.hardware.devicestate.DeviceState
import android.hardware.devicestate.DeviceState.PROPERTY_FEATURE_REAR_DISPLAY
+import android.hardware.devicestate.DeviceState.PROPERTY_FEATURE_REAR_DISPLAY_OUTER_DEFAULT
import android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY
import android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY
import android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_CLOSED
@@ -44,7 +45,7 @@
.setSystemProperties(
setOf(
PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY,
- PROPERTY_POWER_CONFIGURATION_TRIGGER_SLEEP
+ PROPERTY_POWER_CONFIGURATION_TRIGGER_SLEEP,
)
)
.setPhysicalProperties(
@@ -57,7 +58,7 @@
.setSystemProperties(
setOf(
PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY,
- PROPERTY_POWER_CONFIGURATION_TRIGGER_SLEEP
+ PROPERTY_POWER_CONFIGURATION_TRIGGER_SLEEP,
)
)
.setPhysicalProperties(
@@ -70,14 +71,14 @@
.setSystemProperties(
setOf(
PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY,
- PROPERTY_POWER_CONFIGURATION_TRIGGER_SLEEP
+ PROPERTY_POWER_CONFIGURATION_TRIGGER_SLEEP,
)
)
.setPhysicalProperties(
setOf(PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_CLOSED)
)
.build()
- )
+ ),
)
}
@@ -88,7 +89,7 @@
.setSystemProperties(
setOf(
PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY,
- PROPERTY_POWER_CONFIGURATION_TRIGGER_WAKE
+ PROPERTY_POWER_CONFIGURATION_TRIGGER_WAKE,
)
)
.setPhysicalProperties(
@@ -105,7 +106,7 @@
.setSystemProperties(
setOf(
PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY,
- PROPERTY_POWER_CONFIGURATION_TRIGGER_WAKE
+ PROPERTY_POWER_CONFIGURATION_TRIGGER_WAKE,
)
)
.setPhysicalProperties(setOf(PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_OPEN))
@@ -120,7 +121,22 @@
.setSystemProperties(
setOf(
PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY,
- PROPERTY_FEATURE_REAR_DISPLAY
+ PROPERTY_FEATURE_REAR_DISPLAY,
+ )
+ )
+ .build()
+ )
+ }
+
+val Kosmos.rearDisplayOuterDefaultDeviceState by
+ Kosmos.Fixture {
+ DeviceState(
+ DeviceState.Configuration.Builder(5 /* identifier */, "REAR_DISPLAY")
+ .setSystemProperties(
+ setOf(
+ PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY,
+ PROPERTY_FEATURE_REAR_DISPLAY,
+ PROPERTY_FEATURE_REAR_DISPLAY_OUTER_DEFAULT,
)
)
.build()
diff --git a/core/java/android/text/ClientFlags.java b/packages/SystemUI/tests/utils/src/com/android/systemui/RearDisplayInnerDialogDelegateKosmos.kt
similarity index 60%
copy from core/java/android/text/ClientFlags.java
copy to packages/SystemUI/tests/utils/src/com/android/systemui/RearDisplayInnerDialogDelegateKosmos.kt
index ca88764..6f59855 100644
--- a/core/java/android/text/ClientFlags.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/RearDisplayInnerDialogDelegateKosmos.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,16 +14,11 @@
* limitations under the License.
*/
-package android.text;
+package com.android.systemui
-/**
- * An aconfig feature flags that can be accessible from application process without
- * ContentProvider IPCs.
- *
- * When you add new flags, you have to add flag string to {@link TextFlags#TEXT_ACONFIGS_FLAGS}.
- *
- * TODO(nona): Remove this class.
- * @hide
- */
-public class ClientFlags {
-}
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.reardisplay.RearDisplayInnerDialogDelegate
+import org.mockito.kotlin.mock
+
+val Kosmos.rearDisplayInnerDialogDelegateFactory by
+ Kosmos.Fixture { mock<RearDisplayInnerDialogDelegate.Factory>() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/brightness/ui/viewmodel/BrightnessSliderViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/brightness/ui/viewmodel/BrightnessSliderViewModelKosmos.kt
index 2198e04..e36ad42 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/brightness/ui/viewmodel/BrightnessSliderViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/brightness/ui/viewmodel/BrightnessSliderViewModelKosmos.kt
@@ -21,7 +21,7 @@
import com.android.systemui.haptics.slider.sliderHapticsViewModelFactory
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.settings.brightness.domain.interactor.brightnessMirrorShowingInteractor
-import com.android.systemui.kosmos.brightnessWarningToast
+import com.android.systemui.settings.brightness.ui.brightnessWarningToast
val Kosmos.brightnessSliderViewModelFactory: BrightnessSliderViewModel.Factory by
Kosmos.Fixture {
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/development/data/repository/DevelopmentSettingRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/development/data/repository/DevelopmentSettingRepositoryKosmos.kt
new file mode 100644
index 0000000..3ce1195
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/development/data/repository/DevelopmentSettingRepositoryKosmos.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.development.data.repository
+
+import android.os.userManager
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.util.settings.fakeGlobalSettings
+
+val Kosmos.developmentSettingRepository by
+ Kosmos.Fixture { DevelopmentSettingRepository(fakeGlobalSettings, userManager, testDispatcher) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/development/domain/interactor/BuildNumberInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/development/domain/interactor/BuildNumberInteractorKosmos.kt
new file mode 100644
index 0000000..aa4dd18
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/development/domain/interactor/BuildNumberInteractorKosmos.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.development.domain.interactor
+
+import android.content.clipboardManager
+import android.content.res.mainResources
+import com.android.systemui.development.data.repository.developmentSettingRepository
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.user.data.repository.userRepository
+
+val Kosmos.buildNumberInteractor by
+ Kosmos.Fixture {
+ BuildNumberInteractor(
+ developmentSettingRepository,
+ mainResources,
+ userRepository,
+ { clipboardManager },
+ testDispatcher,
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/development/ui/viewmodel/BuildNumberViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/development/ui/viewmodel/BuildNumberViewModelKosmos.kt
new file mode 100644
index 0000000..c827311
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/development/ui/viewmodel/BuildNumberViewModelKosmos.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.development.ui.viewmodel
+
+import com.android.systemui.development.domain.interactor.buildNumberInteractor
+import com.android.systemui.kosmos.Kosmos
+
+val Kosmos.buildNumberViewModelFactory by
+ Kosmos.Fixture {
+ object : BuildNumberViewModel.Factory {
+ override fun create(): BuildNumberViewModel {
+ return BuildNumberViewModel(buildNumberInteractor)
+ }
+ }
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayWindowPropertiesRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayWindowPropertiesRepository.kt
index 92dc897..7fd9276 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayWindowPropertiesRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayWindowPropertiesRepository.kt
@@ -35,4 +35,9 @@
)
.also { properties.put(displayId, windowType, it) }
}
+
+ /** Sets an instance, just for testing purposes. */
+ fun insert(instance: DisplayWindowProperties) {
+ properties.put(instance.displayId, instance.windowType, instance)
+ }
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/dreams/ui/viewmodel/DreamUserActionsViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/dreams/ui/viewmodel/DreamUserActionsViewModelKosmos.kt
new file mode 100644
index 0000000..b24b3ad
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/dreams/ui/viewmodel/DreamUserActionsViewModelKosmos.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.dreams.ui.viewmodel
+
+import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.shade.domain.interactor.shadeInteractor
+
+val Kosmos.dreamUserActionsViewModel by
+ Kosmos.Fixture {
+ DreamUserActionsViewModel(
+ deviceUnlockedInteractor = deviceUnlockedInteractor,
+ shadeInteractor = shadeInteractor,
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FakeFeatureFlags.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FakeFeatureFlags.kt
index 32469b6..ad38bbe 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FakeFeatureFlags.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FakeFeatureFlags.kt
@@ -23,13 +23,13 @@
class FakeFeatureFlagsClassic : FakeFeatureFlags()
+val FeatureFlagsClassic.fake
+ get() = this as FakeFeatureFlagsClassic
+
@Deprecated(
message = "Use FakeFeatureFlagsClassic instead.",
replaceWith =
- ReplaceWith(
- "FakeFeatureFlagsClassic",
- "com.android.systemui.flags.FakeFeatureFlagsClassic",
- ),
+ ReplaceWith("FakeFeatureFlagsClassic", "com.android.systemui.flags.FakeFeatureFlagsClassic"),
)
open class FakeFeatureFlags : FeatureFlagsClassic {
private val booleanFlags = mutableMapOf<String, Boolean>()
@@ -105,6 +105,7 @@
listener.onFlagChanged(
object : FlagListenable.FlagEvent {
override val flagName = flag.name
+
override fun requestNoRestart() {}
}
)
@@ -165,7 +166,7 @@
@Module(includes = [FakeFeatureFlagsClassicModule.Bindings::class])
class FakeFeatureFlagsClassicModule(
- @get:Provides val fakeFeatureFlagsClassic: FakeFeatureFlagsClassic = FakeFeatureFlagsClassic(),
+ @get:Provides val fakeFeatureFlagsClassic: FakeFeatureFlagsClassic = FakeFeatureFlagsClassic()
) {
constructor(
@@ -175,7 +176,9 @@
@Module
interface Bindings {
@Binds fun bindFake(fake: FakeFeatureFlagsClassic): FeatureFlagsClassic
+
@Binds fun bindClassic(classic: FeatureFlagsClassic): FeatureFlags
+
@Binds fun bindFakeClassic(fake: FakeFeatureFlagsClassic): FakeFeatureFlags
}
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt
index f52f039..9cb15c5 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt
@@ -109,6 +109,7 @@
applicationCoroutineScope,
testDispatcher,
shortcutCategoriesUtils,
+ applicationContext,
)
}
@@ -135,7 +136,11 @@
}
val Kosmos.shortcutHelperCategoriesInteractor by
- Kosmos.Fixture { ShortcutHelperCategoriesInteractor(defaultShortcutCategoriesRepository) }
+ Kosmos.Fixture {
+ ShortcutHelperCategoriesInteractor(defaultShortcutCategoriesRepository) {
+ customShortcutCategoriesRepository
+ }
+ }
val Kosmos.shortcutHelperViewModel by
Kosmos.Fixture {
@@ -161,7 +166,8 @@
}
}
-val Kosmos.shortcutCustomizationInteractor by Kosmos.Fixture { ShortcutCustomizationInteractor() }
+val Kosmos.shortcutCustomizationInteractor by
+ Kosmos.Fixture { ShortcutCustomizationInteractor(customShortcutCategoriesRepository) }
val Kosmos.shortcutCustomizationViewModelFactory by
Kosmos.Fixture {
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorKosmos.kt
index 769612c..255a780 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorKosmos.kt
@@ -30,7 +30,7 @@
val Kosmos.keyguardTouchHandlingInteractor by
Kosmos.Fixture {
KeyguardTouchHandlingInteractor(
- appContext = applicationContext,
+ context = applicationContext,
scope = applicationCoroutineScope,
transitionInteractor = keyguardTransitionInteractor,
repository = keyguardRepository,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardSmartspaceViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardSmartspaceViewModelKosmos.kt
index d33d594..76e2cc8 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardSmartspaceViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardSmartspaceViewModelKosmos.kt
@@ -19,6 +19,7 @@
import com.android.systemui.keyguard.domain.interactor.keyguardSmartspaceInteractor
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.shade.domain.interactor.shadeInteractor
import com.android.systemui.util.mockito.mock
val Kosmos.keyguardSmartspaceViewModel by
@@ -28,5 +29,6 @@
smartspaceController = mock(),
keyguardClockViewModel = keyguardClockViewModel,
smartspaceInteractor = keyguardSmartspaceInteractor,
+ shadeInteractor = shadeInteractor,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/GeneralKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/GeneralKosmos.kt
index f43841b..72cb1df 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/GeneralKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/GeneralKosmos.kt
@@ -3,9 +3,6 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.Kosmos.Fixture
-import com.android.systemui.settings.brightness.ui.BrightnessWarningToast
-
-import com.android.systemui.util.mockito.mock
import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.test.StandardTestDispatcher
@@ -41,9 +38,6 @@
testScope.backgroundScope.coroutineContext
}
var Kosmos.mainCoroutineContext: CoroutineContext by Fixture { testScope.coroutineContext }
-var Kosmos.brightnessWarningToast: BrightnessWarningToast by Kosmos.Fixture {
- mock<BrightnessWarningToast>()
-}
/**
* Run this test body with a [Kosmos] as receiver, and using the [testScope] currently installed in
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt
index 63e6eb6..3d60abf 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt
@@ -71,14 +71,19 @@
import com.android.systemui.shade.ui.viewmodel.notificationShadeWindowModel
import com.android.systemui.statusbar.chips.ui.viewmodel.ongoingActivityChipsViewModel
import com.android.systemui.statusbar.data.repository.fakeStatusBarModePerDisplayRepository
+import com.android.systemui.statusbar.disableflags.data.repository.fakeDisableFlagsRepository
+import com.android.systemui.statusbar.disableflags.domain.interactor.disableFlagsInteractor
+import com.android.systemui.statusbar.notification.collection.provider.visualStabilityProvider
import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor
import com.android.systemui.statusbar.notification.domain.interactor.seenNotificationsInteractor
import com.android.systemui.statusbar.notification.stack.domain.interactor.headsUpNotificationInteractor
import com.android.systemui.statusbar.notification.stack.domain.interactor.sharedNotificationContainerInteractor
+import com.android.systemui.statusbar.phone.keyguardBypassController
import com.android.systemui.statusbar.phone.scrimController
-import com.android.systemui.statusbar.pipeline.mobile.data.repository.fakeMobileConnectionsRepository
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.mobileConnectionsRepository
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.policy.configurationController
import com.android.systemui.statusbar.policy.data.repository.fakeDeviceProvisioningRepository
import com.android.systemui.statusbar.policy.domain.interactor.deviceProvisioningInteractor
import com.android.systemui.statusbar.ui.viewmodel.keyguardStatusBarViewModel
@@ -105,6 +110,7 @@
val testScope by lazy { kosmos.testScope }
val fakeExecutor by lazy { kosmos.fakeExecutor }
val fakeExecutorHandler by lazy { kosmos.fakeExecutorHandler }
+ val configurationController by lazy { kosmos.configurationController }
val configurationRepository by lazy { kosmos.fakeConfigurationRepository }
val configurationInteractor by lazy { kosmos.configurationInteractor }
val bouncerRepository by lazy { kosmos.bouncerRepository }
@@ -115,13 +121,14 @@
val seenNotificationsInteractor by lazy { kosmos.seenNotificationsInteractor }
val keyguardRepository by lazy { kosmos.fakeKeyguardRepository }
val keyguardBouncerRepository by lazy { kosmos.fakeKeyguardBouncerRepository }
+ val keyguardBypassController by lazy { kosmos.keyguardBypassController }
val keyguardInteractor by lazy { kosmos.keyguardInteractor }
val keyguardTransitionRepository by lazy { kosmos.fakeKeyguardTransitionRepository }
val keyguardTransitionInteractor by lazy { kosmos.keyguardTransitionInteractor }
val keyguardStatusBarViewModel by lazy { kosmos.keyguardStatusBarViewModel }
val powerRepository by lazy { kosmos.fakePowerRepository }
val clock by lazy { kosmos.systemClock }
- val mobileConnectionsRepository by lazy { kosmos.fakeMobileConnectionsRepository }
+ val mobileConnectionsRepository by lazy { kosmos.mobileConnectionsRepository }
val simBouncerInteractor by lazy { kosmos.simBouncerInteractor }
val statusBarStateController by lazy { kosmos.statusBarStateController }
val statusBarModePerDisplayRepository by lazy { kosmos.fakeStatusBarModePerDisplayRepository }
@@ -158,6 +165,7 @@
val shadeRepository by lazy { kosmos.shadeRepository }
val shadeInteractor by lazy { kosmos.shadeInteractor }
val notificationShadeWindowModel by lazy { kosmos.notificationShadeWindowModel }
+ val visualStabilityProvider by lazy { kosmos.visualStabilityProvider }
val wifiInteractor by lazy { kosmos.wifiInteractor }
val fakeWifiRepository by lazy { kosmos.fakeWifiRepository }
val volumeDialogInteractor by lazy { kosmos.volumeDialogInteractor }
@@ -177,4 +185,6 @@
val lockscreenToGlanceableHubTransitionViewModel by lazy {
kosmos.lockscreenToGlanceableHubTransitionViewModel
}
+ val disableFlagsInteractor by lazy { kosmos.disableFlagsInteractor }
+ val fakeDisableFlagsRepository by lazy { kosmos.fakeDisableFlagsRepository }
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelKosmos.kt
index 4ed49123..45d5b38 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelKosmos.kt
@@ -33,7 +33,7 @@
import com.android.systemui.qs.ui.viewmodel.quickSettingsContainerViewModelFactory
import com.android.systemui.shade.largeScreenHeaderHelper
import com.android.systemui.shade.transition.largeScreenShadeInterpolator
-import com.android.systemui.statusbar.disableflags.data.repository.disableFlagsRepository
+import com.android.systemui.statusbar.disableflags.domain.interactor.disableFlagsInteractor
import com.android.systemui.statusbar.sysuiStatusBarStateController
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -51,7 +51,7 @@
footerActionsController,
sysuiStatusBarStateController,
deviceEntryInteractor,
- disableFlagsRepository,
+ disableFlagsInteractor,
keyguardTransitionInteractor,
largeScreenShadeInterpolator,
configurationInteractor,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/PaginatedGridViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/PaginatedGridViewModelKosmos.kt
index 0e5edb7..2e80293 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/PaginatedGridViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/PaginatedGridViewModelKosmos.kt
@@ -16,6 +16,7 @@
package com.android.systemui.qs.panels.ui.viewmodel
+import com.android.systemui.development.ui.viewmodel.buildNumberViewModelFactory
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.qs.panels.domain.interactor.paginatedGridInteractor
@@ -26,5 +27,6 @@
qsColumnsViewModelFactory,
paginatedGridInteractor,
inFirstPageViewModel,
+ buildNumberViewModelFactory,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/resolver/SceneResolverKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/resolver/SceneResolverKosmos.kt
index 8b12425..a4a63ec 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/resolver/SceneResolverKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/resolver/SceneResolverKosmos.kt
@@ -21,6 +21,7 @@
import com.android.compose.animation.scene.SceneKey
import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
import com.android.systemui.keyguard.domain.interactor.keyguardEnabledInteractor
+import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.scene.shared.model.SceneFamilies
@@ -34,6 +35,7 @@
HomeSceneFamilyResolver(
applicationScope = applicationCoroutineScope,
deviceEntryInteractor = deviceEntryInteractor,
+ keyguardInteractor = keyguardInteractor,
keyguardEnabledInteractor = keyguardEnabledInteractor,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/settings/BrightnessSliderControllerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/settings/BrightnessSliderControllerKosmos.kt
index aac122c..5d146fb 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/settings/BrightnessSliderControllerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/settings/BrightnessSliderControllerKosmos.kt
@@ -21,9 +21,9 @@
import com.android.systemui.haptics.msdl.msdlPlayer
import com.android.systemui.haptics.vibratorHelper
import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.kosmos.brightnessWarningToast
import com.android.systemui.plugins.activityStarter
import com.android.systemui.settings.brightness.BrightnessSliderController
+import com.android.systemui.settings.brightness.ui.brightnessWarningToast
import com.android.systemui.util.time.systemClock
/** This factory creates empty mocks. */
diff --git a/core/java/android/text/ClientFlags.java b/packages/SystemUI/tests/utils/src/com/android/systemui/settings/brightness/ui/BrightnessWarningToastKosmos.kt
similarity index 60%
copy from core/java/android/text/ClientFlags.java
copy to packages/SystemUI/tests/utils/src/com/android/systemui/settings/brightness/ui/BrightnessWarningToastKosmos.kt
index ca88764..d9acb52 100644
--- a/core/java/android/text/ClientFlags.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/settings/brightness/ui/BrightnessWarningToastKosmos.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,16 +14,10 @@
* limitations under the License.
*/
-package android.text;
+package com.android.systemui.settings.brightness.ui
-/**
- * An aconfig feature flags that can be accessible from application process without
- * ContentProvider IPCs.
- *
- * When you add new flags, you have to add flag string to {@link TextFlags#TEXT_ACONFIGS_FLAGS}.
- *
- * TODO(nona): Remove this class.
- * @hide
- */
-public class ClientFlags {
-}
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.util.mockito.mock
+
+var Kosmos.brightnessWarningToast: BrightnessWarningToast by
+ Kosmos.Fixture { mock<BrightnessWarningToast>() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeInteractorKosmos.kt
index 39f58ae..af6d624 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeInteractorKosmos.kt
@@ -25,7 +25,7 @@
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.shade.ShadeModule
import com.android.systemui.shade.data.repository.shadeRepository
-import com.android.systemui.statusbar.disableflags.data.repository.disableFlagsRepository
+import com.android.systemui.statusbar.disableflags.domain.interactor.disableFlagsInteractor
import com.android.systemui.statusbar.phone.dozeParameters
import com.android.systemui.statusbar.policy.data.repository.userSetupRepository
import com.android.systemui.statusbar.policy.domain.interactor.deviceProvisioningInteractor
@@ -60,7 +60,7 @@
ShadeInteractorImpl(
scope = applicationCoroutineScope,
deviceProvisioningInteractor = deviceProvisioningInteractor,
- disableFlagsRepository = disableFlagsRepository,
+ disableFlagsInteractor = disableFlagsInteractor,
dozeParams = dozeParameters,
keyguardRepository = fakeKeyguardRepository,
keyguardTransitionInteractor = keyguardTransitionInteractor,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/disableflags/data/repository/FakeDisableFlagsRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/disableflags/data/repository/FakeDisableFlagsRepository.kt
index 466a3eb..9dbb547 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/disableflags/data/repository/FakeDisableFlagsRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/disableflags/data/repository/FakeDisableFlagsRepository.kt
@@ -15,7 +15,7 @@
package com.android.systemui.statusbar.disableflags.data.repository
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.statusbar.disableflags.data.model.DisableFlagsModel
+import com.android.systemui.statusbar.disableflags.shared.model.DisableFlagsModel
import dagger.Binds
import dagger.Module
import javax.inject.Inject
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/disableflags/domain/interactor/DisableFlagsInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/disableflags/domain/interactor/DisableFlagsInteractorKosmos.kt
new file mode 100644
index 0000000..7b4b047
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/disableflags/domain/interactor/DisableFlagsInteractorKosmos.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.disableflags.domain.interactor
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.statusbar.disableflags.data.repository.disableFlagsRepository
+
+val Kosmos.disableFlagsInteractor by Fixture {
+ DisableFlagsInteractor(repository = disableFlagsRepository)
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/airplane/domain/interactor/AirplaneModeInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/airplane/domain/interactor/AirplaneModeInteractorKosmos.kt
index d76defe..99ed4f0 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/airplane/domain/interactor/AirplaneModeInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/airplane/domain/interactor/AirplaneModeInteractorKosmos.kt
@@ -18,7 +18,7 @@
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
-import com.android.systemui.statusbar.pipeline.mobile.data.repository.fakeMobileConnectionsRepository
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.mobileConnectionsRepository
import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
val Kosmos.airplaneModeInteractor: AirplaneModeInteractor by
@@ -26,6 +26,6 @@
AirplaneModeInteractor(
FakeAirplaneModeRepository(),
FakeConnectivityRepository(),
- fakeMobileConnectionsRepository,
+ mobileConnectionsRepository,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionsRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionsRepository.kt
index de73d33..bfd46b6 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionsRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionsRepository.kt
@@ -77,11 +77,7 @@
override fun getRepoForSubId(subId: Int): MobileConnectionRepository {
return subIdRepos[subId]
- ?: FakeMobileConnectionRepository(
- subId,
- tableLogBuffer,
- )
- .also { subIdRepos[subId] = it }
+ ?: FakeMobileConnectionRepository(subId, tableLogBuffer).also { subIdRepos[subId] = it }
}
override val defaultDataSubRatConfig = MutableStateFlow(MobileMappings.Config())
@@ -135,3 +131,6 @@
const val LTE_ADVANCED_PRO = TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_ADVANCED_PRO
}
}
+
+val MobileConnectionsRepository.fake
+ get() = this as FakeMobileConnectionsRepository
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionsRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionsRepositoryKosmos.kt
index cd22f1d..b952d71 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionsRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionsRepositoryKosmos.kt
@@ -19,12 +19,20 @@
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
import com.android.systemui.log.table.logcatTableLogBuffer
+import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
+import com.android.systemui.statusbar.pipeline.mobile.util.MobileMappingsProxy
+
+val Kosmos.mobileMappingsProxy: MobileMappingsProxy by Fixture { FakeMobileMappingsProxy() }
+
+var Kosmos.mobileConnectionsRepositoryLogbufferName by Fixture { "FakeMobileConnectionsRepository" }
val Kosmos.fakeMobileConnectionsRepository by Fixture {
FakeMobileConnectionsRepository(
- tableLogBuffer = logcatTableLogBuffer(this, "FakeMobileConnectionsRepository"),
+ mobileMappings = mobileMappingsProxy,
+ tableLogBuffer = logcatTableLogBuffer(this, mobileConnectionsRepositoryLogbufferName),
)
}
-val Kosmos.mobileConnectionsRepository by
- Fixture<MobileConnectionsRepository> { fakeMobileConnectionsRepository }
+val Kosmos.mobileConnectionsRepository: MobileConnectionsRepository by Fixture {
+ fakeMobileConnectionsRepository
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/data/repository/ConnectivityRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/data/repository/ConnectivityRepositoryKosmos.kt
index 8e656cf..00bfa99 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/data/repository/ConnectivityRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/data/repository/ConnectivityRepositoryKosmos.kt
@@ -18,7 +18,5 @@
import com.android.systemui.kosmos.Kosmos
-val Kosmos.fakeConnectivityRepository: FakeConnectivityRepository by
- Kosmos.Fixture { FakeConnectivityRepository() }
val Kosmos.connectivityRepository: ConnectivityRepository by
- Kosmos.Fixture { fakeConnectivityRepository }
+ Kosmos.Fixture { FakeConnectivityRepository() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/data/repository/FakeConnectivityRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/data/repository/FakeConnectivityRepository.kt
index 331e2fa..c69d9a2 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/data/repository/FakeConnectivityRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/data/repository/FakeConnectivityRepository.kt
@@ -42,10 +42,7 @@
* validated
*/
@JvmOverloads
- fun setMobileConnected(
- default: Boolean = true,
- validated: Boolean = true,
- ) {
+ fun setMobileConnected(default: Boolean = true, validated: Boolean = true) {
defaultConnections.value =
DefaultConnectionModel(
mobile = DefaultConnectionModel.Mobile(default),
@@ -55,10 +52,7 @@
/** Similar convenience method for ethernet */
@JvmOverloads
- fun setEthernetConnected(
- default: Boolean = true,
- validated: Boolean = true,
- ) {
+ fun setEthernetConnected(default: Boolean = true, validated: Boolean = true) {
defaultConnections.value =
DefaultConnectionModel(
ethernet = DefaultConnectionModel.Ethernet(default),
@@ -67,10 +61,7 @@
}
@JvmOverloads
- fun setWifiConnected(
- default: Boolean = true,
- validated: Boolean = true,
- ) {
+ fun setWifiConnected(default: Boolean = true, validated: Boolean = true) {
defaultConnections.value =
DefaultConnectionModel(
wifi = DefaultConnectionModel.Wifi(default),
@@ -78,3 +69,6 @@
)
}
}
+
+val ConnectivityRepository.fake
+ get() = this as FakeConnectivityRepository
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/domain/interactor/CollapsedStatusBarInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/domain/interactor/CollapsedStatusBarInteractorKosmos.kt
index 385a813..13fde96 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/domain/interactor/CollapsedStatusBarInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/domain/interactor/CollapsedStatusBarInteractorKosmos.kt
@@ -17,7 +17,7 @@
package com.android.systemui.statusbar.pipeline.shared.domain.interactor
import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.statusbar.disableflags.data.repository.fakeDisableFlagsRepository
+import com.android.systemui.statusbar.disableflags.domain.interactor.disableFlagsInteractor
val Kosmos.collapsedStatusBarInteractor: CollapsedStatusBarInteractor by
- Kosmos.Fixture { CollapsedStatusBarInteractor(fakeDisableFlagsRepository) }
+ Kosmos.Fixture { CollapsedStatusBarInteractor(disableFlagsInteractor) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/FakeStatusBarWindowController.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/FakeStatusBarWindowController.kt
index 528c9d9..a110a49 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/FakeStatusBarWindowController.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/FakeStatusBarWindowController.kt
@@ -27,6 +27,9 @@
var isAttached = false
private set
+ var isStopped = false
+ private set
+
override val statusBarHeight: Int = 0
override fun refreshStatusBarHeight() {}
@@ -35,6 +38,10 @@
isAttached = true
}
+ override fun stop() {
+ isStopped = true
+ }
+
override fun addViewToWindow(view: View, layoutParams: ViewGroup.LayoutParams) {}
override val backgroundView: View
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/StatusBarWindowControllerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/StatusBarWindowControllerKosmos.kt
index 173e909..23f2b42 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/StatusBarWindowControllerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/StatusBarWindowControllerKosmos.kt
@@ -18,7 +18,8 @@
import android.content.testableContext
import android.view.windowManagerService
-import com.android.app.viewcapture.viewCaptureAwareWindowManager
+import com.android.app.viewcapture.realCaptureAwareWindowManager
+import com.android.systemui.concurrency.fakeExecutor
import com.android.systemui.fragments.fragmentService
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.statusbar.phone.statusBarContentInsetsProvider
@@ -32,12 +33,13 @@
StatusBarWindowControllerImpl(
testableContext,
statusBarWindowViewInflater,
- viewCaptureAwareWindowManager,
+ realCaptureAwareWindowManager,
statusBarConfigurationController,
windowManagerService,
statusBarContentInsetsProvider,
fragmentService,
Optional.empty(),
+ fakeExecutor,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/StatusBarWindowControllerStoreKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/StatusBarWindowControllerStoreKosmos.kt
new file mode 100644
index 0000000..4941ceb
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/StatusBarWindowControllerStoreKosmos.kt
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.window
+
+import android.view.WindowManager
+import com.android.app.viewcapture.ViewCaptureAwareWindowManager
+import com.android.app.viewcapture.realCaptureAwareWindowManager
+import com.android.systemui.display.data.repository.displayRepository
+import com.android.systemui.display.data.repository.displayWindowPropertiesRepository
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.statusbar.data.repository.statusBarConfigurationControllerStore
+import com.android.systemui.statusbar.data.repository.statusBarContentInsetsProviderStore
+import org.mockito.kotlin.mock
+
+val Kosmos.multiDisplayStatusBarWindowControllerStore by
+ Kosmos.Fixture {
+ MultiDisplayStatusBarWindowControllerStore(
+ backgroundApplicationScope = applicationCoroutineScope,
+ controllerFactory = { _, _, _, _ -> mock() },
+ displayWindowPropertiesRepository = displayWindowPropertiesRepository,
+ viewCaptureAwareWindowManagerFactory =
+ object : ViewCaptureAwareWindowManager.Factory {
+ override fun create(
+ windowManager: WindowManager
+ ): ViewCaptureAwareWindowManager {
+ return realCaptureAwareWindowManager
+ }
+ },
+ statusBarConfigurationControllerStore = statusBarConfigurationControllerStore,
+ statusBarContentInsetsProviderStore = statusBarContentInsetsProviderStore,
+ displayRepository = displayRepository,
+ )
+ }
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
index 65f4122..21a910b 100644
--- 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
@@ -16,6 +16,8 @@
package com.android.systemui.util.settings;
+import static com.android.systemui.util.settings.JavaAdapter.newCoroutineScope;
+
import static kotlinx.coroutines.test.TestCoroutineDispatchersKt.StandardTestDispatcher;
import android.annotation.NonNull;
@@ -25,6 +27,7 @@
import android.net.Uri;
import kotlinx.coroutines.CoroutineDispatcher;
+import kotlinx.coroutines.CoroutineScope;
import java.util.ArrayList;
import java.util.HashMap;
@@ -34,7 +37,7 @@
public class FakeGlobalSettings implements GlobalSettings {
private final Map<String, String> mValues = new HashMap<>();
private final Map<String, List<ContentObserver>> mContentObserversAllUsers = new HashMap<>();
- private final CoroutineDispatcher mDispatcher;
+ private final CoroutineScope mSettingsScope;
public static final Uri CONTENT_URI = Uri.parse("content://settings/fake_global");
@@ -44,11 +47,15 @@
*/
@Deprecated
public FakeGlobalSettings() {
- mDispatcher = StandardTestDispatcher(/* scheduler = */ null, /* name = */ null);
+ CoroutineDispatcher dispatcher = StandardTestDispatcher(
+ /* scheduler = */ null,
+ /* name = */ null
+ );
+ mSettingsScope = newCoroutineScope(dispatcher);
}
public FakeGlobalSettings(CoroutineDispatcher dispatcher) {
- mDispatcher = dispatcher;
+ mSettingsScope = newCoroutineScope(dispatcher);
}
@NonNull
@@ -61,8 +68,8 @@
@NonNull
@Override
- public CoroutineDispatcher getBackgroundDispatcher() {
- return mDispatcher;
+ public CoroutineScope getSettingsScope() {
+ return mSettingsScope;
}
@Override
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeGlobalSettingsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeGlobalSettingsKosmos.kt
index 35fa2af..78b78ca 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeGlobalSettingsKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeGlobalSettingsKosmos.kt
@@ -19,5 +19,14 @@
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
import com.android.systemui.kosmos.testDispatcher
+import kotlin.coroutines.CoroutineContext
+import kotlinx.coroutines.CoroutineScope
val Kosmos.fakeGlobalSettings: FakeGlobalSettings by Fixture { FakeGlobalSettings(testDispatcher) }
+
+object JavaAdapter {
+ @JvmStatic
+ fun newCoroutineScope(context: CoroutineContext): CoroutineScope {
+ return CoroutineScope(context)
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeSettings.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeSettings.kt
index e5d113b..a357275 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeSettings.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeSettings.kt
@@ -24,6 +24,7 @@
import androidx.annotation.VisibleForTesting
import com.android.systemui.util.settings.SettingsProxy.CurrentUserIdProvider
import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestDispatcher
@@ -33,7 +34,7 @@
private val contentObservers = mutableMapOf<SettingsKey, MutableList<ContentObserver>>()
private val contentObserversAllUsers = mutableMapOf<String, MutableList<ContentObserver>>()
- override val backgroundDispatcher: CoroutineDispatcher
+ override val settingsScope: CoroutineScope
@UserIdInt override var userId = UserHandle.USER_CURRENT
override val currentUserProvider: CurrentUserIdProvider
@@ -43,17 +44,17 @@
by main test scope."""
)
constructor() {
- backgroundDispatcher = StandardTestDispatcher(scheduler = null, name = null)
+ settingsScope = CoroutineScope(StandardTestDispatcher(scheduler = null, name = null))
currentUserProvider = CurrentUserIdProvider { userId }
}
constructor(dispatcher: CoroutineDispatcher) {
- backgroundDispatcher = dispatcher
+ settingsScope = CoroutineScope(dispatcher)
currentUserProvider = CurrentUserIdProvider { userId }
}
constructor(dispatcher: CoroutineDispatcher, currentUserProvider: CurrentUserIdProvider) {
- backgroundDispatcher = dispatcher
+ settingsScope = CoroutineScope(dispatcher)
this.currentUserProvider = currentUserProvider
}
@@ -77,7 +78,7 @@
uri: Uri,
notifyForDescendants: Boolean,
settingsObserver: ContentObserver,
- userHandle: Int
+ userHandle: Int,
) {
if (userHandle == UserHandle.USER_ALL) {
contentObserversAllUsers
@@ -107,31 +108,31 @@
override suspend fun registerContentObserver(
uri: Uri,
notifyForDescendants: Boolean,
- settingsObserver: ContentObserver
+ settingsObserver: ContentObserver,
) = suspendAdvanceDispatcher {
super<UserSettingsProxy>.registerContentObserver(
uri,
notifyForDescendants,
- settingsObserver
+ settingsObserver,
)
}
override fun registerContentObserverAsync(
uri: Uri,
notifyForDescendants: Boolean,
- settingsObserver: ContentObserver
+ settingsObserver: ContentObserver,
): Job = advanceDispatcher {
super<UserSettingsProxy>.registerContentObserverAsync(
uri,
notifyForDescendants,
- settingsObserver
+ settingsObserver,
)
}
override suspend fun registerContentObserverForUser(
name: String,
settingsObserver: ContentObserver,
- userHandle: Int
+ userHandle: Int,
) = suspendAdvanceDispatcher {
super<UserSettingsProxy>.registerContentObserverForUser(name, settingsObserver, userHandle)
}
@@ -139,12 +140,12 @@
override fun registerContentObserverForUserAsync(
name: String,
settingsObserver: ContentObserver,
- userHandle: Int
+ userHandle: Int,
): Job = advanceDispatcher {
super<UserSettingsProxy>.registerContentObserverForUserAsync(
name,
settingsObserver,
- userHandle
+ userHandle,
)
}
@@ -156,7 +157,7 @@
override suspend fun registerContentObserverForUser(
uri: Uri,
settingsObserver: ContentObserver,
- userHandle: Int
+ userHandle: Int,
) = suspendAdvanceDispatcher {
super<UserSettingsProxy>.registerContentObserverForUser(uri, settingsObserver, userHandle)
}
@@ -164,12 +165,12 @@
override fun registerContentObserverForUserAsync(
uri: Uri,
settingsObserver: ContentObserver,
- userHandle: Int
+ userHandle: Int,
): Job = advanceDispatcher {
super<UserSettingsProxy>.registerContentObserverForUserAsync(
uri,
settingsObserver,
- userHandle
+ userHandle,
)
}
@@ -177,13 +178,13 @@
uri: Uri,
settingsObserver: ContentObserver,
userHandle: Int,
- registered: Runnable
+ registered: Runnable,
): Job = advanceDispatcher {
super<UserSettingsProxy>.registerContentObserverForUserAsync(
uri,
settingsObserver,
userHandle,
- registered
+ registered,
)
}
@@ -191,13 +192,13 @@
name: String,
notifyForDescendants: Boolean,
settingsObserver: ContentObserver,
- userHandle: Int
+ userHandle: Int,
) = suspendAdvanceDispatcher {
super<UserSettingsProxy>.registerContentObserverForUser(
name,
notifyForDescendants,
settingsObserver,
- userHandle
+ userHandle,
)
}
@@ -205,13 +206,13 @@
name: String,
notifyForDescendants: Boolean,
settingsObserver: ContentObserver,
- userHandle: Int
+ userHandle: Int,
) = advanceDispatcher {
super<UserSettingsProxy>.registerContentObserverForUserAsync(
name,
notifyForDescendants,
settingsObserver,
- userHandle
+ userHandle,
)
}
@@ -219,13 +220,13 @@
uri: Uri,
notifyForDescendants: Boolean,
settingsObserver: ContentObserver,
- userHandle: Int
+ userHandle: Int,
): Job = advanceDispatcher {
super<UserSettingsProxy>.registerContentObserverForUserAsync(
uri,
notifyForDescendants,
settingsObserver,
- userHandle
+ userHandle,
)
}
@@ -259,7 +260,7 @@
tag: String?,
makeDefault: Boolean,
userHandle: Int,
- overrideableByRestore: Boolean
+ overrideableByRestore: Boolean,
): Boolean {
val key = SettingsKey(userHandle, getUriFor(name).toString())
values[key] = value
@@ -275,7 +276,7 @@
name: String,
value: String?,
tag: String?,
- makeDefault: Boolean
+ makeDefault: Boolean,
): Boolean {
return putString(name, value)
}
@@ -293,8 +294,9 @@
return result
}
+ @OptIn(ExperimentalStdlibApi::class)
private fun testDispatcherRunCurrent() {
- val testDispatcher = backgroundDispatcher as? TestDispatcher
+ val testDispatcher = settingsScope.coroutineContext[CoroutineDispatcher] as? TestDispatcher
testDispatcher?.scheduler?.runCurrent()
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/domain/interactor/FakeAudioSharingInteractor.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/domain/interactor/FakeAudioSharingInteractor.kt
new file mode 100644
index 0000000..1fb5e77
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/domain/interactor/FakeAudioSharingInteractor.kt
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.domain.interactor
+
+import android.content.Context
+import androidx.annotation.IntRange
+import com.android.dream.lowlight.dagger.qualifiers.Application
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+
+class FakeAudioSharingInteractor : AudioSharingInteractor {
+ private val mutableInAudioSharing: MutableStateFlow<Boolean> = MutableStateFlow(false)
+ private val mutableVolume: MutableStateFlow<Int?> = MutableStateFlow(null)
+ private var audioSharingVolumeBarAvailable = false
+
+ override val isInAudioSharing: Flow<Boolean> = mutableInAudioSharing
+ override val volume: Flow<Int?> = mutableVolume
+ override val volumeMin: Int = AUDIO_SHARING_VOLUME_MIN
+ override val volumeMax: Int = AUDIO_SHARING_VOLUME_MAX
+
+ override suspend fun audioSharingVolumeBarAvailable(@Application context: Context): Boolean =
+ audioSharingVolumeBarAvailable
+
+ override fun setStreamVolume(
+ @IntRange(from = AUDIO_SHARING_VOLUME_MIN.toLong(), to = AUDIO_SHARING_VOLUME_MAX.toLong())
+ level: Int
+ ) {}
+
+ override fun handlePrimaryGroupChange() {}
+
+ fun setInAudioSharing(state: Boolean) {
+ mutableInAudioSharing.value = state
+ }
+
+ fun setVolume(volume: Int?) {
+ mutableVolume.value = volume
+ }
+
+ fun setAudioSharingVolumeBarAvailable(available: Boolean) {
+ audioSharingVolumeBarAvailable = available
+ }
+
+ companion object {
+ const val AUDIO_SHARING_VOLUME_MIN = 0
+ const val AUDIO_SHARING_VOLUME_MAX = 255
+ }
+}
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java
index 869d854..9b71f80 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java
@@ -30,6 +30,8 @@
import androidx.test.platform.app.InstrumentationRegistry;
+import com.android.ravenwood.common.RavenwoodCommonUtils;
+
import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runner.Runner;
@@ -229,7 +231,9 @@
s.evaluate();
onAfter(description, scope, order, null);
} catch (Throwable t) {
- if (onAfter(description, scope, order, t)) {
+ var shouldReportFailure = RavenwoodCommonUtils.runIgnoringException(
+ () -> onAfter(description, scope, order, t));
+ if (shouldReportFailure == null || shouldReportFailure) {
throw t;
}
}
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java
index 678a97b..1c1f157 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java
@@ -22,6 +22,8 @@
import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_RESOURCE_APK;
import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_VERBOSE_LOGGING;
import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_VERSION_JAVA_SYSPROP;
+import static com.android.ravenwood.common.RavenwoodCommonUtils.parseNullableInt;
+import static com.android.ravenwood.common.RavenwoodCommonUtils.withDefault;
import static org.junit.Assert.assertThrows;
import static org.mockito.ArgumentMatchers.any;
@@ -39,6 +41,7 @@
import android.content.res.Resources;
import android.os.Binder;
import android.os.Build;
+import android.os.Build.VERSION_CODES;
import android.os.Bundle;
import android.os.HandlerThread;
import android.os.Looper;
@@ -154,6 +157,13 @@
private static RavenwoodAwareTestRunner sRunner;
private static RavenwoodSystemProperties sProps;
+ private static final int DEFAULT_TARGET_SDK_LEVEL = VERSION_CODES.CUR_DEVELOPMENT;
+ private static final String DEFAULT_PACKAGE_NAME = "com.android.ravenwoodtests.defaultname";
+
+ private static int sTargetSdkLevel;
+ private static String sTestPackageName;
+ private static String sTargetPackageName;
+
/**
* Initialize the global environment.
*/
@@ -235,9 +245,22 @@
System.setProperty("android.junit.runner",
"androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner");
+ loadRavenwoodProperties();
+
assertMockitoVersion();
}
+ private static void loadRavenwoodProperties() {
+ var props = RavenwoodSystemProperties.readProperties("ravenwood.properties");
+
+ sTargetSdkLevel = withDefault(
+ parseNullableInt(props.get("targetSdkVersionInt")), DEFAULT_TARGET_SDK_LEVEL);
+ sTargetPackageName = withDefault(props.get("packageName"), DEFAULT_PACKAGE_NAME);
+ sTestPackageName = withDefault(props.get("instPackageName"), sTargetPackageName);
+
+ // TODO(b/377765941) Read them from the manifest too?
+ }
+
/**
* Initialize the environment.
*/
@@ -256,7 +279,9 @@
initInner(runner.mState.getConfig());
} catch (Exception th) {
Log.e(TAG, "init() failed", th);
- reset();
+
+ RavenwoodCommonUtils.runIgnoringException(()-> reset());
+
SneakyThrow.sneakyThrow(th);
}
}
@@ -267,6 +292,14 @@
Thread.setDefaultUncaughtExceptionHandler(sUncaughtExceptionHandler);
}
+ config.mTargetPackageName = sTargetPackageName;
+ config.mTestPackageName = sTestPackageName;
+ config.mTargetSdkLevel = sTargetSdkLevel;
+
+ Log.i(TAG, "TargetPackageName=" + sTargetPackageName);
+ Log.i(TAG, "TestPackageName=" + sTestPackageName);
+ Log.i(TAG, "TargetSdkLevel=" + sTargetSdkLevel);
+
RavenwoodRuntimeState.sUid = config.mUid;
RavenwoodRuntimeState.sPid = config.mPid;
RavenwoodRuntimeState.sTargetSdkLevel = config.mTargetSdkLevel;
@@ -349,8 +382,11 @@
* Partially re-initialize after each test method invocation
*/
public static void reinit() {
- var config = sRunner.mState.getConfig();
- Binder.restoreCallingIdentity(packBinderIdentityToken(false, config.mUid, config.mPid));
+ // sRunner could be null, if there was a failure in the initialization.
+ if (sRunner != null) {
+ var config = sRunner.mState.getConfig();
+ Binder.restoreCallingIdentity(packBinderIdentityToken(false, config.mUid, config.mPid));
+ }
}
private static void initializeCompatIds(RavenwoodConfig config) {
@@ -380,6 +416,9 @@
/**
* De-initialize.
+ *
+ * Note, we call this method when init() fails too, so this method should deal with
+ * any partially-initialized states.
*/
public static void reset() {
if (RAVENWOOD_VERBOSE_LOGGING) {
@@ -411,7 +450,9 @@
config.mState.mSystemServerContext.cleanUp();
}
- Looper.getMainLooper().quit();
+ if (Looper.getMainLooper() != null) {
+ Looper.getMainLooper().quit();
+ }
Looper.clearMainLooperForTest();
ActivityManager.reset$ravenwood();
diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodConfig.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodConfig.java
index 3ed8b0a..7ca9239 100644
--- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodConfig.java
+++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodConfig.java
@@ -22,7 +22,6 @@
import android.annotation.Nullable;
import android.app.Instrumentation;
import android.content.Context;
-import android.os.Build;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
@@ -30,16 +29,12 @@
import java.lang.annotation.Target;
import java.util.ArrayList;
import java.util.List;
-import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;
/**
- * Represents how to configure the ravenwood environment for a test class.
- *
- * If a ravenwood test class has a public static field with the {@link Config} annotation,
- * Ravenwood will extract the config from it and initializes the environment. The type of the
- * field must be of {@link RavenwoodConfig}.
+ * @deprecated This class will be removed. Reach out to g/ravenwood if you need any features in it.
*/
+@Deprecated
public final class RavenwoodConfig {
/**
* Use this to mark a field as the configuration.
@@ -66,7 +61,7 @@
String mTestPackageName;
String mTargetPackageName;
- int mTargetSdkLevel = Build.VERSION_CODES.CUR_DEVELOPMENT;
+ int mTargetSdkLevel;
final RavenwoodSystemProperties mSystemProperties = new RavenwoodSystemProperties();
@@ -91,12 +86,6 @@
return RavenwoodRule.isOnRavenwood();
}
- private void setDefaults() {
- if (mTargetPackageName == null) {
- mTargetPackageName = mTestPackageName;
- }
- }
-
public static class Builder {
private final RavenwoodConfig mConfig = new RavenwoodConfig();
@@ -120,28 +109,27 @@
}
/**
- * Configure the package name of the test, which corresponds to
- * {@link Instrumentation#getContext()}.
+ * @deprecated no longer used. Package name is set in the build file. (for now)
*/
+ @Deprecated
public Builder setPackageName(@NonNull String packageName) {
- mConfig.mTestPackageName = Objects.requireNonNull(packageName);
return this;
}
/**
- * Configure the package name of the target app, which corresponds to
- * {@link Instrumentation#getTargetContext()}. Defaults to {@link #setPackageName}.
+ * @deprecated no longer used. Package name is set in the build file. (for now)
*/
+ @Deprecated
public Builder setTargetPackageName(@NonNull String packageName) {
- mConfig.mTargetPackageName = Objects.requireNonNull(packageName);
return this;
}
+
/**
- * Configure the target SDK level of the test.
+ * @deprecated no longer used. Target SDK level is set in the build file. (for now)
*/
+ @Deprecated
public Builder setTargetSdkLevel(int sdkLevel) {
- mConfig.mTargetSdkLevel = sdkLevel;
return this;
}
@@ -154,33 +142,31 @@
}
/**
- * Configure the given system property as immutable for the duration of the test.
- * Read access to the key is allowed, and write access will fail. When {@code value} is
- * {@code null}, the value is left as undefined.
- *
- * All properties in the {@code debug.*} namespace are automatically mutable, with no
- * developer action required.
- *
- * Has no effect on non-Ravenwood environments.
+ * @deprecated Use {@link RavenwoodRule.Builder#setSystemPropertyImmutable(String, Object)}
*/
+ @Deprecated
public Builder setSystemPropertyImmutable(@NonNull String key,
@Nullable Object value) {
+ return this;
+ }
+
+ /**
+ * @deprecated Use {@link RavenwoodRule.Builder#setSystemPropertyMutable(String, Object)}
+ */
+ @Deprecated
+ public Builder setSystemPropertyMutable(@NonNull String key,
+ @Nullable Object value) {
+ return this;
+ }
+
+ Builder setSystemPropertyImmutableReal(@NonNull String key,
+ @Nullable Object value) {
mConfig.mSystemProperties.setValue(key, value);
mConfig.mSystemProperties.setAccessReadOnly(key);
return this;
}
- /**
- * Configure the given system property as mutable for the duration of the test.
- * Both read and write access to the key is allowed, and its value will be reset between
- * each test. When {@code value} is {@code null}, the value is left as undefined.
- *
- * All properties in the {@code debug.*} namespace are automatically mutable, with no
- * developer action required.
- *
- * Has no effect on non-Ravenwood environments.
- */
- public Builder setSystemPropertyMutable(@NonNull String key,
+ Builder setSystemPropertyMutableReal(@NonNull String key,
@Nullable Object value) {
mConfig.mSystemProperties.setValue(key, value);
mConfig.mSystemProperties.setAccessReadWrite(key);
@@ -205,7 +191,6 @@
}
public RavenwoodConfig build() {
- mConfig.setDefaults();
return mConfig;
}
}
diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
index bfa3802..5681a90 100644
--- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
+++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
@@ -36,10 +36,8 @@
import java.util.regex.Pattern;
/**
- * @deprecated Use {@link RavenwoodConfig} to configure the ravenwood environment instead.
- * A {@link RavenwoodRule} is no longer needed for {@link DisabledOnRavenwood}. To get the
- * {@link Context} and {@link Instrumentation}, use
- * {@link androidx.test.platform.app.InstrumentationRegistry} instead.
+ * @deprecated This class is undergoing a major change. Reach out to g/ravenwood if you need
+ * any featues in it.
*/
@Deprecated
public final class RavenwoodRule implements TestRule {
@@ -128,11 +126,10 @@
}
/**
- * Configure the identity of this process to be the given package name for the duration
- * of the test. Has no effect on non-Ravenwood environments.
+ * @deprecated no longer used.
*/
+ @Deprecated
public Builder setPackageName(@NonNull String packageName) {
- mBuilder.setPackageName(packageName);
return this;
}
@@ -155,7 +152,7 @@
* Has no effect on non-Ravenwood environments.
*/
public Builder setSystemPropertyImmutable(@NonNull String key, @Nullable Object value) {
- mBuilder.setSystemPropertyImmutable(key, value);
+ mBuilder.setSystemPropertyImmutableReal(key, value);
return this;
}
@@ -170,7 +167,7 @@
* Has no effect on non-Ravenwood environments.
*/
public Builder setSystemPropertyMutable(@NonNull String key, @Nullable Object value) {
- mBuilder.setSystemPropertyMutable(key, value);
+ mBuilder.setSystemPropertyMutableReal(key, value);
return this;
}
diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodSystemProperties.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodSystemProperties.java
index 3e4619f..9bd376a 100644
--- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodSystemProperties.java
+++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodSystemProperties.java
@@ -52,7 +52,7 @@
"vendor_dlkm",
};
- private static Map<String, String> readProperties(String propFile) {
+ static Map<String, String> readProperties(String propFile) {
// Use an ordered map just for cleaner dump log.
final Map<String, String> ret = new LinkedHashMap<>();
try {
@@ -60,7 +60,7 @@
.map(String::trim)
.filter(s -> !s.startsWith("#"))
.map(s -> s.split("\\s*=\\s*", 2))
- .filter(a -> a.length == 2)
+ .filter(a -> a.length == 2 && a[1].length() > 0)
.forEach(a -> ret.put(a[0], a[1]));
} catch (IOException e) {
throw new RuntimeException(e);
diff --git a/ravenwood/runtime-common-src/com/android/ravenwood/common/RavenwoodCommonUtils.java b/ravenwood/runtime-common-src/com/android/ravenwood/common/RavenwoodCommonUtils.java
index 520f050..2a04d44 100644
--- a/ravenwood/runtime-common-src/com/android/ravenwood/common/RavenwoodCommonUtils.java
+++ b/ravenwood/runtime-common-src/com/android/ravenwood/common/RavenwoodCommonUtils.java
@@ -30,6 +30,7 @@
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;
+import java.util.function.Supplier;
public class RavenwoodCommonUtils {
private static final String TAG = "RavenwoodCommonUtils";
@@ -277,11 +278,55 @@
(isStatic ? "static" : "")));
}
+ /**
+ * Run a supplier and swallow the exception, if any.
+ *
+ * It's a dangerous function. Only use it in an exception handler where we don't want to crash.
+ */
+ @Nullable
+ public static <T> T runIgnoringException(@NonNull Supplier<T> s) {
+ try {
+ return s.get();
+ } catch (Throwable th) {
+ log(TAG, "Warning: Exception detected! " + getStackTraceString(th));
+ }
+ return null;
+ }
+
+ /**
+ * Run a runnable and swallow the exception, if any.
+ *
+ * It's a dangerous function. Only use it in an exception handler where we don't want to crash.
+ */
+ public static void runIgnoringException(@NonNull Runnable r) {
+ runIgnoringException(() -> {
+ r.run();
+ return null;
+ });
+ }
+
@NonNull
- public static String getStackTraceString(@Nullable Throwable th) {
+ public static String getStackTraceString(@NonNull Throwable th) {
StringWriter stringWriter = new StringWriter();
PrintWriter writer = new PrintWriter(stringWriter);
th.printStackTrace(writer);
return stringWriter.toString();
}
+
+ /** Same as {@link Integer#parseInt(String)} but accepts null and returns null. */
+ @Nullable
+ public static Integer parseNullableInt(@Nullable String value) {
+ if (value == null) {
+ return null;
+ }
+ return Integer.parseInt(value);
+ }
+
+ /**
+ * @return {@code value} if it's non-null. Otherwise, returns {@code def}.
+ */
+ @Nullable
+ public static <T> T withDefault(@Nullable T value, @Nullable T def) {
+ return value != null ? value : def;
+ }
}
diff --git a/ravenwood/scripts/run-ravenwood-tests.sh b/ravenwood/scripts/run-ravenwood-tests.sh
index fe2269a..27c5ea1 100755
--- a/ravenwood/scripts/run-ravenwood-tests.sh
+++ b/ravenwood/scripts/run-ravenwood-tests.sh
@@ -33,7 +33,7 @@
exclude_re=""
smoke_exclude_re=""
dry_run=""
-while getopts "sx:f:dt" opt; do
+while getopts "sx:f:dtb" opt; do
case "$opt" in
s)
# Remove slow tests.
@@ -52,8 +52,13 @@
dry_run="echo"
;;
t)
+ # Redirect log to terminal
export RAVENWOOD_LOG_OUT=$(tty)
;;
+ b)
+ # Build only
+ ATEST=m
+ ;;
'?')
exit 1
;;
@@ -99,11 +104,16 @@
# Calculate the removed tests.
-diff="$(diff <(echo "${all_tests[@]}" | tr ' ' '\n') <(echo "${targets[@]}" | tr ' ' '\n') )"
+diff="$(diff <(echo "${all_tests[@]}" | tr ' ' '\n') <(echo "${targets[@]}" | tr ' ' '\n') | grep -v [0-9] )"
if [[ "$diff" != "" ]]; then
echo "Excluded tests:"
echo "$diff"
fi
-$dry_run ${ATEST:-atest} "${targets[@]}"
+run() {
+ echo "Running: ${@}"
+ "${@}"
+}
+
+run $dry_run ${ATEST:-atest} "${targets[@]}"
diff --git a/ravenwood/tests/bivalentinst/Android.bp b/ravenwood/tests/bivalentinst/Android.bp
index 41e45e5..31e3bcc 100644
--- a/ravenwood/tests/bivalentinst/Android.bp
+++ b/ravenwood/tests/bivalentinst/Android.bp
@@ -27,6 +27,9 @@
"junit",
"truth",
],
+
+ package_name: "com.android.ravenwood.bivalentinsttest_self_inst",
+
resource_apk: "RavenwoodBivalentInstTest_self_inst_device",
auto_gen_config: true,
}
@@ -53,6 +56,10 @@
"truth",
],
resource_apk: "RavenwoodBivalentInstTestTarget",
+
+ package_name: "com.android.ravenwood.bivalentinst_target_app",
+ inst_package_name: "com.android.ravenwood.bivalentinsttest_nonself_inst",
+
inst_resource_apk: "RavenwoodBivalentInstTest_nonself_inst_device",
auto_gen_config: true,
}
diff --git a/ravenwood/tests/bivalentinst/test/com/android/ravenwoodtest/bivalentinst/RavenwoodInstrumentationTest_nonself.java b/ravenwood/tests/bivalentinst/test/com/android/ravenwoodtest/bivalentinst/RavenwoodInstrumentationTest_nonself.java
index 92d43d7..db252d8 100644
--- a/ravenwood/tests/bivalentinst/test/com/android/ravenwoodtest/bivalentinst/RavenwoodInstrumentationTest_nonself.java
+++ b/ravenwood/tests/bivalentinst/test/com/android/ravenwoodtest/bivalentinst/RavenwoodInstrumentationTest_nonself.java
@@ -19,8 +19,6 @@
import android.app.Instrumentation;
import android.content.Context;
-import android.platform.test.ravenwood.RavenwoodConfig;
-import android.platform.test.ravenwood.RavenwoodConfig.Config;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.platform.app.InstrumentationRegistry;
@@ -39,11 +37,6 @@
private static final String TEST_PACKAGE_NAME =
"com.android.ravenwood.bivalentinsttest_nonself_inst";
- @Config
- public static final RavenwoodConfig sConfig = new RavenwoodConfig.Builder()
- .setPackageName(TEST_PACKAGE_NAME)
- .setTargetPackageName(TARGET_PACKAGE_NAME)
- .build();
private static Instrumentation sInstrumentation;
private static Context sTestContext;
diff --git a/ravenwood/tests/bivalentinst/test/com/android/ravenwoodtest/bivalentinst/RavenwoodInstrumentationTest_self.java b/ravenwood/tests/bivalentinst/test/com/android/ravenwoodtest/bivalentinst/RavenwoodInstrumentationTest_self.java
index 2f35923..94b1861 100644
--- a/ravenwood/tests/bivalentinst/test/com/android/ravenwoodtest/bivalentinst/RavenwoodInstrumentationTest_self.java
+++ b/ravenwood/tests/bivalentinst/test/com/android/ravenwoodtest/bivalentinst/RavenwoodInstrumentationTest_self.java
@@ -19,8 +19,6 @@
import android.app.Instrumentation;
import android.content.Context;
-import android.platform.test.ravenwood.RavenwoodConfig;
-import android.platform.test.ravenwood.RavenwoodConfig.Config;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.platform.app.InstrumentationRegistry;
@@ -40,13 +38,6 @@
private static final String TEST_PACKAGE_NAME =
"com.android.ravenwood.bivalentinsttest_self_inst";
- @Config
- public static final RavenwoodConfig sConfig = new RavenwoodConfig.Builder()
- .setPackageName(TEST_PACKAGE_NAME)
- .setTargetPackageName(TARGET_PACKAGE_NAME)
- .build();
-
-
private static Instrumentation sInstrumentation;
private static Context sTestContext;
private static Context sTargetContext;
diff --git a/ravenwood/tests/bivalenttest/Android.bp b/ravenwood/tests/bivalenttest/Android.bp
index 40e6672..ac545df 100644
--- a/ravenwood/tests/bivalenttest/Android.bp
+++ b/ravenwood/tests/bivalenttest/Android.bp
@@ -84,6 +84,8 @@
android_ravenwood_test {
name: "RavenwoodBivalentTest",
defaults: ["ravenwood-bivalent-defaults"],
+ target_sdk_version: "34",
+ package_name: "com.android.ravenwoodtest.bivalenttest",
auto_gen_config: true,
}
diff --git a/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/RavenwoodConfigTest.java b/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/RavenwoodConfigTest.java
index a5a16c1..306c2b39 100644
--- a/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/RavenwoodConfigTest.java
+++ b/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/RavenwoodConfigTest.java
@@ -20,8 +20,6 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assume.assumeTrue;
-import android.platform.test.ravenwood.RavenwoodConfig;
-
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.platform.app.InstrumentationRegistry;
@@ -33,13 +31,7 @@
*/
@RunWith(AndroidJUnit4.class)
public class RavenwoodConfigTest {
- private static final String PACKAGE_NAME = "com.test";
-
- @RavenwoodConfig.Config
- public static RavenwoodConfig sConfig =
- new RavenwoodConfig.Builder()
- .setPackageName(PACKAGE_NAME)
- .build();
+ private static final String PACKAGE_NAME = "com.android.ravenwoodtest.bivalenttest";
@Test
public void testConfig() {
diff --git a/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/compat/RavenwoodCompatFrameworkTest.kt b/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/compat/RavenwoodCompatFrameworkTest.kt
index a95760d..882c91c 100644
--- a/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/compat/RavenwoodCompatFrameworkTest.kt
+++ b/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/compat/RavenwoodCompatFrameworkTest.kt
@@ -16,8 +16,6 @@
package com.android.ravenwoodtest.bivalenttest.compat
import android.app.compat.CompatChanges
-import android.os.Build
-import android.platform.test.ravenwood.RavenwoodConfig
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.internal.ravenwood.RavenwoodEnvironment.CompatIdsForTest
import org.junit.Assert
@@ -26,14 +24,6 @@
@RunWith(AndroidJUnit4::class)
class RavenwoodCompatFrameworkTest {
- companion object {
- @JvmField // Expose as a raw field, not as a property.
- @RavenwoodConfig.Config
- val config = RavenwoodConfig.Builder()
- .setTargetSdkLevel(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
- .build()
- }
-
@Test
fun testEnabled() {
Assert.assertTrue(CompatChanges.isChangeEnabled(CompatIdsForTest.TEST_COMPAT_ID_1))
@@ -53,4 +43,4 @@
fun testEnabledAfterUForUApps() {
Assert.assertFalse(CompatChanges.isChangeEnabled(CompatIdsForTest.TEST_COMPAT_ID_4))
}
-}
\ No newline at end of file
+}
diff --git a/ravenwood/tests/coretest/test/com/android/ravenwoodtest/runnercallbacktests/RavenwoodRunnerConfigValidationTest.java b/ravenwood/tests/coretest/test/com/android/ravenwoodtest/runnercallbacktests/RavenwoodRunnerConfigValidationTest.java
index 02d1073..f94b98b 100644
--- a/ravenwood/tests/coretest/test/com/android/ravenwoodtest/runnercallbacktests/RavenwoodRunnerConfigValidationTest.java
+++ b/ravenwood/tests/coretest/test/com/android/ravenwoodtest/runnercallbacktests/RavenwoodRunnerConfigValidationTest.java
@@ -24,6 +24,7 @@
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.platform.app.InstrumentationRegistry;
+import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestRule;
@@ -32,6 +33,10 @@
/**
* Test for @Config field extraction and validation.
+ *
+ * TODO(b/377765941) Most of the tests here will be obsolete and deleted with b/377765941, but
+ * some of the tests may need to be re-implemented one way or another. (e.g. the package name
+ * test.) Until that happens, we'll keep all tests here but add an {@code @Ignore} instead.
*/
@NoRavenizer // This class shouldn't be executed with RavenwoodAwareTestRunner.
public class RavenwoodRunnerConfigValidationTest extends RavenwoodRunnerTestBase {
@@ -59,6 +64,7 @@
testRunFinished: 1,0,0,0
""")
// CHECKSTYLE:ON
+ @Ignore // Package name is no longer set via config.
public static class ConfigInBaseClassTest extends ConfigInBaseClass {
@Test
public void test() {
@@ -83,6 +89,7 @@
testRunFinished: 1,0,0,0
""")
// CHECKSTYLE:ON
+ @Ignore // Package name is no longer set via config.
public static class ConfigOverridingTest extends ConfigInBaseClass {
static String PACKAGE_NAME_OVERRIDE = "com.ConfigOverridingTest";
@@ -376,6 +383,7 @@
testRunFinished: 1,0,0,0
""")
// CHECKSTYLE:ON
+ @Ignore // Package name is no longer set via config.
public static class RuleInBaseClassSuccessTest extends RuleInBaseClass {
@Test
@@ -437,6 +445,7 @@
testRunFinished: 1,1,0,0
""")
// CHECKSTYLE:ON
+ @Ignore // Package name is no longer set via config.
public static class RuleWithDifferentTypeInBaseClassSuccessTest extends RuleWithDifferentTypeInBaseClass {
@Test
diff --git a/ravenwood/tests/coretest/test/com/android/ravenwoodtest/runnercallbacktests/RavenwoodRunnerTestBase.java b/ravenwood/tests/coretest/test/com/android/ravenwoodtest/runnercallbacktests/RavenwoodRunnerTestBase.java
index f7a2198..0e3d053 100644
--- a/ravenwood/tests/coretest/test/com/android/ravenwoodtest/runnercallbacktests/RavenwoodRunnerTestBase.java
+++ b/ravenwood/tests/coretest/test/com/android/ravenwoodtest/runnercallbacktests/RavenwoodRunnerTestBase.java
@@ -25,6 +25,8 @@
import junitparams.JUnitParamsRunner;
import junitparams.Parameters;
+
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.Description;
import org.junit.runner.JUnitCore;
@@ -103,6 +105,7 @@
var thisClass = this.getClass();
var ret = Arrays.stream(thisClass.getNestMembers())
.filter((c) -> c.getAnnotation(Expected.class) != null)
+ .filter((c) -> c.getAnnotation(Ignore.class) == null)
.toArray(Class[]::new);
assertThat(ret.length).isGreaterThan(0);
diff --git a/ravenwood/tests/runtime-test/Android.bp b/ravenwood/tests/runtime-test/Android.bp
index 0c0df1f..c352003 100644
--- a/ravenwood/tests/runtime-test/Android.bp
+++ b/ravenwood/tests/runtime-test/Android.bp
@@ -9,7 +9,7 @@
android_ravenwood_test {
name: "RavenwoodRuntimeTest",
-
+ target_sdk_version: "34",
libs: [
"ravenwood-helper-runtime",
],
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index 827e3ef..762665c 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -16,6 +16,7 @@
package com.android.server.appwidget;
+import static android.appwidget.flags.Flags.checkRemoteViewsUriPermission;
import static android.appwidget.flags.Flags.remoteAdapterConversion;
import static android.appwidget.flags.Flags.remoteViewsProto;
import static android.appwidget.flags.Flags.removeAppWidgetServiceIoFromCriticalPath;
@@ -62,6 +63,7 @@
import android.appwidget.PendingHostUpdate;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.Intent.FilterComparison;
@@ -150,6 +152,8 @@
import com.android.server.LocalServices;
import com.android.server.ServiceThread;
import com.android.server.WidgetBackupProvider;
+import com.android.server.uri.GrantUri;
+import com.android.server.uri.UriGrantsManagerInternal;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -2548,6 +2552,10 @@
// Make sure the package runs under the caller uid.
mSecurityPolicy.enforceCallFromPackage(callingPackage);
+ // Make sure RemoteViews do not contain URIs that the caller cannot access.
+ if (checkRemoteViewsUriPermission()) {
+ checkRemoteViewsUris(views);
+ }
synchronized (mLock) {
ensureGroupStateLoadedLocked(userId);
@@ -2568,6 +2576,39 @@
}
/**
+ * Checks that all of the Uris in the given RemoteViews are accessible to the caller.
+ */
+ private void checkRemoteViewsUris(RemoteViews views) {
+ UriGrantsManagerInternal uriGrantsManager = LocalServices.getService(
+ UriGrantsManagerInternal.class);
+ int callingUid = Binder.getCallingUid();
+ int callingUser = UserHandle.getCallingUserId();
+ views.visitUris(uri -> {
+ switch (uri.getScheme()) {
+ // Check that content:// URIs are accessible to the caller.
+ case ContentResolver.SCHEME_CONTENT:
+ boolean canAccessUri = uriGrantsManager.checkUriPermission(
+ GrantUri.resolve(callingUser, uri,
+ Intent.FLAG_GRANT_READ_URI_PERMISSION), callingUid,
+ Intent.FLAG_GRANT_READ_URI_PERMISSION,
+ /* isFullAccessForContentUri= */ true);
+ if (!canAccessUri) {
+ throw new SecurityException(
+ "Provider uid " + callingUid + " cannot access URI " + uri);
+ }
+ break;
+ // android.resource:// URIs are always allowed.
+ case ContentResolver.SCHEME_ANDROID_RESOURCE:
+ break;
+ // file:// and any other schemes are disallowed.
+ case ContentResolver.SCHEME_FILE:
+ default:
+ throw new SecurityException("Disallowed URI " + uri + " in RemoteViews.");
+ }
+ });
+ }
+
+ /**
* Increment the counter of widget ids and return the new id.
*
* Typically called by {@link #allocateAppWidgetId} when a instance of widget is created,
diff --git a/services/autofill/features.aconfig b/services/autofill/features.aconfig
index bd46deb..b3fe5f2 100644
--- a/services/autofill/features.aconfig
+++ b/services/autofill/features.aconfig
@@ -2,6 +2,13 @@
container: "system"
flag {
+ name: "autofill_w_metrics"
+ namespace: "autofill"
+ description: "Guards against new metrics definitions introduced in W"
+ bug: "342676602"
+}
+
+flag {
name: "autofill_credman_integration"
namespace: "autofill"
description: "Guards Autofill Framework against Autofill-Credman integration"
diff --git a/services/autofill/java/com/android/server/autofill/Helper.java b/services/autofill/java/com/android/server/autofill/Helper.java
index cd2a535..4f632c9 100644
--- a/services/autofill/java/com/android/server/autofill/Helper.java
+++ b/services/autofill/java/com/android/server/autofill/Helper.java
@@ -28,8 +28,11 @@
import android.app.assist.AssistStructure;
import android.app.assist.AssistStructure.ViewNode;
import android.app.assist.AssistStructure.WindowNode;
+import android.app.slice.Slice;
+import android.app.slice.SliceItem;
import android.content.ComponentName;
import android.content.Context;
+import android.graphics.drawable.Icon;
import android.hardware.display.DisplayManager;
import android.metrics.LogMaker;
import android.os.UserHandle;
@@ -97,11 +100,12 @@
@UserIdInt int userId, @NonNull RemoteViews rView) {
final AtomicBoolean permissionsOk = new AtomicBoolean(true);
- rView.visitUris(uri -> {
- int uriOwnerId = android.content.ContentProvider.getUserIdFromUri(uri);
- boolean allowed = uriOwnerId == userId;
- permissionsOk.set(allowed & permissionsOk.get());
- });
+ rView.visitUris(
+ uri -> {
+ int uriOwnerId = android.content.ContentProvider.getUserIdFromUri(uri, userId);
+ boolean allowed = uriOwnerId == userId;
+ permissionsOk.set(allowed & permissionsOk.get());
+ });
return permissionsOk.get();
}
@@ -150,6 +154,47 @@
return (ok ? rView : null);
}
+ /**
+ * Checks the URI permissions of the icon in the slice, to see if the current userId is able to
+ * access it.
+ *
+ * <p>Returns null if slice contains user inaccessible icons
+ *
+ * <p>TODO: instead of returning a null Slice when the current userId cannot access an icon,
+ * return a reconstructed Slice without the icons. This is currently non-trivial since there are
+ * no public methods to generically add SliceItems to Slices
+ */
+ public static @Nullable Slice sanitizeSlice(Slice slice) {
+ if (slice == null) {
+ return null;
+ }
+
+ int userId = ActivityManager.getCurrentUser();
+
+ // Recontruct the Slice, filtering out bad icons
+ for (SliceItem sliceItem : slice.getItems()) {
+ if (!sliceItem.getFormat().equals(SliceItem.FORMAT_IMAGE)) {
+ // Not an image slice
+ continue;
+ }
+
+ Icon icon = sliceItem.getIcon();
+ if (icon.getType() != Icon.TYPE_URI
+ && icon.getType() != Icon.TYPE_URI_ADAPTIVE_BITMAP) {
+ // No URIs to sanitize
+ continue;
+ }
+
+ int iconUriId = android.content.ContentProvider.getUserIdFromUri(icon.getUri(), userId);
+
+ if (iconUriId != userId) {
+ Slog.w(TAG, "sanitizeSlice() user: " + userId + " cannot access icons in Slice");
+ return null;
+ }
+ }
+
+ return slice;
+ }
@Nullable
static AutofillId[] toArray(@Nullable ArraySet<AutofillId> set) {
@@ -308,7 +353,10 @@
private static void addAutofillableIds(@NonNull ViewNode node,
@NonNull ArrayList<AutofillId> ids, boolean autofillableOnly) {
if (!autofillableOnly || node.getAutofillType() != View.AUTOFILL_TYPE_NONE) {
- ids.add(node.getAutofillId());
+ AutofillId id = node.getAutofillId();
+ if (id != null) {
+ ids.add(id);
+ }
}
final int size = node.getChildCount();
for (int i = 0; i < size; i++) {
diff --git a/services/autofill/java/com/android/server/autofill/ui/RemoteInlineSuggestionViewConnector.java b/services/autofill/java/com/android/server/autofill/ui/RemoteInlineSuggestionViewConnector.java
index 38a412f..50a26b3 100644
--- a/services/autofill/java/com/android/server/autofill/ui/RemoteInlineSuggestionViewConnector.java
+++ b/services/autofill/java/com/android/server/autofill/ui/RemoteInlineSuggestionViewConnector.java
@@ -27,6 +27,7 @@
import android.util.Slog;
import com.android.server.LocalServices;
+import com.android.server.autofill.Helper;
import com.android.server.autofill.RemoteInlineSuggestionRenderService;
import com.android.server.inputmethod.InputMethodManagerInternal;
@@ -83,6 +84,10 @@
*/
public boolean renderSuggestion(int width, int height,
@NonNull IInlineSuggestionUiCallback callback) {
+ if (Helper.sanitizeSlice(mInlinePresentation.getSlice()) == null) {
+ if (sDebug) Slog.d(TAG, "Skipped rendering inline suggestion.");
+ return false;
+ }
if (mRemoteRenderService != null) {
if (sDebug) Slog.d(TAG, "Request to recreate the UI");
mRemoteRenderService.renderSuggestion(callback, mInlinePresentation, width, height,
diff --git a/services/backup/flags.aconfig b/services/backup/flags.aconfig
index d53f949..fcb7934 100644
--- a/services/backup/flags.aconfig
+++ b/services/backup/flags.aconfig
@@ -60,3 +60,12 @@
bug: "331749778"
is_fixed_read_only: true
}
+
+flag {
+ name: "enable_restricted_mode_changes"
+ namespace: "onboarding"
+ description: "Enables the new framework behavior of not putting apps in restricted mode for "
+ "B&R operations in certain cases."
+ bug: "376661510"
+ is_fixed_read_only: true
+}
diff --git a/services/backup/java/com/android/server/backup/BackupAgentConnectionManager.java b/services/backup/java/com/android/server/backup/BackupAgentConnectionManager.java
new file mode 100644
index 0000000..5e4bab15
--- /dev/null
+++ b/services/backup/java/com/android/server/backup/BackupAgentConnectionManager.java
@@ -0,0 +1,318 @@
+/*
+ * 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.server.backup;
+
+import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
+import static com.android.server.backup.BackupManagerService.TAG;
+
+import android.annotation.Nullable;
+import android.app.ActivityManager;
+import android.app.ActivityManagerInternal;
+import android.app.ApplicationThreadConstants;
+import android.app.IActivityManager;
+import android.app.IBackupAgent;
+import android.app.backup.BackupAnnotations;
+import android.app.compat.CompatChanges;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledSince;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.os.Binder;
+import android.os.Build;
+import android.os.IBinder;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.util.ArraySet;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.LocalServices;
+import com.android.server.backup.internal.LifecycleOperationStorage;
+
+import java.util.Set;
+
+/**
+ * Handles the lifecycle of {@link IBackupAgent}s that the {@link UserBackupManagerService}
+ * communicates with.
+ *
+ * <p>There can only be one agent that's connected to at a time.
+ *
+ * <p>There should be only one instance of this class per {@link UserBackupManagerService}.
+ */
+public class BackupAgentConnectionManager {
+
+ /**
+ * Enables the OS making a decision on whether backup restricted mode should be used for apps
+ * that haven't explicitly opted in or out. See
+ * {@link android.content.pm.PackageManager#PROPERTY_USE_RESTRICTED_BACKUP_MODE} for details.
+ */
+ @ChangeId
+ @EnabledSince(targetSdkVersion = Build.VERSION_CODES.BAKLAVA)
+ public static final long OS_DECIDES_BACKUP_RESTRICTED_MODE = 376661510;
+
+ // The thread performing the sequence of queued backups binds to each app's agent
+ // in succession. Bind notifications are asynchronously delivered through the
+ // Activity Manager; use this lock object to signal when a requested binding has
+ // completed.
+ private final Object mAgentConnectLock = new Object();
+ private IBackupAgent mConnectedAgent;
+ private volatile boolean mConnecting;
+ private final ArraySet<String> mRestoreNoRestrictedModePackages = new ArraySet<>();
+ private final ArraySet<String> mBackupNoRestrictedModePackages = new ArraySet<>();
+
+ private final IActivityManager mActivityManager;
+ private final ActivityManagerInternal mActivityManagerInternal;
+ private final LifecycleOperationStorage mOperationStorage;
+ private final PackageManager mPackageManager;
+ private final UserBackupManagerService mUserBackupManagerService;
+ private final int mUserId;
+ private final String mUserIdMsg;
+
+ BackupAgentConnectionManager(LifecycleOperationStorage operationStorage,
+ PackageManager packageManager, UserBackupManagerService userBackupManagerService,
+ int userId) {
+ mActivityManager = ActivityManager.getService();
+ mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
+ mOperationStorage = operationStorage;
+ mPackageManager = packageManager;
+ mUserBackupManagerService = userBackupManagerService;
+ mUserId = userId;
+ mUserIdMsg = "[UserID:" + userId + "] ";
+ }
+
+ /**
+ * Fires off a backup agent, blocking until it attaches (and ActivityManager will call
+ * {@link #agentConnected(String, IBinder)}) or until this operation times out.
+ *
+ * @param mode a {@code BACKUP_MODE} from {@link android.app.ApplicationThreadConstants}.
+ */
+ @Nullable
+ public IBackupAgent bindToAgentSynchronous(ApplicationInfo app, int mode,
+ @BackupAnnotations.BackupDestination int backupDestination) {
+ IBackupAgent agent = null;
+ synchronized (mAgentConnectLock) {
+ mConnecting = true;
+ mConnectedAgent = null;
+ boolean useRestrictedMode = shouldUseRestrictedBackupModeForPackage(mode,
+ app.packageName);
+ try {
+ if (mActivityManager.bindBackupAgent(app.packageName, mode, mUserId,
+ backupDestination, useRestrictedMode)) {
+ Slog.d(TAG, mUserIdMsg + "awaiting agent for " + app);
+
+ // success; wait for the agent to arrive
+ // only wait 10 seconds for the bind to happen
+ long timeoutMark = System.currentTimeMillis() + 10 * 1000;
+ while (mConnecting && mConnectedAgent == null && (System.currentTimeMillis()
+ < timeoutMark)) {
+ try {
+ mAgentConnectLock.wait(5000);
+ } catch (InterruptedException e) {
+ // just bail
+ Slog.w(TAG, mUserIdMsg + "Interrupted: " + e);
+ mConnecting = false;
+ mConnectedAgent = null;
+ }
+ }
+
+ // if we timed out with no connect, abort and move on
+ if (mConnecting) {
+ Slog.w(TAG, mUserIdMsg + "Timeout waiting for agent " + app);
+ mConnectedAgent = null;
+ }
+ Slog.i(TAG, mUserIdMsg + "got agent " + mConnectedAgent);
+ agent = mConnectedAgent;
+ }
+ } catch (RemoteException e) {
+ // can't happen - ActivityManager is local
+ }
+ }
+ if (agent == null) {
+ mActivityManagerInternal.clearPendingBackup(mUserId);
+ }
+ return agent;
+ }
+
+ /**
+ * Tell the ActivityManager that we are done with the {@link IBackupAgent} of this {@code app}.
+ * It will tell the app to destroy the agent.
+ */
+ public void unbindAgent(ApplicationInfo app) {
+ try {
+ mActivityManager.unbindBackupAgent(app);
+ } catch (RemoteException e) {
+ // Can't happen - activity manager is local
+ }
+ }
+
+ /**
+ * Callback: a requested backup agent has been instantiated. This should only be called from
+ * the
+ * {@link ActivityManager} when it's telling us that an agent is ready after a call to
+ * {@link #bindToAgentSynchronous(ApplicationInfo, int, int)}.
+ */
+ public void agentConnected(String packageName, IBinder agentBinder) {
+ synchronized (mAgentConnectLock) {
+ if (getCallingUid() == android.os.Process.SYSTEM_UID) {
+ Slog.d(TAG,
+ mUserIdMsg + "agentConnected pkg=" + packageName + " agent=" + agentBinder);
+ mConnectedAgent = IBackupAgent.Stub.asInterface(agentBinder);
+ mConnecting = false;
+ } else {
+ Slog.w(TAG, mUserIdMsg + "Non-system process uid=" + getCallingUid()
+ + " claiming agent connected");
+ }
+ mAgentConnectLock.notifyAll();
+ }
+ }
+
+ /**
+ * Callback: a backup agent has failed to come up, or has unexpectedly quit. If the agent failed
+ * to come up in the first place, the agentBinder argument will be {@code null}. This should
+ * only be called from the {@link ActivityManager}.
+ */
+ public void agentDisconnected(String packageName) {
+ synchronized (mAgentConnectLock) {
+ if (getCallingUid() == Process.SYSTEM_UID) {
+ mConnectedAgent = null;
+ mConnecting = false;
+ } else {
+ Slog.w(TAG, mUserIdMsg + "Non-system process uid=" + getCallingUid()
+ + " claiming agent disconnected");
+ }
+ Slog.w(TAG, mUserIdMsg + "agentDisconnected: the backup agent for " + packageName
+ + " died: cancel current operations");
+
+ // Offload operation cancellation off the main thread as the cancellation callbacks
+ // might call out to BackupTransport. Other operations started on the same package
+ // before the cancellation callback has executed will also be cancelled by the callback.
+ Runnable cancellationRunnable = () -> {
+ // handleCancel() causes the PerformFullTransportBackupTask to go on to
+ // tearDownAgentAndKill: that will unbindBackupAgent in the Activity Manager, so
+ // that the package being backed up doesn't get stuck in restricted mode until the
+ // backup time-out elapses.
+ for (int token : mOperationStorage.operationTokensForPackage(packageName)) {
+ if (MORE_DEBUG) {
+ Slog.d(TAG,
+ mUserIdMsg + "agentDisconnected: will handleCancel(all) for token:"
+ + Integer.toHexString(token));
+ }
+ mUserBackupManagerService.handleCancel(token, true /* cancelAll */);
+ }
+ };
+ getThreadForCancellation(cancellationRunnable).start();
+
+ mAgentConnectLock.notifyAll();
+ }
+ }
+
+ /**
+ * Marks the given set of packages as packages that should not be put into restricted mode if
+ * they are started for the given {@link BackupAnnotations.OperationType}.
+ */
+ public void setNoRestrictedModePackages(Set<String> packageNames,
+ @BackupAnnotations.OperationType int opType) {
+ if (opType == BackupAnnotations.OperationType.BACKUP) {
+ mBackupNoRestrictedModePackages.clear();
+ mBackupNoRestrictedModePackages.addAll(packageNames);
+ } else if (opType == BackupAnnotations.OperationType.RESTORE) {
+ mRestoreNoRestrictedModePackages.clear();
+ mRestoreNoRestrictedModePackages.addAll(packageNames);
+ } else {
+ throw new IllegalArgumentException("opType must be BACKUP or RESTORE");
+ }
+ }
+
+ /**
+ * Clears the list of packages that should not be put into restricted mode for either backup or
+ * restore.
+ */
+ public void clearNoRestrictedModePackages() {
+ mBackupNoRestrictedModePackages.clear();
+ mRestoreNoRestrictedModePackages.clear();
+ }
+
+ /**
+ * If the app has specified {@link PackageManager#PROPERTY_USE_RESTRICTED_BACKUP_MODE}, then
+ * its value is returned. If it hasn't and it targets an SDK below
+ * {@link Build.VERSION_CODES#BAKLAVA} then returns true. If it targets a newer SDK, then
+ * returns the decision made by the {@link android.app.backup.BackupTransport}.
+ *
+ * <p>When this method is called, we should have already asked the transport and cached its
+ * response in {@link #mBackupNoRestrictedModePackages} or
+ * {@link #mRestoreNoRestrictedModePackages} so this method will immediately return without
+ * any IPC to the transport.
+ */
+ private boolean shouldUseRestrictedBackupModeForPackage(
+ @BackupAnnotations.OperationType int mode, String packageName) {
+ if (!Flags.enableRestrictedModeChanges()) {
+ return true;
+ }
+
+ // Key/Value apps are never put in restricted mode.
+ if (mode == ApplicationThreadConstants.BACKUP_MODE_INCREMENTAL
+ || mode == ApplicationThreadConstants.BACKUP_MODE_RESTORE) {
+ return false;
+ }
+
+ try {
+ PackageManager.Property property = mPackageManager.getPropertyAsUser(
+ PackageManager.PROPERTY_USE_RESTRICTED_BACKUP_MODE,
+ packageName, /* className= */ null, mUserId);
+ if (property.isBoolean()) {
+ // If the package has explicitly specified, we won't ask the transport.
+ return property.getBoolean();
+ } else {
+ Slog.w(TAG,
+ PackageManager.PROPERTY_USE_RESTRICTED_BACKUP_MODE + "must be a boolean.");
+ }
+ } catch (NameNotFoundException e) {
+ // This is expected when the package has not defined the property in its manifest.
+ }
+
+ // The package has not specified the property. The behavior depends on the package's
+ // targetSdk.
+ // <36 gets the old behavior of always using restricted mode.
+ if (!CompatChanges.isChangeEnabled(OS_DECIDES_BACKUP_RESTRICTED_MODE, packageName,
+ UserHandle.of(mUserId))) {
+ return true;
+ }
+
+ // Apps targeting >=36 get the behavior decided by the transport.
+ // By this point, we should have asked the transport and cached its decision.
+ if ((mode == ApplicationThreadConstants.BACKUP_MODE_FULL
+ && mBackupNoRestrictedModePackages.contains(packageName)) || (
+ mode == ApplicationThreadConstants.BACKUP_MODE_RESTORE_FULL
+ && mRestoreNoRestrictedModePackages.contains(packageName))) {
+ Slog.d(TAG, "Transport requested no restricted mode for: " + packageName);
+ return false;
+ }
+ return true;
+ }
+
+ @VisibleForTesting
+ Thread getThreadForCancellation(Runnable operation) {
+ return new Thread(operation, /* operationName */ "agent-disconnected");
+ }
+
+ @VisibleForTesting
+ int getCallingUid() {
+ return Binder.getCallingUid();
+ }
+}
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index 5f0071d..3f6ede9 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -658,7 +658,8 @@
getServiceForUserIfCallerHasPermission(userId, "agentConnected()");
if (userBackupManagerService != null) {
- userBackupManagerService.agentConnected(packageName, agentBinder);
+ userBackupManagerService.getBackupAgentConnectionManager().agentConnected(packageName,
+ agentBinder);
}
}
@@ -683,7 +684,8 @@
getServiceForUserIfCallerHasPermission(userId, "agentDisconnected()");
if (userBackupManagerService != null) {
- userBackupManagerService.agentDisconnected(packageName);
+ userBackupManagerService.getBackupAgentConnectionManager().agentDisconnected(
+ packageName);
}
}
diff --git a/services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java b/services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java
index f5d6836..136bacd 100644
--- a/services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java
+++ b/services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java
@@ -146,7 +146,8 @@
private IBackupAgent bindToAgent(ApplicationInfo targetApp) {
try {
- return mBackupManagerService.bindToAgentSynchronous(targetApp,
+ return mBackupManagerService.getBackupAgentConnectionManager().bindToAgentSynchronous(
+ targetApp,
ApplicationThreadConstants.BACKUP_MODE_INCREMENTAL,
BackupAnnotations.BackupDestination.CLOUD);
} catch (SecurityException e) {
diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
index 466d477..e085f6e 100644
--- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
@@ -44,7 +44,6 @@
import android.app.AlarmManager;
import android.app.AppGlobals;
import android.app.IActivityManager;
-import android.app.IBackupAgent;
import android.app.PendingIntent;
import android.app.backup.BackupAgent;
import android.app.backup.BackupAnnotations;
@@ -79,7 +78,6 @@
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
-import android.os.IBinder;
import android.os.Message;
import android.os.ParcelFileDescriptor;
import android.os.PowerManager;
@@ -302,8 +300,6 @@
// CPU on bring-up and increase time-to-UI.
private static final long INITIALIZATION_DELAY_MILLIS = 3000;
- // Timeout interval for deciding that a bind has taken too long.
- private static final long BIND_TIMEOUT_INTERVAL = 10 * 1000;
// Timeout interval for deciding that a clear-data has taken too long.
private static final long CLEAR_DATA_TIMEOUT_INTERVAL = 30 * 1000;
@@ -354,17 +350,7 @@
// locking around the pending-backup management
private final Object mQueueLock = new Object();
-
private final UserBackupPreferences mBackupPreferences;
-
- // The thread performing the sequence of queued backups binds to each app's agent
- // in succession. Bind notifications are asynchronously delivered through the
- // Activity Manager; use this lock object to signal when a requested binding has
- // completed.
- private final Object mAgentConnectLock = new Object();
- private IBackupAgent mConnectedAgent;
- private volatile boolean mConnecting;
-
private volatile boolean mBackupRunning;
private volatile long mLastBackupPass;
@@ -394,6 +380,7 @@
private ActiveRestoreSession mActiveRestoreSession;
+ private final BackupAgentConnectionManager mBackupAgentConnectionManager;
private final LifecycleOperationStorage mOperationStorage;
private final Random mTokenGenerator = new Random();
@@ -523,17 +510,22 @@
@VisibleForTesting
UserBackupManagerService(Context context, PackageManager packageManager,
LifecycleOperationStorage operationStorage, TransportManager transportManager,
- BackupHandler backupHandler, BackupManagerConstants backupManagerConstants) {
+ BackupHandler backupHandler, BackupManagerConstants backupManagerConstants,
+ IActivityManager activityManager, ActivityManagerInternal activityManagerInternal) {
mContext = context;
mUserId = 0;
mRegisterTransportsRequestedTime = 0;
mPackageManager = packageManager;
mOperationStorage = operationStorage;
+ mBackupAgentConnectionManager = new BackupAgentConnectionManager(mOperationStorage,
+ mPackageManager, this, mUserId);
mTransportManager = transportManager;
mFullBackupQueue = new ArrayList<>();
mBackupHandler = backupHandler;
mConstants = backupManagerConstants;
+ mActivityManager = activityManager;
+ mActivityManagerInternal = activityManagerInternal;
mBaseStateDir = null;
mDataDir = null;
@@ -543,13 +535,11 @@
mRunInitReceiver = null;
mRunInitIntent = null;
mAgentTimeoutParameters = null;
- mActivityManagerInternal = null;
mAlarmManager = null;
mWakelock = null;
mBackupPreferences = null;
mBackupPasswordManager = null;
mPackageManagerBinder = null;
- mActivityManager = null;
mBackupManagerBinder = null;
mScheduledBackupEligibility = null;
}
@@ -582,6 +572,8 @@
mAgentTimeoutParameters.start();
mOperationStorage = new LifecycleOperationStorage(mUserId);
+ mBackupAgentConnectionManager = new BackupAgentConnectionManager(mOperationStorage,
+ mPackageManager, this, mUserId);
Objects.requireNonNull(userBackupThread, "userBackupThread cannot be null");
mBackupHandler = new BackupHandler(this, mOperationStorage, userBackupThread);
@@ -1643,65 +1635,6 @@
}
}
- /** Fires off a backup agent, blocking until it attaches or times out. */
- @Nullable
- public IBackupAgent bindToAgentSynchronous(ApplicationInfo app, int mode,
- @BackupDestination int backupDestination) {
- IBackupAgent agent = null;
- synchronized (mAgentConnectLock) {
- mConnecting = true;
- mConnectedAgent = null;
- try {
- if (mActivityManager.bindBackupAgent(app.packageName, mode, mUserId,
- backupDestination)) {
- Slog.d(TAG, addUserIdToLogMessage(mUserId, "awaiting agent for " + app));
-
- // success; wait for the agent to arrive
- // only wait 10 seconds for the bind to happen
- long timeoutMark = System.currentTimeMillis() + BIND_TIMEOUT_INTERVAL;
- while (mConnecting && mConnectedAgent == null
- && (System.currentTimeMillis() < timeoutMark)) {
- try {
- mAgentConnectLock.wait(5000);
- } catch (InterruptedException e) {
- // just bail
- Slog.w(TAG, addUserIdToLogMessage(mUserId, "Interrupted: " + e));
- mConnecting = false;
- mConnectedAgent = null;
- }
- }
-
- // if we timed out with no connect, abort and move on
- if (mConnecting) {
- Slog.w(
- TAG,
- addUserIdToLogMessage(mUserId, "Timeout waiting for agent " + app));
- mConnectedAgent = null;
- }
- if (DEBUG) {
- Slog.i(TAG, addUserIdToLogMessage(mUserId, "got agent " + mConnectedAgent));
- }
- agent = mConnectedAgent;
- }
- } catch (RemoteException e) {
- // can't happen - ActivityManager is local
- }
- }
- if (agent == null) {
- mActivityManagerInternal.clearPendingBackup(mUserId);
- }
- return agent;
- }
-
- /** Unbind from a backup agent. */
- public void unbindAgent(ApplicationInfo app) {
- try {
- mActivityManager.unbindBackupAgent(app);
- } catch (RemoteException e) {
- // Can't happen - activity manager is local
- }
- }
-
/**
* Clear an application's data after a failed restore, blocking until the operation completes or
* times out.
@@ -2474,10 +2407,6 @@
AppWidgetBackupBridge.restoreWidgetState(packageName, widgetData, mUserId);
}
- // *****************************
- // NEW UNIFIED RESTORE IMPLEMENTATION
- // *****************************
-
/** Schedule a backup pass for {@code packageName}. */
public void dataChangedImpl(String packageName) {
HashSet<String> targets = dataChangedTargets(packageName);
@@ -3794,83 +3723,6 @@
}
/**
- * Callback: a requested backup agent has been instantiated. This should only be called from the
- * {@link ActivityManager}.
- */
- public void agentConnected(String packageName, IBinder agentBinder) {
- synchronized (mAgentConnectLock) {
- if (Binder.getCallingUid() == Process.SYSTEM_UID) {
- Slog.d(
- TAG,
- addUserIdToLogMessage(
- mUserId,
- "agentConnected pkg=" + packageName + " agent=" + agentBinder));
- mConnectedAgent = IBackupAgent.Stub.asInterface(agentBinder);
- mConnecting = false;
- } else {
- Slog.w(
- TAG,
- addUserIdToLogMessage(
- mUserId,
- "Non-system process uid="
- + Binder.getCallingUid()
- + " claiming agent connected"));
- }
- mAgentConnectLock.notifyAll();
- }
- }
-
- /**
- * Callback: a backup agent has failed to come up, or has unexpectedly quit. If the agent failed
- * to come up in the first place, the agentBinder argument will be {@code null}. This should
- * only be called from the {@link ActivityManager}.
- */
- public void agentDisconnected(String packageName) {
- synchronized (mAgentConnectLock) {
- if (Binder.getCallingUid() == Process.SYSTEM_UID) {
- mConnectedAgent = null;
- mConnecting = false;
- } else {
- Slog.w(
- TAG,
- addUserIdToLogMessage(
- mUserId,
- "Non-system process uid="
- + Binder.getCallingUid()
- + " claiming agent disconnected"));
- }
- Slog.w(TAG, "agentDisconnected: the backup agent for " + packageName
- + " died: cancel current operations");
-
- // Offload operation cancellation off the main thread as the cancellation callbacks
- // might call out to BackupTransport. Other operations started on the same package
- // before the cancellation callback has executed will also be cancelled by the callback.
- Runnable cancellationRunnable = () -> {
- // handleCancel() causes the PerformFullTransportBackupTask to go on to
- // tearDownAgentAndKill: that will unbindBackupAgent in the Activity Manager, so
- // that the package being backed up doesn't get stuck in restricted mode until the
- // backup time-out elapses.
- for (int token : mOperationStorage.operationTokensForPackage(packageName)) {
- if (MORE_DEBUG) {
- Slog.d(TAG, "agentDisconnected: will handleCancel(all) for token:"
- + Integer.toHexString(token));
- }
- handleCancel(token, true /* cancelAll */);
- }
- };
- getThreadForAsyncOperation(/* operationName */ "agent-disconnected",
- cancellationRunnable).start();
-
- mAgentConnectLock.notifyAll();
- }
- }
-
- @VisibleForTesting
- Thread getThreadForAsyncOperation(String operationName, Runnable operation) {
- return new Thread(operation, operationName);
- }
-
- /**
* An application being installed will need a restore pass, then the {@link PackageManager} will
* need to be told when the restore is finished.
*/
@@ -4417,4 +4269,8 @@
public IBackupManager getBackupManagerBinder() {
return mBackupManagerBinder;
}
+
+ public BackupAgentConnectionManager getBackupAgentConnectionManager() {
+ return mBackupAgentConnectionManager;
+ }
}
diff --git a/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java b/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java
index 1271206..b98cb10 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java
@@ -314,7 +314,7 @@
Slog.d(TAG, "Binding to full backup agent : " + mPkg.packageName);
}
mAgent =
- backupManagerService.bindToAgentSynchronous(
+ backupManagerService.getBackupAgentConnectionManager().bindToAgentSynchronous(
mPkg.applicationInfo, ApplicationThreadConstants.BACKUP_MODE_FULL,
mBackupEligibilityRules.getBackupDestination());
}
diff --git a/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java b/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
index cca166b..65730c9 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
@@ -16,6 +16,8 @@
package com.android.server.backup.fullbackup;
+import static android.app.backup.BackupAnnotations.OperationType.BACKUP;
+
import static com.android.server.backup.BackupManagerService.DEBUG;
import static com.android.server.backup.BackupManagerService.DEBUG_SCHEDULING;
import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
@@ -34,6 +36,7 @@
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
+import android.util.ArraySet;
import android.util.EventLog;
import android.util.Log;
import android.util.Slog;
@@ -388,6 +391,10 @@
}
}
+ // We ask the transport which packages should not be put in restricted mode and cache
+ // the result in UBMS to be used later when the apps are started for backup.
+ setNoRestrictedModePackages(transport, mPackages);
+
// Set up to send data to the transport
final int N = mPackages.size();
int chunkSizeInBytes = 8 * 1024; // 8KB
@@ -694,6 +701,10 @@
mUserBackupManagerService.scheduleNextFullBackupJob(backoff);
}
+ // Clear this to avoid using the memory until reboot.
+ mUserBackupManagerService
+ .getBackupAgentConnectionManager().clearNoRestrictedModePackages();
+
Slog.i(TAG, "Full data backup pass finished.");
mUserBackupManagerService.getWakelock().release();
}
@@ -722,6 +733,22 @@
}
}
+ private void setNoRestrictedModePackages(BackupTransportClient transport,
+ List<PackageInfo> packages) {
+ try {
+ Set<String> packageNames = new ArraySet<>();
+ for (int i = 0; i < packages.size(); i++) {
+ packageNames.add(packages.get(i).packageName);
+ }
+ packageNames = transport.getPackagesThatShouldNotUseRestrictedMode(packageNames,
+ BACKUP);
+ mUserBackupManagerService.getBackupAgentConnectionManager().setNoRestrictedModePackages(
+ packageNames, BACKUP);
+ } catch (RemoteException e) {
+ Slog.i(TAG, "Failed to retrieve no restricted mode packages from transport");
+ }
+ }
+
// Run the backup and pipe it back to the given socket -- expects to run on
// a standalone thread. The runner owns this half of the pipe, and closes
// it to indicate EOD to the other end.
diff --git a/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java b/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java
index 3a6e1ca..82232a6 100644
--- a/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java
+++ b/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java
@@ -741,7 +741,7 @@
final IBackupAgent agent;
try {
agent =
- mBackupManagerService.bindToAgentSynchronous(
+ mBackupManagerService.getBackupAgentConnectionManager().bindToAgentSynchronous(
packageInfo.applicationInfo, BACKUP_MODE_INCREMENTAL,
mBackupEligibilityRules.getBackupDestination());
if (agent == null) {
@@ -1302,7 +1302,8 @@
// For PM metadata (for which applicationInfo is null) there is no agent-bound state.
if (mCurrentPackage.applicationInfo != null) {
- mBackupManagerService.unbindAgent(mCurrentPackage.applicationInfo);
+ mBackupManagerService.getBackupAgentConnectionManager().unbindAgent(
+ mCurrentPackage.applicationInfo);
}
mAgent = null;
}
diff --git a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
index 2d99c96..b59e860 100644
--- a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
+++ b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
@@ -410,11 +410,7 @@
// All set; now set up the IPC and launch the agent
setUpPipes();
- mAgent = mBackupManagerService.bindToAgentSynchronous(mTargetApp,
- FullBackup.KEY_VALUE_DATA_TOKEN.equals(info.domain)
- ? ApplicationThreadConstants.BACKUP_MODE_RESTORE
- : ApplicationThreadConstants.BACKUP_MODE_RESTORE_FULL,
- mBackupEligibilityRules.getBackupDestination());
+ mAgent = bindToAgent(info);
mAgentPackage = pkg;
} catch (IOException | NameNotFoundException e) {
// fall through to error handling
@@ -805,15 +801,12 @@
return packages.contains(packageName);
}
- void sendOnRestorePackage(String name) {
- if (mObserver != null) {
- try {
- // TODO: use a more user-friendly name string
- mObserver.onRestorePackage(name);
- } catch (RemoteException e) {
- Slog.w(TAG, "full restore observer went away: restorePackage");
- mObserver = null;
- }
- }
+ private IBackupAgent bindToAgent(FileMetadata info) {
+ return mBackupManagerService.getBackupAgentConnectionManager().bindToAgentSynchronous(
+ mTargetApp,
+ FullBackup.KEY_VALUE_DATA_TOKEN.equals(info.domain)
+ ? ApplicationThreadConstants.BACKUP_MODE_RESTORE
+ : ApplicationThreadConstants.BACKUP_MODE_RESTORE_FULL,
+ mBackupEligibilityRules.getBackupDestination());
}
}
diff --git a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
index e536876..e5c7e5c 100644
--- a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
+++ b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
@@ -53,6 +53,7 @@
import android.os.SystemClock;
import android.os.UserHandle;
import android.provider.Settings;
+import android.util.ArraySet;
import android.util.EventLog;
import android.util.Slog;
@@ -482,6 +483,10 @@
return;
}
+ // We ask the transport which packages should not be put in restricted mode and cache
+ // the result in UBMS to be used later when the apps are started for restore.
+ setNoRestrictedModePackages(transport, packages);
+
RestoreDescription desc = transport.nextRestorePackage();
if (desc == null) {
Slog.e(TAG, "No restore metadata available; halting");
@@ -809,7 +814,7 @@
// Good to go! Set up and bind the agent...
mAgent =
- backupManagerService.bindToAgentSynchronous(
+ backupManagerService.getBackupAgentConnectionManager().bindToAgentSynchronous(
mCurrentPackage.applicationInfo,
ApplicationThreadConstants.BACKUP_MODE_RESTORE,
mBackupEligibilityRules.getBackupDestination());
@@ -1358,6 +1363,9 @@
// Clear any ongoing session timeout.
backupManagerService.getBackupHandler().removeMessages(MSG_RESTORE_SESSION_TIMEOUT);
+ // Clear this to avoid using the memory until reboot.
+ backupManagerService.getBackupAgentConnectionManager().clearNoRestrictedModePackages();
+
// If we have a PM token, we must under all circumstances be sure to
// handshake when we've finished.
if (mPmToken > 0) {
@@ -1819,4 +1827,21 @@
return packageInfo;
}
+
+ @VisibleForTesting
+ void setNoRestrictedModePackages(BackupTransportClient transport,
+ PackageInfo[] packages) {
+ try {
+ Set<String> packageNames = new ArraySet<>();
+ for (int i = 0; i < packages.length; i++) {
+ packageNames.add(packages[i].packageName);
+ }
+ packageNames = transport.getPackagesThatShouldNotUseRestrictedMode(packageNames,
+ RESTORE);
+ backupManagerService.getBackupAgentConnectionManager().setNoRestrictedModePackages(
+ packageNames, RESTORE);
+ } catch (RemoteException e) {
+ Slog.i(TAG, "Failed to retrieve restricted mode packages from transport");
+ }
+ }
}
diff --git a/services/backup/java/com/android/server/backup/transport/BackupTransportClient.java b/services/backup/java/com/android/server/backup/transport/BackupTransportClient.java
index daf34152..373811f 100644
--- a/services/backup/java/com/android/server/backup/transport/BackupTransportClient.java
+++ b/services/backup/java/com/android/server/backup/transport/BackupTransportClient.java
@@ -17,6 +17,7 @@
package com.android.server.backup.transport;
import android.annotation.Nullable;
+import android.app.backup.BackupAnnotations;
import android.app.backup.BackupTransport;
import android.app.backup.IBackupManagerMonitor;
import android.app.backup.RestoreDescription;
@@ -26,6 +27,7 @@
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
+import android.util.ArraySet;
import android.util.Slog;
import com.android.internal.backup.IBackupTransport;
@@ -375,6 +377,26 @@
}
/**
+ * See
+ * {@link IBackupTransport#getPackagesThatShouldNotUseRestrictedMode(List, int, AndroidFuture)}.
+ */
+ public Set<String> getPackagesThatShouldNotUseRestrictedMode(Set<String> packageNames,
+ @BackupAnnotations.OperationType
+ int operationType) throws RemoteException {
+ AndroidFuture<List<String>> resultFuture = mTransportFutures.newFuture();
+ mTransportBinder.getPackagesThatShouldNotUseRestrictedMode(List.copyOf(packageNames),
+ operationType,
+ resultFuture);
+ List<String> resultList = getFutureResult(resultFuture);
+ Set<String> set = new ArraySet<>();
+ if (resultList == null) {
+ return set;
+ }
+ set.addAll(resultList);
+ return set;
+ }
+
+ /**
* Allows the {@link TransportConnection} to notify this client
* if the underlying transport has become unusable. If that happens
* we want to cancel all active futures or callbacks.
diff --git a/services/companion/java/com/android/server/companion/securechannel/SecureChannel.java b/services/companion/java/com/android/server/companion/securechannel/SecureChannel.java
index dbeca82a..2d3782f 100644
--- a/services/companion/java/com/android/server/companion/securechannel/SecureChannel.java
+++ b/services/companion/java/com/android/server/companion/securechannel/SecureChannel.java
@@ -16,6 +16,8 @@
package com.android.server.companion.securechannel;
+import static android.security.attestationverification.AttestationVerificationManager.FLAG_FAILURE_UNKNOWN;
+
import android.annotation.NonNull;
import android.content.Context;
import android.os.Build;
@@ -67,7 +69,7 @@
private D2DConnectionContextV1 mConnectionContext;
private String mAlias;
- private int mVerificationResult;
+ private int mVerificationResult = FLAG_FAILURE_UNKNOWN;
private boolean mPskVerified;
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index fa22862..e57b009 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -65,6 +65,7 @@
import android.telephony.CellSignalStrengthNr;
import android.telephony.CellSignalStrengthTdscdma;
import android.telephony.CellSignalStrengthWcdma;
+import android.telephony.CellularIdentifierDisclosure;
import android.telephony.DisconnectCause;
import android.telephony.LinkCapacityEstimate;
import android.telephony.LocationAccessPolicy;
@@ -76,6 +77,7 @@
import android.telephony.PreciseDataConnectionState;
import android.telephony.PreciseDisconnectCause;
import android.telephony.Rlog;
+import android.telephony.SecurityAlgorithmUpdate;
import android.telephony.ServiceState;
import android.telephony.SignalStrength;
import android.telephony.SubscriptionInfo;
@@ -590,7 +592,9 @@
|| events.contains(TelephonyCallback.EVENT_ALLOWED_NETWORK_TYPE_LIST_CHANGED)
|| events.contains(TelephonyCallback.EVENT_EMERGENCY_CALLBACK_MODE_CHANGED)
|| events.contains(TelephonyCallback
- .EVENT_SIMULTANEOUS_CELLULAR_CALLING_SUBSCRIPTIONS_CHANGED);
+ .EVENT_SIMULTANEOUS_CELLULAR_CALLING_SUBSCRIPTIONS_CHANGED)
+ || events.contains(TelephonyCallback.EVENT_CELLULAR_IDENTIFIER_DISCLOSED_CHANGED)
+ || events.contains(TelephonyCallback.EVENT_SECURITY_ALGORITHMS_CHANGED);
}
private static final int MSG_USER_SWITCHED = 1;
@@ -897,7 +901,6 @@
mIsSatelliteEnabled = new AtomicBoolean();
mWasSatelliteEnabledNotified = new AtomicBoolean();
-
for (int i = 0; i < numPhones; i++) {
mCallState[i] = TelephonyManager.CALL_STATE_IDLE;
mDataActivity[i] = TelephonyManager.DATA_ACTIVITY_NONE;
@@ -3825,7 +3828,6 @@
}
}
-
/**
* Notify external listeners that carrier roaming non-terrestrial network
* signal strength changed.
@@ -3835,7 +3837,7 @@
public void notifyCarrierRoamingNtnSignalStrengthChanged(int subId,
@NonNull NtnSignalStrength ntnSignalStrength) {
if (!checkNotifyPermission("notifyCarrierRoamingNtnSignalStrengthChanged")) {
- log("nnotifyCarrierRoamingNtnSignalStrengthChanged: caller does not have required "
+ log("notifyCarrierRoamingNtnSignalStrengthChanged: caller does not have required "
+ "permissions.");
return;
}
@@ -3863,6 +3865,98 @@
}
}
+ /**
+ * Notify that the radio security algorithms have changed.
+ *
+ * @param phoneId the phone id.
+ * @param subId the subId.
+ * @param update the security algorithm update.
+ */
+ public void notifySecurityAlgorithmsChanged(int phoneId, int subId,
+ SecurityAlgorithmUpdate update) {
+ if (!Flags.securityAlgorithmsUpdateIndications()) {
+ log("Not available due to securityAlgorithmsUpdateIndications() flag");
+ return;
+ }
+ if (!checkNotifyPermission("notifySecurityAlgorithmChanged()")) {
+ return;
+ }
+
+ synchronized (mRecords) {
+ if (validatePhoneId(phoneId)) {
+ if (update == null) {
+ loge("SecurityAlgorithmUpdate is null, subId=" + subId
+ + ", phoneId=" + phoneId);
+ // Listeners shouldn't be updated for null updates.
+ return;
+ }
+
+ for (Record r : mRecords) {
+ if (r.matchTelephonyCallbackEvent(
+ TelephonyCallback.EVENT_SECURITY_ALGORITHMS_CHANGED)
+ && idMatch(r, subId, phoneId)) {
+ try {
+ if (VDBG) {
+ log("notifySecurityAlgorithmsChanged: securityAlgorithmUpdate= "
+ + update);
+ }
+ r.callback.onSecurityAlgorithmsChanged(update);
+ } catch (RemoteException ex) {
+ mRemoveList.add(r.binder);
+ }
+ }
+ }
+ }
+ handleRemoveListLocked();
+ }
+ }
+
+ /**
+ * Notify of a cellular identifier disclosure.
+ *
+ * @param phoneId the phone id.
+ * @param subId the subId.
+ * @param disclosure the cellular identifier disclosure.
+ */
+ public void notifyCellularIdentifierDisclosedChanged(int phoneId, int subId,
+ @NonNull CellularIdentifierDisclosure disclosure) {
+ if (!Flags.cellularIdentifierDisclosureIndications()) {
+ log("Not available due to cellularIdentifierDisclosureIndications() flag");
+ return;
+ }
+ if (!checkNotifyPermission("notifyCellularIdentifierDisclosedChanged()")) {
+ return;
+ }
+
+ synchronized (mRecords) {
+ if (validatePhoneId(phoneId)) {
+ if (disclosure == null) {
+ loge("CellularIdentifierDisclosure is null, subId=" + subId
+ + ", phoneId=" + phoneId);
+ // Listeners shouldn't be updated for null disclosures.
+ return;
+ }
+
+ for (Record r : mRecords) {
+ if (r.matchTelephonyCallbackEvent(
+ TelephonyCallback.EVENT_CELLULAR_IDENTIFIER_DISCLOSED_CHANGED)
+ && idMatch(r, subId, phoneId)) {
+ try {
+ if (VDBG) {
+ log("notifyCellularIdentifierDisclosedChanged: disclosure= "
+ + disclosure);
+ }
+ r.callback.onCellularIdentifierDisclosedChanged(disclosure);
+ } catch (RemoteException ex) {
+ mRemoveList.add(r.binder);
+ }
+ }
+ }
+ }
+ handleRemoveListLocked();
+ }
+ }
+
@NeverCompile // Avoid size overhead of debugging code.
@Override
public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index 679c7ac..3ce6451 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -16,6 +16,10 @@
package com.android.server.accounts;
+import static android.Manifest.permission.COPY_ACCOUNTS;
+import static android.Manifest.permission.REMOVE_ACCOUNTS;
+import static android.app.admin.flags.Flags.splitCreateManagedProfileEnabled;
+
import android.Manifest;
import android.accounts.AbstractAccountAuthenticator;
import android.accounts.Account;
@@ -1739,9 +1743,11 @@
public void copyAccountToUser(final IAccountManagerResponse response, final Account account,
final int userFrom, int userTo) {
int callingUid = Binder.getCallingUid();
- if (isCrossUser(callingUid, UserHandle.USER_ALL)) {
+ if (isCrossUser(callingUid, UserHandle.USER_ALL)
+ && !hasCopyAccountsPermission()) {
throw new SecurityException("Calling copyAccountToUser requires "
- + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
+ + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL
+ + " or " + COPY_ACCOUNTS);
}
final UserAccounts fromAccounts = getUserAccounts(userFrom);
final UserAccounts toAccounts = getUserAccounts(userTo);
@@ -1793,6 +1799,12 @@
}
}
+ private boolean hasCopyAccountsPermission() {
+ return splitCreateManagedProfileEnabled()
+ && mContext.checkCallingOrSelfPermission(COPY_ACCOUNTS)
+ == PackageManager.PERMISSION_GRANTED;
+ }
+
@Override
public boolean accountAuthenticated(final Account account) {
final int callingUid = Binder.getCallingUid();
@@ -2346,7 +2358,8 @@
UserHandle user = UserHandle.of(userId);
if (!isAccountManagedByCaller(account.type, callingUid, user.getIdentifier())
&& !isSystemUid(callingUid)
- && !isProfileOwner(callingUid)) {
+ && !isProfileOwner(callingUid)
+ && !hasRemoveAccountsPermission()) {
String msg = String.format(
"uid %s cannot remove accounts of type: %s",
callingUid,
@@ -2408,6 +2421,12 @@
}
}
+ private boolean hasRemoveAccountsPermission() {
+ return splitCreateManagedProfileEnabled()
+ && mContext.checkCallingOrSelfPermission(REMOVE_ACCOUNTS)
+ == PackageManager.PERMISSION_GRANTED;
+ }
+
@Override
public boolean removeAccountExplicitly(Account account) {
final int callingUid = Binder.getCallingUid();
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index dfddc08..e166807 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -478,7 +478,6 @@
import dalvik.annotation.optimization.NeverCompile;
import dalvik.system.VMRuntime;
-
import libcore.util.EmptyArray;
import java.io.File;
@@ -4493,16 +4492,11 @@
Slog.w(TAG, "Unattached app died before backup, skipping");
final int userId = app.userId;
final String packageName = app.info.packageName;
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- try {
- IBackupManager bm = IBackupManager.Stub.asInterface(
- ServiceManager.getService(Context.BACKUP_SERVICE));
- bm.agentDisconnectedForUser(userId, packageName);
- } catch (RemoteException e) {
- // Can't happen; the backup manager is local
- }
+ mHandler.post(() -> {
+ try {
+ getBackupManager().agentDisconnectedForUser(userId, packageName);
+ } catch (RemoteException e) {
+ // Can't happen; the backup manager is local
}
});
}
@@ -4673,7 +4667,8 @@
if (backupTarget != null && backupTarget.appInfo.packageName.equals(processName)) {
isRestrictedBackupMode = backupTarget.appInfo.uid >= FIRST_APPLICATION_UID
&& ((backupTarget.backupMode == BackupRecord.RESTORE_FULL)
- || (backupTarget.backupMode == BackupRecord.BACKUP_FULL));
+ || (backupTarget.backupMode == BackupRecord.BACKUP_FULL))
+ && backupTarget.useRestrictedMode;
}
final ActiveInstrumentation instr = app.getActiveInstrumentation();
@@ -13499,16 +13494,11 @@
if (backupTarget != null && pid == backupTarget.app.getPid()) {
if (DEBUG_BACKUP || DEBUG_CLEANUP) Slog.d(TAG_CLEANUP, "App "
+ backupTarget.appInfo + " died during backup");
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- try {
- IBackupManager bm = IBackupManager.Stub.asInterface(
- ServiceManager.getService(Context.BACKUP_SERVICE));
- bm.agentDisconnectedForUser(app.userId, app.info.packageName);
- } catch (RemoteException e) {
- // can't happen; backup manager is local
- }
+ mHandler.post(() -> {
+ try {
+ getBackupManager().agentDisconnectedForUser(app.userId, app.info.packageName);
+ } catch (RemoteException e) {
+ // can't happen; backup manager is local
}
});
}
@@ -14011,7 +14001,7 @@
// instantiated. The backup agent will invoke backupAgentCreated() on the
// activity manager to announce its creation.
public boolean bindBackupAgent(String packageName, int backupMode, int targetUserId,
- @BackupDestination int backupDestination) {
+ @BackupDestination int backupDestination, boolean useRestrictedMode) {
long startTimeNs = SystemClock.uptimeNanos();
if (DEBUG_BACKUP) {
Slog.v(TAG, "bindBackupAgent: app=" + packageName + " mode=" + backupMode
@@ -14096,7 +14086,8 @@
+ app.packageName + ": " + e);
}
- BackupRecord r = new BackupRecord(app, backupMode, targetUserId, backupDestination);
+ BackupRecord r = new BackupRecord(app, backupMode, targetUserId, backupDestination,
+ useRestrictedMode);
ComponentName hostingName =
(backupMode == ApplicationThreadConstants.BACKUP_MODE_INCREMENTAL
|| backupMode == ApplicationThreadConstants.BACKUP_MODE_RESTORE)
@@ -14122,8 +14113,9 @@
// process, etc, then mark it as being in full backup so that certain calls to the
// process can be blocked. This is not reset to false anywhere because we kill the
// process after the full backup is done and the ProcessRecord will vaporize anyway.
- if (UserHandle.isApp(app.uid) &&
- backupMode == ApplicationThreadConstants.BACKUP_MODE_FULL) {
+ if (UserHandle.isApp(app.uid)
+ && backupMode == ApplicationThreadConstants.BACKUP_MODE_FULL
+ && r.useRestrictedMode) {
proc.setInFullBackup(true);
}
r.app = proc;
@@ -14221,9 +14213,7 @@
final long oldIdent = Binder.clearCallingIdentity();
try {
- IBackupManager bm = IBackupManager.Stub.asInterface(
- ServiceManager.getService(Context.BACKUP_SERVICE));
- bm.agentConnectedForUser(userId, agentPackageName, agent);
+ getBackupManager().agentConnectedForUser(userId, agentPackageName, agent);
} catch (RemoteException e) {
// can't happen; the backup manager service is local
} catch (Exception e) {
@@ -18013,14 +18003,6 @@
@Override
public void addStartInfoTimestamp(int key, long timestampNs, int uid, int pid,
int userId) {
- // For the simplification, we don't support USER_ALL nor USER_CURRENT here.
- if (userId == UserHandle.USER_ALL || userId == UserHandle.USER_CURRENT) {
- throw new IllegalArgumentException("Unsupported userId");
- }
-
- mUserController.handleIncomingUser(pid, uid, userId, true,
- ALLOW_NON_FULL, "addStartInfoTimestampSystem", null);
-
addStartInfoTimestampInternal(key, timestampNs, userId, uid);
}
@@ -19088,8 +19070,13 @@
*/
@Override
public boolean enableFgsNotificationRateLimit(boolean enable) {
- enforceCallingPermission(permission.WRITE_DEVICE_CONFIG,
- "enableFgsNotificationRateLimit");
+ if (android.security.Flags.protectDeviceConfigFlags()) {
+ enforceCallingHasAtLeastOnePermission("enableFgsNotificationRateLimit",
+ permission.WRITE_DEVICE_CONFIG, permission.WRITE_ALLOWLISTED_DEVICE_CONFIG);
+ } else {
+ enforceCallingPermission(permission.WRITE_DEVICE_CONFIG,
+ "enableFgsNotificationRateLimit");
+ }
synchronized (this) {
return mServices.enableFgsNotificationRateLimitLocked(enable);
}
@@ -19353,4 +19340,8 @@
}
return token;
}
+
+ private IBackupManager getBackupManager() {
+ return IBackupManager.Stub.asInterface(ServiceManager.getService(Context.BACKUP_SERVICE));
+ }
}
diff --git a/services/core/java/com/android/server/am/AppStartInfoTracker.java b/services/core/java/com/android/server/am/AppStartInfoTracker.java
index 3913d2f..961022b 100644
--- a/services/core/java/com/android/server/am/AppStartInfoTracker.java
+++ b/services/core/java/com/android/server/am/AppStartInfoTracker.java
@@ -634,12 +634,20 @@
}
final ApplicationStartInfo info = new ApplicationStartInfo(raw);
+ int uid = raw.getRealUid();
- AppStartInfoContainer container = mData.get(raw.getPackageName(), raw.getRealUid());
+ // Isolated process starts won't be reasonably accessible if stored by their uid, don't
+ // store them.
+ if (com.android.server.am.Flags.appStartInfoIsolatedProcess()
+ && UserHandle.isIsolated(uid)) {
+ return null;
+ }
+
+ AppStartInfoContainer container = mData.get(raw.getPackageName(), uid);
if (container == null) {
container = new AppStartInfoContainer(mAppStartInfoHistoryListSize);
- container.mUid = info.getRealUid();
- mData.put(raw.getPackageName(), raw.getRealUid(), container);
+ container.mUid = uid;
+ mData.put(raw.getPackageName(), uid, container);
}
container.addStartInfoLocked(info);
@@ -1010,6 +1018,17 @@
new AppStartInfoContainer(mAppStartInfoHistoryListSize);
int uid = container.readFromProto(proto, AppsStartInfoProto.Package.USERS,
pkgName);
+
+ // If the isolated process flag is enabled and the uid is that of an isolated
+ // process, then break early so that the container will not be added to mData.
+ // This is expected only as a one time mitigation, records added after this flag
+ // is enabled should always return false for isIsolated and thereby always
+ // continue on.
+ if (com.android.server.am.Flags.appStartInfoIsolatedProcess()
+ && UserHandle.isIsolated(uid)) {
+ break;
+ }
+
synchronized (mLock) {
mData.put(pkgName, uid, container);
}
diff --git a/services/core/java/com/android/server/am/BackupRecord.java b/services/core/java/com/android/server/am/BackupRecord.java
index 0b056d7..64cc6f0 100644
--- a/services/core/java/com/android/server/am/BackupRecord.java
+++ b/services/core/java/com/android/server/am/BackupRecord.java
@@ -32,15 +32,18 @@
final int userId; // user for which backup is performed
final int backupMode; // full backup / incremental / restore
@BackupDestination final int backupDestination; // see BackupAnnotations#BackupDestination
+ final boolean useRestrictedMode; // whether the app should be put into restricted backup mode
ProcessRecord app; // where this agent is running or null
// ----- Implementation -----
- BackupRecord(ApplicationInfo _appInfo, int _backupMode, int _userId, int _backupDestination) {
+ BackupRecord(ApplicationInfo _appInfo, int _backupMode, int _userId, int _backupDestination,
+ boolean _useRestrictedMode) {
appInfo = _appInfo;
backupMode = _backupMode;
userId = _userId;
backupDestination = _backupDestination;
+ useRestrictedMode = _useRestrictedMode;
}
public String toString() {
diff --git a/services/core/java/com/android/server/am/BroadcastController.java b/services/core/java/com/android/server/am/BroadcastController.java
index 446b367..c6cb67f 100644
--- a/services/core/java/com/android/server/am/BroadcastController.java
+++ b/services/core/java/com/android/server/am/BroadcastController.java
@@ -553,7 +553,7 @@
}
BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage, callerFeatureId,
receiverId, permission, callingUid, userId, instantApp, visibleToInstantApps,
- exported, mService.mPlatformCompat);
+ exported, callerApp.info, mService.mPlatformCompat);
if (rl.containsFilter(filter)) {
Slog.w(TAG, "Receiver with filter " + filter
+ " already registered for pid " + rl.pid
diff --git a/services/core/java/com/android/server/am/BroadcastFilter.java b/services/core/java/com/android/server/am/BroadcastFilter.java
index 3c7fb52..a32d3cb 100644
--- a/services/core/java/com/android/server/am/BroadcastFilter.java
+++ b/services/core/java/com/android/server/am/BroadcastFilter.java
@@ -16,10 +16,13 @@
package com.android.server.am;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledSince;
import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
+import android.os.Binder;
import android.os.UserHandle;
import android.util.PrintWriterPrinter;
import android.util.Printer;
@@ -40,7 +43,7 @@
@ChangeId
@EnabledSince(targetSdkVersion = android.os.Build.VERSION_CODES.BASE)
@VisibleForTesting
- static final long CHANGE_RESTRICT_PRIORITY_VALUES = 371309185L;
+ static final long RESTRICT_PRIORITY_VALUES = 371309185L;
// Back-pointer to the list this filter is in.
final ReceiverList receiverList;
@@ -54,11 +57,12 @@
final boolean visibleToInstantApp;
public final boolean exported;
final int initialPriority;
+ final ApplicationInfo applicationInfo;
BroadcastFilter(IntentFilter _filter, ReceiverList _receiverList,
String _packageName, String _featureId, String _receiverId, String _requiredPermission,
int _owningUid, int _userId, boolean _instantApp, boolean _visibleToInstantApp,
- boolean _exported, PlatformCompat platformCompat) {
+ boolean _exported, ApplicationInfo _applicationInfo, PlatformCompat platformCompat) {
super(_filter);
receiverList = _receiverList;
packageName = _packageName;
@@ -70,8 +74,10 @@
instantApp = _instantApp;
visibleToInstantApp = _visibleToInstantApp;
exported = _exported;
+ applicationInfo = _applicationInfo;
initialPriority = getPriority();
- setPriority(calculateAdjustedPriority(owningUid, initialPriority, platformCompat));
+ setPriority(calculateAdjustedPriority(owningUid, initialPriority,
+ applicationInfo, platformCompat));
}
public @Nullable String getReceiverClassName() {
@@ -84,6 +90,10 @@
return null;
}
+ public @NonNull ApplicationInfo getApplicationInfo() {
+ return applicationInfo;
+ }
+
@NeverCompile
public void dumpDebug(ProtoOutputStream proto, long fieldId) {
long token = proto.start(fieldId);
@@ -125,13 +135,18 @@
@VisibleForTesting
static int calculateAdjustedPriority(int owningUid, int priority,
- PlatformCompat platformCompat) {
+ ApplicationInfo applicationInfo, PlatformCompat platformCompat) {
if (!Flags.restrictPriorityValues()) {
return priority;
}
- if (!platformCompat.isChangeEnabledByUidInternalNoLogging(
- CHANGE_RESTRICT_PRIORITY_VALUES, owningUid)) {
- return priority;
+ final long token = Binder.clearCallingIdentity();
+ try {
+ if (!platformCompat.isChangeEnabledInternalNoLogging(
+ RESTRICT_PRIORITY_VALUES, applicationInfo)) {
+ return priority;
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
}
if (!UserHandle.isCore(owningUid)) {
if (priority >= SYSTEM_HIGH_PRIORITY) {
diff --git a/services/core/java/com/android/server/am/BroadcastRecord.java b/services/core/java/com/android/server/am/BroadcastRecord.java
index 38df10a..a1ab1eea 100644
--- a/services/core/java/com/android/server/am/BroadcastRecord.java
+++ b/services/core/java/com/android/server/am/BroadcastRecord.java
@@ -49,6 +49,7 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
import android.content.pm.ResolveInfo;
import android.os.Binder;
import android.os.Bundle;
@@ -57,7 +58,6 @@
import android.util.ArrayMap;
import android.util.IntArray;
import android.util.PrintWriterPrinter;
-import android.util.SparseBooleanArray;
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
@@ -88,7 +88,7 @@
@ChangeId
@EnabledSince(targetSdkVersion = android.os.Build.VERSION_CODES.BASE)
@VisibleForTesting
- static final long CHANGE_LIMIT_PRIORITY_SCOPE = 371307720L;
+ static final long LIMIT_PRIORITY_SCOPE = 371307720L;
final @NonNull Intent intent; // the original intent that generated us
final @Nullable ComponentName targetComp; // original component name set on the intent
@@ -781,7 +781,7 @@
} else {
if (Flags.limitPriorityScope()) {
final boolean[] changeEnabled = calculateChangeStateForReceivers(
- receivers, CHANGE_LIMIT_PRIORITY_SCOPE, platformCompat);
+ receivers, LIMIT_PRIORITY_SCOPE, platformCompat);
// Priority of the previous tranche
int lastTranchePriority = 0;
@@ -865,25 +865,35 @@
@VisibleForTesting
static @NonNull boolean[] calculateChangeStateForReceivers(@NonNull List<Object> receivers,
long changeId, PlatformCompat platformCompat) {
- final SparseBooleanArray changeStateForUids = new SparseBooleanArray();
+ // TODO: b/371307720 - Remove this method as we are already avoiding the packagemanager
+ // calls by checking the changeId state using ApplicationInfos.
+ final ArrayMap<String, Boolean> changeStates = new ArrayMap<>();
final int count = receivers.size();
final boolean[] changeStateForReceivers = new boolean[count];
for (int i = 0; i < count; ++i) {
- final int receiverUid = getReceiverUid(receivers.get(i));
+ final ApplicationInfo receiverAppInfo = getReceiverAppInfo(receivers.get(i));
final boolean isChangeEnabled;
- final int idx = changeStateForUids.indexOfKey(receiverUid);
+ final int idx = changeStates.indexOfKey(receiverAppInfo.packageName);
if (idx >= 0) {
- isChangeEnabled = changeStateForUids.valueAt(idx);
+ isChangeEnabled = changeStates.valueAt(idx);
} else {
- isChangeEnabled = platformCompat.isChangeEnabledByUidInternalNoLogging(
- changeId, receiverUid);
- changeStateForUids.put(receiverUid, isChangeEnabled);
+ isChangeEnabled = platformCompat.isChangeEnabledInternalNoLogging(
+ changeId, receiverAppInfo);
+ changeStates.put(receiverAppInfo.packageName, isChangeEnabled);
}
changeStateForReceivers[i] = isChangeEnabled;
}
return changeStateForReceivers;
}
+ static ApplicationInfo getReceiverAppInfo(@NonNull Object receiver) {
+ if (receiver instanceof BroadcastFilter) {
+ return ((BroadcastFilter) receiver).getApplicationInfo();
+ } else {
+ return ((ResolveInfo) receiver).activityInfo.applicationInfo;
+ }
+ }
+
static int getReceiverUid(@NonNull Object receiver) {
if (receiver instanceof BroadcastFilter) {
return ((BroadcastFilter) receiver).owningUid;
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index b51db13..98f738c 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -351,7 +351,8 @@
private String[] mIsolatedEntryPointArgs;
/**
- * Process is currently hosting a backup agent for backup or restore.
+ * Process is currently hosting a backup agent for backup or restore. Note that this is only set
+ * when the process is put into restricted backup mode.
*/
@GuardedBy("mService")
private boolean mInFullBackup;
diff --git a/services/core/java/com/android/server/am/ProcessServiceRecord.java b/services/core/java/com/android/server/am/ProcessServiceRecord.java
index 3644974..14d3fbc 100644
--- a/services/core/java/com/android/server/am/ProcessServiceRecord.java
+++ b/services/core/java/com/android/server/am/ProcessServiceRecord.java
@@ -393,6 +393,8 @@
adj = ProcessList.PERCEPTIBLE_APP_ADJ;
} else if (adj < ProcessList.PERCEPTIBLE_LOW_APP_ADJ) {
adj = ProcessList.PERCEPTIBLE_LOW_APP_ADJ;
+ } else if (Flags.addModifyRawOomAdjServiceLevel() && adj < ProcessList.SERVICE_ADJ) {
+ adj = ProcessList.SERVICE_ADJ;
} else if (adj < ProcessList.CACHED_APP_MIN_ADJ) {
adj = ProcessList.CACHED_APP_MIN_ADJ;
} else if (adj < ProcessList.CACHED_APP_MAX_ADJ) {
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index ef5296e..78c4f74 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -257,6 +257,7 @@
"wear_systems",
"wear_sysui",
"wear_system_managed_surfaces",
+ "wear_watchfaces",
"window_surfaces",
"windowing_frontend",
"xr",
diff --git a/services/core/java/com/android/server/am/flags.aconfig b/services/core/java/com/android/server/am/flags.aconfig
index 711b163..6d247d2 100644
--- a/services/core/java/com/android/server/am/flags.aconfig
+++ b/services/core/java/com/android/server/am/flags.aconfig
@@ -260,3 +260,23 @@
description: "Use PROCESS_CAPABILITY_CPU_TIME to control unfreeze state."
bug: "370817323"
}
+
+flag {
+ name: "add_modify_raw_oom_adj_service_level"
+ namespace: "backstage_power"
+ description: "Add a SERVICE_ADJ level to the modifyRawOomAdj method"
+ bug: "374810368"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
+ name: "app_start_info_isolated_process"
+ namespace: "system_performance"
+ description: "Adjust handling of isolated process records to be discarded."
+ bug: "374032823"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 5e74d67..06c586f 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -2876,7 +2876,8 @@
}
@Override
- public int checkOperationForDevice(int code, int uid, String packageName, int virtualDeviceId) {
+ public int checkOperationForDevice(int code, int uid, String packageName,
+ @Nullable String attributionTag, int virtualDeviceId) {
if (Binder.getCallingPid() != Process.myPid()
&& Flags.appopAccessTrackingLoggingEnabled()) {
FrameworkStatsLog.write(
@@ -2884,7 +2885,7 @@
APP_OP_NOTE_OP_OR_CHECK_OP_BINDER_API_CALLED__BINDER_API__CHECK_OPERATION,
false);
}
- return mCheckOpsDelegateDispatcher.checkOperation(code, uid, packageName, null,
+ return mCheckOpsDelegateDispatcher.checkOperation(code, uid, packageName, attributionTag,
virtualDeviceId, false /*raw*/);
}
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 6ba3569..6e6bf80 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -9224,6 +9224,9 @@
return;
}
+ // index values sent to APM are in the stream type SDK range, not *10
+ int indexMinVolCurve = MIN_STREAM_VOLUME[mStreamType];
+ int indexMaxVolCurve = MAX_STREAM_VOLUME[mStreamType];
synchronized (this) {
if (mStreamType == AudioSystem.STREAM_VOICE_CALL) {
if (MAX_STREAM_VOLUME[AudioSystem.STREAM_BLUETOOTH_SCO]
@@ -9234,11 +9237,15 @@
if (!equalScoLeaVcIndexRange() && isStreamBluetoothSco(mStreamType)) {
// SCO devices have a different min index
mIndexMin = MIN_STREAM_VOLUME[AudioSystem.STREAM_BLUETOOTH_SCO] * 10;
+ indexMinVolCurve = MIN_STREAM_VOLUME[AudioSystem.STREAM_BLUETOOTH_SCO];
+ indexMaxVolCurve = MAX_STREAM_VOLUME[AudioSystem.STREAM_BLUETOOTH_SCO];
mIndexStepFactor = 1.f;
} else if (equalScoLeaVcIndexRange() && isStreamBluetoothComm(mStreamType)) {
// For non SCO devices the stream state does not change the min index
if (mBtCommDeviceActive.get() == BT_COMM_DEVICE_ACTIVE_SCO) {
mIndexMin = MIN_STREAM_VOLUME[AudioSystem.STREAM_BLUETOOTH_SCO] * 10;
+ indexMinVolCurve = MIN_STREAM_VOLUME[AudioSystem.STREAM_BLUETOOTH_SCO];
+ indexMaxVolCurve = MAX_STREAM_VOLUME[AudioSystem.STREAM_BLUETOOTH_SCO];
} else {
mIndexMin = MIN_STREAM_VOLUME[mStreamType] * 10;
}
@@ -9259,7 +9266,7 @@
}
final int status = AudioSystem.initStreamVolume(
- mStreamType, mIndexMin / 10, mIndexMax / 10);
+ mStreamType, indexMinVolCurve, indexMaxVolCurve);
sVolumeLogger.enqueue(new EventLogger.StringEvent(
"updateIndexFactors() stream:" + mStreamType + " index min/max:"
+ mIndexMin / 10 + "/" + mIndexMax / 10 + " indexStepFactor:"
@@ -14159,10 +14166,10 @@
* Update player event
* @param piid Player id to update
* @param event The new player event
- * @param eventValue The value associated with this event
+ * @param eventValues The values associated with this event
*/
- public void playerEvent(int piid, int event, int eventValue) {
- mPlaybackMonitor.playerEvent(piid, event, eventValue, Binder.getCallingUid());
+ public void playerEvent(int piid, int event, int[] eventValues) {
+ mPlaybackMonitor.playerEvent(piid, event, eventValues, Binder.getCallingUid());
}
/**
diff --git a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
index b63b07f..a62ac82 100644
--- a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
+++ b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
@@ -20,6 +20,7 @@
import static android.media.AudioPlaybackConfiguration.MUTED_BY_APP_OPS;
import static android.media.AudioPlaybackConfiguration.MUTED_BY_CLIENT_VOLUME;
import static android.media.AudioPlaybackConfiguration.MUTED_BY_MASTER;
+import static android.media.AudioPlaybackConfiguration.MUTED_BY_PORT_VOLUME;
import static android.media.AudioPlaybackConfiguration.MUTED_BY_STREAM_MUTED;
import static android.media.AudioPlaybackConfiguration.MUTED_BY_STREAM_VOLUME;
import static android.media.AudioPlaybackConfiguration.MUTED_BY_VOLUME_SHAPER;
@@ -364,10 +365,11 @@
* @param eventValue The value associated with this event
* @param binderUid Calling binder uid
*/
- public void playerEvent(int piid, int event, int eventValue, int binderUid) {
+ public void playerEvent(int piid, int event, int[] eventValues, int binderUid) {
if (DEBUG) {
- Log.v(TAG, TextUtils.formatSimple("playerEvent(piid=%d, event=%s, eventValue=%d)",
- piid, AudioPlaybackConfiguration.playerStateToString(event), eventValue));
+ Log.v(TAG, TextUtils.formatSimple("playerEvent(piid=%d, event=%s, eventValues=%d)",
+ piid, AudioPlaybackConfiguration.playerStateToString(event),
+ Arrays.toString(eventValues)));
}
boolean change;
synchronized(mPlayerLock) {
@@ -381,13 +383,13 @@
// do not log nor dispatch events for "ignored" players other than the release
return;
}
- sEventLogger.enqueue(new PlayerEvent(piid, event, eventValue));
+ sEventLogger.enqueue(new PlayerEvent(piid, event, eventValues));
if (event == AudioPlaybackConfiguration.PLAYER_UPDATE_PORT_ID) {
if (portToPiidSimplification()) {
- mPiidToPortId.put(piid, eventValue);
+ mPiidToPortId.put(piid, eventValues[0]);
} else {
- mPortIdToPiid.put(eventValue, piid);
+ mPortIdToPiid.put(eventValues[0], piid);
}
return;
} else if (event == AudioPlaybackConfiguration.PLAYER_STATE_STARTED) {
@@ -408,7 +410,7 @@
if (checkConfigurationCaller(piid, apc, binderUid)) {
//TODO add generation counter to only update to the latest state
checkVolumeForPrivilegedAlarm(apc, event);
- change = apc.handleStateEvent(event, eventValue);
+ change = apc.handleStateEvent(event, eventValues);
} else {
Log.e(TAG, "Error handling event " + event);
change = false;
@@ -444,7 +446,7 @@
}
if (DEBUG) {
- Log.v(TAG, TextUtils.formatSimple("BLA portEvent(portId=%d, event=%s, extras=%s)",
+ Log.v(TAG, TextUtils.formatSimple("portEvent(portId=%d, event=%s, extras=%s)",
portId, AudioPlaybackConfiguration.playerStateToString(event), extras));
}
@@ -516,7 +518,7 @@
mMutedPlayersAwaitingConnection.remove(Integer.valueOf(piid));
checkVolumeForPrivilegedAlarm(apc, AudioPlaybackConfiguration.PLAYER_STATE_RELEASED);
change = apc.handleStateEvent(AudioPlaybackConfiguration.PLAYER_STATE_RELEASED,
- AudioPlaybackConfiguration.PLAYER_DEVICEID_INVALID);
+ AudioPlaybackConfiguration.PLAYER_DEVICEIDS_INVALID);
if (portToPiidSimplification()) {
mPiidToPortId.delete(piid);
@@ -1335,12 +1337,12 @@
// only keeping the player interface ID as it uniquely identifies the player in the event
final int mPlayerIId;
final int mEvent;
- final int mEventValue;
+ final int[] mEventValues;
- PlayerEvent(int piid, int event, int eventValue) {
+ PlayerEvent(int piid, int event, int[] eventValues) {
mPlayerIId = piid;
mEvent = event;
- mEventValue = eventValue;
+ mEventValues = eventValues;
}
@Override
@@ -1352,35 +1354,40 @@
switch (mEvent) {
case AudioPlaybackConfiguration.PLAYER_UPDATE_PORT_ID:
return AudioPlaybackConfiguration.toLogFriendlyPlayerState(mEvent) + " portId:"
- + mEventValue + " mapped to player piid:" + mPlayerIId;
+ + Arrays.toString(mEventValues) + " mapped to player piid:"
+ + mPlayerIId;
case AudioPlaybackConfiguration.PLAYER_UPDATE_DEVICE_ID:
- if (mEventValue != 0) {
- builder.append(" deviceId:").append(mEventValue);
+ if ((mEventValues.length > 0) && (mEventValues[0] != 0)) {
+ builder.append(" deviceIds:").append(Arrays.toString(mEventValues));
}
return builder.toString();
case AudioPlaybackConfiguration.PLAYER_UPDATE_MUTED:
builder.append(" source:");
- if (mEventValue <= 0) {
+ int eventValue = mEventValues[0];
+ if (eventValue <= 0) {
builder.append("none ");
} else {
- if ((mEventValue & MUTED_BY_MASTER) != 0) {
+ if ((eventValue & MUTED_BY_MASTER) != 0) {
builder.append("masterMute ");
}
- if ((mEventValue & MUTED_BY_STREAM_VOLUME) != 0) {
+ if ((eventValue & MUTED_BY_STREAM_VOLUME) != 0) {
builder.append("streamVolume ");
}
- if ((mEventValue & MUTED_BY_STREAM_MUTED) != 0) {
+ if ((eventValue & MUTED_BY_STREAM_MUTED) != 0) {
builder.append("streamMute ");
}
- if ((mEventValue & MUTED_BY_APP_OPS) != 0) {
+ if ((eventValue & MUTED_BY_APP_OPS) != 0) {
builder.append("appOps ");
}
- if ((mEventValue & MUTED_BY_CLIENT_VOLUME) != 0) {
+ if ((eventValue & MUTED_BY_CLIENT_VOLUME) != 0) {
builder.append("clientVolume ");
}
- if ((mEventValue & MUTED_BY_VOLUME_SHAPER) != 0) {
+ if ((eventValue & MUTED_BY_VOLUME_SHAPER) != 0) {
builder.append("volumeShaper ");
}
+ if ((eventValue & MUTED_BY_PORT_VOLUME) != 0) {
+ builder.append("portVolume ");
+ }
}
return builder.toString();
default:
@@ -1728,8 +1735,11 @@
synchronized (mPlayerLock) {
int piid = msg.arg1;
+
+ int[] eventValues = new int[1];
+ eventValues[0] = eventValue;
sEventLogger.enqueue(
- new PlayerEvent(piid, PLAYER_UPDATE_MUTED, eventValue));
+ new PlayerEvent(piid, PLAYER_UPDATE_MUTED, eventValues));
final AudioPlaybackConfiguration apc = mPlayers.get(piid);
if (apc == null || !apc.handleMutedEvent(eventValue)) {
diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java
index 6578023..97a8854 100644
--- a/services/core/java/com/android/server/biometrics/BiometricService.java
+++ b/services/core/java/com/android/server/biometrics/BiometricService.java
@@ -101,6 +101,7 @@
import java.util.Random;
import java.util.Set;
import java.util.concurrent.atomic.AtomicLong;
+import java.util.concurrent.CopyOnWriteArrayList;
import java.util.function.Supplier;
/**
@@ -132,7 +133,7 @@
IGateKeeperService mGateKeeper;
// Get and cache the available biometric authenticators and their associated info.
- final ArrayList<BiometricSensor> mSensors = new ArrayList<>();
+ final CopyOnWriteArrayList<BiometricSensor> mSensors = new CopyOnWriteArrayList<>();
@VisibleForTesting
BiometricStrengthController mBiometricStrengthController;
@@ -156,13 +157,13 @@
@NonNull private final Set<Integer> mSensorsPendingInvalidation;
public static InvalidationTracker start(@NonNull Context context,
- @NonNull ArrayList<BiometricSensor> sensors,
- int userId, int fromSensorId, @NonNull IInvalidationCallback clientCallback) {
+ @NonNull List<BiometricSensor> sensors, int userId,
+ int fromSensorId, @NonNull IInvalidationCallback clientCallback) {
return new InvalidationTracker(context, sensors, userId, fromSensorId, clientCallback);
}
private InvalidationTracker(@NonNull Context context,
- @NonNull ArrayList<BiometricSensor> sensors, int userId,
+ @NonNull List<BiometricSensor> sensors, int userId,
int fromSensorId, @NonNull IInvalidationCallback clientCallback) {
mClientCallback = clientCallback;
mSensorsPendingInvalidation = new ArraySet<>();
@@ -879,7 +880,7 @@
@android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL)
@Override
- public synchronized void registerAuthenticator(int id, int modality,
+ public void registerAuthenticator(int id, int modality,
@Authenticators.Types int strength,
@NonNull IBiometricAuthenticator authenticator) {
diff --git a/services/core/java/com/android/server/biometrics/PreAuthInfo.java b/services/core/java/com/android/server/biometrics/PreAuthInfo.java
index afdc0c0..6ed1ac85 100644
--- a/services/core/java/com/android/server/biometrics/PreAuthInfo.java
+++ b/services/core/java/com/android/server/biometrics/PreAuthInfo.java
@@ -226,10 +226,6 @@
return BIOMETRIC_NO_HARDWARE;
}
- if (sensor.modality == TYPE_FACE && biometricCameraManager.isAnyCameraUnavailable()) {
- return BIOMETRIC_HARDWARE_NOT_DETECTED;
- }
-
final boolean wasStrongEnough =
Utils.isAtLeastStrength(sensor.oemStrength, requestedStrength);
final boolean isStrongEnough =
@@ -241,6 +237,10 @@
return BIOMETRIC_INSUFFICIENT_STRENGTH;
}
+ if (sensor.modality == TYPE_FACE && biometricCameraManager.isAnyCameraUnavailable()) {
+ return BIOMETRIC_HARDWARE_NOT_DETECTED;
+ }
+
try {
if (!sensor.impl.isHardwareDetected(opPackageName)) {
return BIOMETRIC_HARDWARE_NOT_DETECTED;
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 82d5d4d..e8786be 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
@@ -677,10 +677,11 @@
* Start the timeout for the watchdog.
*/
public void startWatchdog() {
- if (mCurrentOperation == null) {
+ final BiometricSchedulerOperation operation = mCurrentOperation;
+ if (operation == null) {
+ Slog.e(TAG, "Current operation is null,no need to start watchdog");
return;
}
- final BiometricSchedulerOperation operation = mCurrentOperation;
mHandler.postDelayed(() -> {
if (operation == mCurrentOperation && !operation.isFinished()) {
Counter.logIncrement("biometric.value_scheduler_watchdog_triggered_count");
diff --git a/services/core/java/com/android/server/compat/PlatformCompat.java b/services/core/java/com/android/server/compat/PlatformCompat.java
index eeac260..6feae34 100644
--- a/services/core/java/com/android/server/compat/PlatformCompat.java
+++ b/services/core/java/com/android/server/compat/PlatformCompat.java
@@ -522,7 +522,7 @@
// b/282922910 - we don't want apps sharing system uid and targeting
// older target sdk to impact all system uid apps
if (Flags.systemUidTargetSystemSdk() && !mIsWear &&
- uid == Process.SYSTEM_UID) {
+ uid == Process.SYSTEM_UID && appInfo != null) {
appInfo.targetSdkVersion = Build.VERSION.SDK_INT;
}
return appInfo;
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 5a2610b..62fcccf 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -2460,6 +2460,15 @@
DisplayManagerGlobal.EVENT_DISPLAY_HDR_SDR_RATIO_CHANGED);
}
+ private void handleLogicalDisplayRefreshRateChangedLocked(@NonNull LogicalDisplay display) {
+ sendDisplayEventIfEnabledLocked(display,
+ DisplayManagerGlobal.EVENT_DISPLAY_REFRESH_RATE_CHANGED);
+ }
+
+ private void handleLogicalDisplayStateChangedLocked(@NonNull LogicalDisplay display) {
+ sendDisplayEventIfEnabledLocked(display, DisplayManagerGlobal.EVENT_DISPLAY_STATE_CHANGED);
+ }
+
private void notifyDefaultDisplayDeviceUpdated(LogicalDisplay display) {
mDisplayModeDirector.defaultDisplayDeviceUpdated(display.getPrimaryDisplayDeviceLocked()
.mDisplayDeviceConfig);
@@ -3991,6 +4000,12 @@
case LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_DISCONNECTED:
handleLogicalDisplayDisconnectedLocked(display);
break;
+ case LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_REFRESH_RATE_CHANGED:
+ handleLogicalDisplayRefreshRateChangedLocked(display);
+ break;
+ case LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_STATE_CHANGED:
+ handleLogicalDisplayStateChangedLocked(display);
+ break;
}
}
@@ -4198,6 +4213,13 @@
return (mask
& DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_CONNECTION_CHANGED)
!= 0;
+ case DisplayManagerGlobal.EVENT_DISPLAY_REFRESH_RATE_CHANGED:
+ return (mask
+ & DisplayManagerGlobal
+ .INTERNAL_EVENT_FLAG_DISPLAY_REFRESH_RATE) != 0;
+ case DisplayManagerGlobal.EVENT_DISPLAY_STATE_CHANGED:
+ return (mask & DisplayManagerGlobal
+ .INTERNAL_EVENT_FLAG_DISPLAY_STATE) != 0;
default:
// This should never happen.
Slog.e(TAG, "Unknown display event " + event);
@@ -5261,6 +5283,23 @@
}
@Override
+ public void setScreenBrightnessOverrideFromWindowManager(
+ SparseArray<DisplayBrightnessOverrideRequest> brightnessOverrides) {
+ SparseArray<DisplayPowerController> dpcs = new SparseArray<>();
+ synchronized (mSyncRoot) {
+ for (int i = 0; i < mDisplayPowerControllers.size(); i++) {
+ dpcs.put(mDisplayPowerControllers.keyAt(i),
+ mDisplayPowerControllers.valueAt(i));
+ }
+ }
+ for (int i = 0; i < dpcs.size(); ++i) {
+ final int displayId = dpcs.keyAt(i);
+ final DisplayPowerController dpc = dpcs.valueAt(i);
+ dpc.setBrightnessOverrideRequest(brightnessOverrides.get(displayId));
+ }
+ }
+
+ @Override
public boolean requestPowerState(int groupId, DisplayPowerRequest request,
boolean waitForNegativeProximity) {
synchronized (mSyncRoot) {
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index c90dfbf..2948ae4 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -42,6 +42,7 @@
import android.hardware.display.BrightnessChangeEvent;
import android.hardware.display.BrightnessConfiguration;
import android.hardware.display.BrightnessInfo;
+import android.hardware.display.DisplayManagerInternal;
import android.hardware.display.DisplayManagerInternal.DisplayOffloadSession;
import android.hardware.display.DisplayManagerInternal.DisplayPowerCallbacks;
import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest;
@@ -170,6 +171,7 @@
private static final int MSG_OFFLOADING_SCREEN_ON_UNBLOCKED = 18;
private static final int MSG_SET_STYLUS_BEING_USED = 19;
private static final int MSG_SET_STYLUS_USE_ENDED = 20;
+ private static final int MSG_SET_WINDOW_MANAGER_BRIGHTNESS_OVERRIDE = 21;
private static final int BRIGHTNESS_CHANGE_STATSD_REPORT_INTERVAL_MS = 500;
@@ -850,6 +852,12 @@
}
}
+ public void setBrightnessOverrideRequest(
+ DisplayManagerInternal.DisplayBrightnessOverrideRequest request) {
+ Message msg = mHandler.obtainMessage(MSG_SET_WINDOW_MANAGER_BRIGHTNESS_OVERRIDE, request);
+ mHandler.sendMessageAtTime(msg, mClock.uptimeMillis());
+ }
+
public void setDisplayOffloadSession(DisplayOffloadSession session) {
if (session == mDisplayOffloadSession) {
return;
@@ -3100,6 +3108,13 @@
updatePowerState();
break;
+ case MSG_SET_WINDOW_MANAGER_BRIGHTNESS_OVERRIDE:
+ if (mDisplayBrightnessController.updateWindowManagerBrightnessOverride(
+ (DisplayManagerInternal.DisplayBrightnessOverrideRequest) msg.obj)) {
+ updatePowerState();
+ }
+ break;
+
case MSG_STOP:
cleanupHandlerThreadAfterStop();
break;
diff --git a/services/core/java/com/android/server/display/LogicalDisplayMapper.java b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
index 09fa4e6..c0903a9 100644
--- a/services/core/java/com/android/server/display/LogicalDisplayMapper.java
+++ b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
@@ -79,15 +79,18 @@
// 'adb shell setprop persist.log.tag.LogicalDisplayMapper DEBUG && adb reboot'
private static final boolean DEBUG = DebugUtils.isDebuggable(TAG);
- public static final int LOGICAL_DISPLAY_EVENT_ADDED = 1;
- public static final int LOGICAL_DISPLAY_EVENT_CHANGED = 2;
- public static final int LOGICAL_DISPLAY_EVENT_REMOVED = 3;
- public static final int LOGICAL_DISPLAY_EVENT_SWAPPED = 4;
- public static final int LOGICAL_DISPLAY_EVENT_FRAME_RATE_OVERRIDES_CHANGED = 5;
- public static final int LOGICAL_DISPLAY_EVENT_DEVICE_STATE_TRANSITION = 6;
- public static final int LOGICAL_DISPLAY_EVENT_HDR_SDR_RATIO_CHANGED = 7;
- public static final int LOGICAL_DISPLAY_EVENT_CONNECTED = 8;
- public static final int LOGICAL_DISPLAY_EVENT_DISCONNECTED = 9;
+ public static final int LOGICAL_DISPLAY_EVENT_BASE = 0;
+ public static final int LOGICAL_DISPLAY_EVENT_ADDED = 1 << 0;
+ public static final int LOGICAL_DISPLAY_EVENT_CHANGED = 1 << 1;
+ public static final int LOGICAL_DISPLAY_EVENT_REMOVED = 1 << 2;
+ public static final int LOGICAL_DISPLAY_EVENT_SWAPPED = 1 << 3;
+ public static final int LOGICAL_DISPLAY_EVENT_FRAME_RATE_OVERRIDES_CHANGED = 1 << 4;
+ public static final int LOGICAL_DISPLAY_EVENT_DEVICE_STATE_TRANSITION = 1 << 5;
+ public static final int LOGICAL_DISPLAY_EVENT_HDR_SDR_RATIO_CHANGED = 1 << 6;
+ public static final int LOGICAL_DISPLAY_EVENT_CONNECTED = 1 << 7;
+ public static final int LOGICAL_DISPLAY_EVENT_DISCONNECTED = 1 << 8;
+ public static final int LOGICAL_DISPLAY_EVENT_REFRESH_RATE_CHANGED = 1 << 9;
+ public static final int LOGICAL_DISPLAY_EVENT_STATE_CHANGED = 1 << 10;
public static final int DISPLAY_GROUP_EVENT_ADDED = 1;
public static final int DISPLAY_GROUP_EVENT_CHANGED = 2;
@@ -804,6 +807,8 @@
final boolean wasPreviouslyUpdated = updateState != UPDATE_STATE_NEW;
final boolean wasPreviouslyEnabled = mDisplaysEnabledCache.get(displayId);
final boolean isCurrentlyEnabled = display.isEnabledLocked();
+ int logicalDisplayEventMask = mLogicalDisplaysToUpdate
+ .get(displayId, LOGICAL_DISPLAY_EVENT_BASE);
// The display is no longer valid and needs to be removed.
if (!display.isValidLocked()) {
@@ -821,20 +826,20 @@
if (mDisplaysEnabledCache.get(displayId)) {
// We still need to send LOGICAL_DISPLAY_EVENT_DISCONNECTED
reloop = true;
- mLogicalDisplaysToUpdate.put(displayId, LOGICAL_DISPLAY_EVENT_REMOVED);
+ logicalDisplayEventMask |= LOGICAL_DISPLAY_EVENT_REMOVED;
} else {
mUpdatedLogicalDisplays.delete(displayId);
- mLogicalDisplaysToUpdate.put(displayId,
- LOGICAL_DISPLAY_EVENT_DISCONNECTED);
+ logicalDisplayEventMask |= LOGICAL_DISPLAY_EVENT_DISCONNECTED;
}
} else {
mUpdatedLogicalDisplays.delete(displayId);
- mLogicalDisplaysToUpdate.put(displayId, LOGICAL_DISPLAY_EVENT_REMOVED);
+ logicalDisplayEventMask |= LOGICAL_DISPLAY_EVENT_REMOVED;
}
} else {
// This display never left this class, safe to remove without notification
mLogicalDisplays.removeAt(i);
}
+ mLogicalDisplaysToUpdate.put(displayId, logicalDisplayEventMask);
continue;
// The display is new.
@@ -842,38 +847,40 @@
if (mFlags.isConnectedDisplayManagementEnabled()) {
// We still need to send LOGICAL_DISPLAY_EVENT_ADDED
reloop = true;
- mLogicalDisplaysToUpdate.put(displayId, LOGICAL_DISPLAY_EVENT_CONNECTED);
+ logicalDisplayEventMask |= LOGICAL_DISPLAY_EVENT_CONNECTED;
} else {
- mLogicalDisplaysToUpdate.put(displayId, LOGICAL_DISPLAY_EVENT_ADDED);
+ logicalDisplayEventMask |= LOGICAL_DISPLAY_EVENT_ADDED;
}
// Underlying displays device has changed to a different one.
} else if (!TextUtils.equals(mTempDisplayInfo.uniqueId, newDisplayInfo.uniqueId)) {
- mLogicalDisplaysToUpdate.put(displayId, LOGICAL_DISPLAY_EVENT_SWAPPED);
+ logicalDisplayEventMask |= LOGICAL_DISPLAY_EVENT_SWAPPED;
// Something about the display device has changed.
} else if (mFlags.isConnectedDisplayManagementEnabled()
&& wasPreviouslyEnabled != isCurrentlyEnabled) {
int event = isCurrentlyEnabled ? LOGICAL_DISPLAY_EVENT_ADDED :
LOGICAL_DISPLAY_EVENT_REMOVED;
- mLogicalDisplaysToUpdate.put(displayId, event);
+ logicalDisplayEventMask |= event;
} else if (wasDirty || !mTempDisplayInfo.equals(newDisplayInfo)) {
// If only the hdr/sdr ratio changed, then send just the event for that case
if ((diff == DisplayDeviceInfo.DIFF_HDR_SDR_RATIO)) {
- mLogicalDisplaysToUpdate.put(displayId,
- LOGICAL_DISPLAY_EVENT_HDR_SDR_RATIO_CHANGED);
+ logicalDisplayEventMask |= LOGICAL_DISPLAY_EVENT_HDR_SDR_RATIO_CHANGED;
} else {
- mLogicalDisplaysToUpdate.put(displayId, LOGICAL_DISPLAY_EVENT_CHANGED);
+ logicalDisplayEventMask |= LOGICAL_DISPLAY_EVENT_CHANGED;
}
- // The display is involved in a display layout transition
+ if (mFlags.isDisplayListenerPerformanceImprovementsEnabled()) {
+ logicalDisplayEventMask
+ |= updateAndGetMaskForDisplayPropertyChanges(newDisplayInfo);
+ }
+
+ // The display is involved in a display layout transition
} else if (updateState == UPDATE_STATE_TRANSITION) {
- mLogicalDisplaysToUpdate.put(displayId,
- LOGICAL_DISPLAY_EVENT_DEVICE_STATE_TRANSITION);
+ logicalDisplayEventMask |= LOGICAL_DISPLAY_EVENT_DEVICE_STATE_TRANSITION;
// Display frame rate overrides changed.
} else if (!display.getPendingFrameRateOverrideUids().isEmpty()) {
- mLogicalDisplaysToUpdate.put(
- displayId, LOGICAL_DISPLAY_EVENT_FRAME_RATE_OVERRIDES_CHANGED);
+ logicalDisplayEventMask |= LOGICAL_DISPLAY_EVENT_FRAME_RATE_OVERRIDES_CHANGED;
// Non-override display values changed.
} else {
@@ -882,10 +889,10 @@
// things like display cutouts.
display.getNonOverrideDisplayInfoLocked(mTempDisplayInfo);
if (!mTempNonOverrideDisplayInfo.equals(mTempDisplayInfo)) {
- mLogicalDisplaysToUpdate.put(displayId, LOGICAL_DISPLAY_EVENT_CHANGED);
+ logicalDisplayEventMask |= LOGICAL_DISPLAY_EVENT_CHANGED;
}
}
-
+ mLogicalDisplaysToUpdate.put(displayId, logicalDisplayEventMask);
mUpdatedLogicalDisplays.put(displayId, UPDATE_STATE_UPDATED);
}
@@ -922,6 +929,8 @@
sendUpdatesForDisplaysLocked(LOGICAL_DISPLAY_EVENT_DISCONNECTED);
}
sendUpdatesForDisplaysLocked(LOGICAL_DISPLAY_EVENT_CHANGED);
+ sendUpdatesForDisplaysLocked(LOGICAL_DISPLAY_EVENT_REFRESH_RATE_CHANGED);
+ sendUpdatesForDisplaysLocked(LOGICAL_DISPLAY_EVENT_STATE_CHANGED);
sendUpdatesForDisplaysLocked(LOGICAL_DISPLAY_EVENT_FRAME_RATE_OVERRIDES_CHANGED);
sendUpdatesForDisplaysLocked(LOGICAL_DISPLAY_EVENT_SWAPPED);
if (mFlags.isConnectedDisplayManagementEnabled()) {
@@ -944,13 +953,25 @@
}
}
+ @VisibleForTesting
+ int updateAndGetMaskForDisplayPropertyChanges(DisplayInfo newDisplayInfo) {
+ int mask = LOGICAL_DISPLAY_EVENT_BASE;
+ if (mTempDisplayInfo.getRefreshRate() != newDisplayInfo.getRefreshRate()) {
+ mask |= LOGICAL_DISPLAY_EVENT_REFRESH_RATE_CHANGED;
+ }
+
+ if (mTempDisplayInfo.state != newDisplayInfo.state) {
+ mask |= LOGICAL_DISPLAY_EVENT_STATE_CHANGED;
+ }
+ return mask;
+ }
/**
* Send the specified message for all relevant displays in the specified display-to-message map.
*/
- private void sendUpdatesForDisplaysLocked(int msg) {
+ private void sendUpdatesForDisplaysLocked(int logicalDisplayEvent) {
for (int i = mLogicalDisplaysToUpdate.size() - 1; i >= 0; --i) {
- final int currMsg = mLogicalDisplaysToUpdate.valueAt(i);
- if (currMsg != msg) {
+ final int logicalDisplayEventMask = mLogicalDisplaysToUpdate.valueAt(i);
+ if ((logicalDisplayEventMask & logicalDisplayEvent) == 0) {
continue;
}
@@ -959,25 +980,25 @@
if (DEBUG) {
final DisplayDevice device = display.getPrimaryDisplayDeviceLocked();
final String uniqueId = device == null ? "null" : device.getUniqueId();
- Slog.d(TAG, "Sending " + displayEventToString(msg) + " for display=" + id
- + " with device=" + uniqueId);
+ Slog.d(TAG, "Sending " + displayEventToString(logicalDisplayEvent) + " for "
+ + "display=" + id + " with device=" + uniqueId);
}
if (mFlags.isConnectedDisplayManagementEnabled()) {
- if (msg == LOGICAL_DISPLAY_EVENT_ADDED) {
+ if (logicalDisplayEvent == LOGICAL_DISPLAY_EVENT_ADDED) {
mDisplaysEnabledCache.put(id, true);
- } else if (msg == LOGICAL_DISPLAY_EVENT_REMOVED) {
+ } else if (logicalDisplayEvent == LOGICAL_DISPLAY_EVENT_REMOVED) {
mDisplaysEnabledCache.delete(id);
}
}
- mListener.onLogicalDisplayEventLocked(display, msg);
+ mListener.onLogicalDisplayEventLocked(display, logicalDisplayEvent);
if (mFlags.isConnectedDisplayManagementEnabled()) {
- if (msg == LOGICAL_DISPLAY_EVENT_DISCONNECTED) {
+ if (logicalDisplayEvent == LOGICAL_DISPLAY_EVENT_DISCONNECTED) {
mLogicalDisplays.delete(id);
}
- } else if (msg == LOGICAL_DISPLAY_EVENT_REMOVED) {
+ } else if (logicalDisplayEvent == LOGICAL_DISPLAY_EVENT_REMOVED) {
// We wait until we sent the EVENT_REMOVED event before actually removing the
// display.
mLogicalDisplays.delete(id);
@@ -1348,6 +1369,10 @@
return "connected";
case LOGICAL_DISPLAY_EVENT_DISCONNECTED:
return "disconnected";
+ case LOGICAL_DISPLAY_EVENT_STATE_CHANGED:
+ return "state_changed";
+ case LOGICAL_DISPLAY_EVENT_REFRESH_RATE_CHANGED:
+ return "refresh_rate_changed";
}
return null;
}
diff --git a/services/core/java/com/android/server/display/OWNERS b/services/core/java/com/android/server/display/OWNERS
index 9439eaa..9f0cabf 100644
--- a/services/core/java/com/android/server/display/OWNERS
+++ b/services/core/java/com/android/server/display/OWNERS
@@ -1,5 +1,4 @@
michaelwr@google.com
-dangittik@google.com
hackbod@google.com
ogunwale@google.com
santoscordon@google.com
@@ -7,5 +6,6 @@
wilczynskip@google.com
brup@google.com
petsjonkin@google.com
+olb@google.com
per-file ColorDisplayService.java=christyfranks@google.com
diff --git a/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java b/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java
index 5b12dfb..35f6fd0 100644
--- a/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java
+++ b/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java
@@ -180,6 +180,20 @@
}
/**
+ * Updates the brightness override from WindowManager.
+ *
+ * @param request The request to override the brightness
+ * @return whether this request will result in a change of the brightness
+ */
+ public boolean updateWindowManagerBrightnessOverride(
+ DisplayManagerInternal.DisplayBrightnessOverrideRequest request) {
+ synchronized (mLock) {
+ return mDisplayBrightnessStrategySelector.getOverrideBrightnessStrategy()
+ .updateWindowManagerBrightnessOverride(request);
+ }
+ }
+
+ /**
* Sets the brightness to follow
*/
public void setBrightnessToFollow(float brightnessToFollow, boolean slowChange) {
diff --git a/services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java b/services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java
index 60c1b8f..a0ad49d 100644
--- a/services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java
+++ b/services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java
@@ -180,8 +180,10 @@
displayBrightnessStrategy = mFollowerBrightnessStrategy;
} else if (displayPowerRequest.boostScreenBrightness) {
displayBrightnessStrategy = mBoostBrightnessStrategy;
- } else if (BrightnessUtils
- .isValidBrightnessValue(displayPowerRequest.screenBrightnessOverride)) {
+ } else if (BrightnessUtils.isValidBrightnessValue(
+ displayPowerRequest.screenBrightnessOverride)
+ || BrightnessUtils.isValidBrightnessValue(
+ mOverrideBrightnessStrategy.getWindowManagerBrightnessOverride())) {
displayBrightnessStrategy = mOverrideBrightnessStrategy;
} else if (BrightnessUtils.isValidBrightnessValue(
mTemporaryBrightnessStrategy.getTemporaryScreenBrightness())) {
@@ -256,6 +258,10 @@
return mAutoBrightnessFallbackStrategy;
}
+ public OverrideBrightnessStrategy getOverrideBrightnessStrategy() {
+ return mOverrideBrightnessStrategy;
+ }
+
/**
* Dumps the state of this class.
*/
diff --git a/services/core/java/com/android/server/display/brightness/strategy/OverrideBrightnessStrategy.java b/services/core/java/com/android/server/display/brightness/strategy/OverrideBrightnessStrategy.java
index 3fc15d1..649f9da 100644
--- a/services/core/java/com/android/server/display/brightness/strategy/OverrideBrightnessStrategy.java
+++ b/services/core/java/com/android/server/display/brightness/strategy/OverrideBrightnessStrategy.java
@@ -16,10 +16,13 @@
package com.android.server.display.brightness.strategy;
+import android.hardware.display.DisplayManagerInternal;
import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest;
+import android.os.PowerManager;
import com.android.server.display.DisplayBrightnessState;
import com.android.server.display.brightness.BrightnessReason;
+import com.android.server.display.brightness.BrightnessUtils;
import com.android.server.display.brightness.StrategyExecutionRequest;
import com.android.server.display.brightness.StrategySelectionNotifyRequest;
@@ -29,6 +32,10 @@
* Manages the brightness of the display when the system brightness is overridden
*/
public class OverrideBrightnessStrategy implements DisplayBrightnessStrategy {
+
+ private float mWindowManagerBrightnessOverride = PowerManager.BRIGHTNESS_INVALID_FLOAT;
+ private CharSequence mWindowManagerBrightnessOverrideTag = null;
+
@Override
public DisplayBrightnessState updateBrightness(
StrategyExecutionRequest strategyExecutionRequest) {
@@ -36,9 +43,18 @@
// the brightness
DisplayPowerRequest dpr = strategyExecutionRequest.getDisplayPowerRequest();
BrightnessReason reason = new BrightnessReason(BrightnessReason.REASON_OVERRIDE);
- reason.setTag(dpr.screenBrightnessOverrideTag);
+
+ float brightness = dpr.screenBrightnessOverride;
+ if (BrightnessUtils.isValidBrightnessValue(dpr.screenBrightnessOverride)) {
+ brightness = dpr.screenBrightnessOverride;
+ reason.setTag(dpr.screenBrightnessOverrideTag);
+ } else if (BrightnessUtils.isValidBrightnessValue(mWindowManagerBrightnessOverride)) {
+ brightness = mWindowManagerBrightnessOverride;
+ reason.setTag(mWindowManagerBrightnessOverrideTag);
+ }
+
return new DisplayBrightnessState.Builder()
- .setBrightness(dpr.screenBrightnessOverride)
+ .setBrightness(brightness)
.setBrightnessReason(reason)
.setDisplayBrightnessStrategyName(getName())
.build();
@@ -50,7 +66,12 @@
}
@Override
- public void dump(PrintWriter writer) {}
+ public void dump(PrintWriter writer) {
+ writer.println("OverrideBrightnessStrategy:");
+ writer.println(" mWindowManagerBrightnessOverride=" + mWindowManagerBrightnessOverride);
+ writer.println(" mWindowManagerBrightnessOverrideTag="
+ + mWindowManagerBrightnessOverrideTag);
+ }
@Override
public void strategySelectionPostProcessor(
@@ -58,6 +79,37 @@
// DO NOTHING
}
+ /**
+ * Updates the brightness override from WindowManager.
+ *
+ * @param request The request to override the brightness
+ * @return whether this request will result in a change of the brightness
+ */
+ public boolean updateWindowManagerBrightnessOverride(
+ DisplayManagerInternal.DisplayBrightnessOverrideRequest request) {
+ float newBrightness = request == null
+ ? PowerManager.BRIGHTNESS_INVALID_FLOAT : request.brightness;
+ mWindowManagerBrightnessOverrideTag = request == null ? null : request.tag;
+
+ if (floatEquals(newBrightness, mWindowManagerBrightnessOverride)) {
+ return false;
+ }
+
+ mWindowManagerBrightnessOverride = newBrightness;
+ return true;
+ }
+
+ /**
+ * Returns the current brightness override from WindowManager.
+ */
+ public float getWindowManagerBrightnessOverride() {
+ return mWindowManagerBrightnessOverride;
+ }
+
+ private boolean floatEquals(float f1, float f2) {
+ return f1 == f2 || (Float.isNaN(f1) && Float.isNaN(f2));
+ }
+
@Override
public int getReason() {
return BrightnessReason.REASON_OVERRIDE;
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 716661d..68dc80f 100644
--- a/services/core/java/com/android/server/display/color/TintController.java
+++ b/services/core/java/com/android/server/display/color/TintController.java
@@ -19,6 +19,7 @@
import android.animation.ValueAnimator;
import android.content.Context;
import android.util.Slog;
+import com.android.internal.annotations.GuardedBy;
import java.io.PrintWriter;
@@ -29,23 +30,33 @@
*/
private static final long TRANSITION_DURATION = 3000L;
+ private final Object mLock = new Object();
+
+ @GuardedBy("mLock")
private ValueAnimator mAnimator;
+ @GuardedBy("mLock")
private Boolean mIsActivated;
public ValueAnimator getAnimator() {
- return mAnimator;
+ synchronized (mLock) {
+ return mAnimator;
+ }
}
public void setAnimator(ValueAnimator animator) {
- mAnimator = animator;
+ synchronized (mLock) {
+ mAnimator = animator;
+ }
}
/**
* Cancel the animator if it's still running.
*/
public void cancelAnimator() {
- if (mAnimator != null) {
- mAnimator.cancel();
+ synchronized (mLock) {
+ if (mAnimator != null) {
+ mAnimator.cancel();
+ }
}
}
@@ -53,22 +64,30 @@
* End the animator if it's still running, jumping to the end state.
*/
public void endAnimator() {
- if (mAnimator != null) {
- mAnimator.end();
- mAnimator = null;
+ synchronized (mLock) {
+ if (mAnimator != null) {
+ mAnimator.end();
+ mAnimator = null;
+ }
}
}
public void setActivated(Boolean isActivated) {
- mIsActivated = isActivated;
+ synchronized (mLock) {
+ mIsActivated = isActivated;
+ }
}
public boolean isActivated() {
- return mIsActivated != null && mIsActivated;
+ synchronized (mLock) {
+ return mIsActivated != null && mIsActivated;
+ }
}
public boolean isActivatedStateNotSet() {
- return mIsActivated == null;
+ synchronized (mLock) {
+ return mIsActivated == null;
+ }
}
public long getTransitionDurationMilliseconds() {
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 71f17d1..1a7d74a 100644
--- a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
+++ b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
@@ -246,6 +246,10 @@
Flags.FLAG_ENABLE_PLUGIN_MANAGER,
Flags::enablePluginManager
);
+ private final FlagState mDisplayListenerPerformanceImprovementsFlagState = new FlagState(
+ Flags.FLAG_DISPLAY_LISTENER_PERFORMANCE_IMPROVEMENTS,
+ Flags::displayListenerPerformanceImprovements
+ );
/**
* @return {@code true} if 'port' is allowed in display layout configuration file.
@@ -527,6 +531,13 @@
}
/**
+ * @return {@code true} if the flag for display listener performance improvements is enabled
+ */
+ public boolean isDisplayListenerPerformanceImprovementsEnabled() {
+ return mDisplayListenerPerformanceImprovementsFlagState.isEnabled();
+ }
+
+ /**
* dumps all flagstates
* @param pw printWriter
*/
@@ -578,6 +589,7 @@
pw.println(" " + mHasArrSupport);
pw.println(" " + mAutoBrightnessModeBedtimeWearFlagState);
pw.println(" " + mEnablePluginManagerFlagState);
+ pw.println(" " + mDisplayListenerPerformanceImprovementsFlagState);
}
private static class FlagState {
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 b509544..e7ea868 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
@@ -4,6 +4,14 @@
# Important: Flags must be accessed through DisplayManagerFlags.
flag {
+ name: "is_always_on_available_api"
+ namespace: "display_manager"
+ description: "Allows querying of AOD availability"
+ bug: "324046664"
+ is_fixed_read_only: true
+}
+
+flag {
name: "enable_port_in_display_layout"
namespace: "display_manager"
description: "Allows refering to displays by port in display layout"
@@ -440,6 +448,14 @@
}
flag {
+ name: "display_listener_performance_improvements"
+ namespace: "display_manager"
+ description: "Feature flag for an API to let the apps subscribe to a specific property change of the Display."
+ bug: "372700957"
+ is_fixed_read_only: true
+}
+
+flag {
name: "enable_get_supported_refresh_rates"
namespace: "core_graphics"
description: "Flag to use the surfaceflinger rates for getSupportedRefreshRates"
diff --git a/services/core/java/com/android/server/display/mode/SystemRequestObserver.java b/services/core/java/com/android/server/display/mode/SystemRequestObserver.java
index 15f19cc..4a4c8da 100644
--- a/services/core/java/com/android/server/display/mode/SystemRequestObserver.java
+++ b/services/core/java/com/android/server/display/mode/SystemRequestObserver.java
@@ -20,6 +20,7 @@
import android.annotation.Nullable;
import android.os.IBinder;
import android.os.RemoteException;
+import android.util.Slog;
import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
@@ -28,12 +29,15 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.NoSuchElementException;
/**
* SystemRequestObserver responsible for handling system requests to filter allowable display
* modes
*/
class SystemRequestObserver {
+ private static final String TAG = "SystemRequestObserver";
+
private final VotesStorage mVotesStorage;
private final IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
@@ -43,6 +47,7 @@
}
@Override
public void binderDied(@NonNull IBinder who) {
+ Slog.d(TAG, "binder died: " + who);
removeSystemRequestedVotes(who);
who.unlinkToDeath(mDeathRecipient, 0);
}
@@ -83,9 +88,11 @@
updateStorageLocked(displayId);
}
if (needLinkToDeath) {
+ Slog.d(TAG, "binder linking to death: " + token);
token.linkToDeath(mDeathRecipient, 0);
}
} catch (RemoteException re) {
+ Slog.d(TAG, "linking to death failed: " + token, re);
removeSystemRequestedVotes(token);
}
}
@@ -94,14 +101,19 @@
boolean needToUnlink = false;
synchronized (mLock) {
SparseArray<List<Integer>> modesByDisplay = mDisplaysRestrictions.get(token);
- if (modesByDisplay != null) {
+ if (modesByDisplay != null && modesByDisplay.size() > 0) {
modesByDisplay.remove(displayId);
needToUnlink = modesByDisplay.size() == 0;
updateStorageLocked(displayId);
}
}
if (needToUnlink) {
- token.unlinkToDeath(mDeathRecipient, 0);
+ try {
+ Slog.d(TAG, "binder unlinking to death: " + token);
+ token.unlinkToDeath(mDeathRecipient, 0);
+ } catch (NoSuchElementException e) {
+ Slog.d(TAG, "unlinking to death failed: " + token, e);
+ }
}
}
diff --git a/services/core/java/com/android/server/input/AppLaunchShortcutManager.java b/services/core/java/com/android/server/input/AppLaunchShortcutManager.java
index aef207f..f3820e5 100644
--- a/services/core/java/com/android/server/input/AppLaunchShortcutManager.java
+++ b/services/core/java/com/android/server/input/AppLaunchShortcutManager.java
@@ -21,6 +21,7 @@
import android.app.role.RoleManager;
import android.content.Context;
import android.content.Intent;
+import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.hardware.input.AppLaunchData;
import android.hardware.input.InputGestureData;
@@ -137,11 +138,19 @@
String categoryName = parser.getAttributeValue(null, ATTRIBUTE_CATEGORY);
String shiftName = parser.getAttributeValue(null, ATTRIBUTE_SHIFT);
String roleName = parser.getAttributeValue(null, ATTRIBUTE_ROLE);
-
- // TODO(b/358569822): Shift bookmarks to use keycode instead of shortcutChar
- int keycode = KeyEvent.KEYCODE_UNKNOWN;
String shortcut = parser.getAttributeValue(null, ATTRIBUTE_SHORTCUT);
- if (!TextUtils.isEmpty(shortcut)) {
+ int keycode;
+ int modifierState;
+ TypedArray a = mContext.getResources().obtainAttributes(parser,
+ R.styleable.Bookmark);
+ try {
+ keycode = a.getInt(R.styleable.Bookmark_keycode, KeyEvent.KEYCODE_UNKNOWN);
+ modifierState = a.getInt(R.styleable.Bookmark_modifierState, 0);
+ } finally {
+ a.recycle();
+ }
+ if (keycode == KeyEvent.KEYCODE_UNKNOWN && !TextUtils.isEmpty(shortcut)) {
+ // Fetch keycode using shortcut char
KeyEvent[] events = virtualKcm.getEvents(new char[]{shortcut.toLowerCase(
Locale.ROOT).charAt(0)});
// Single key press can generate the character
@@ -153,12 +162,17 @@
Log.w(TAG, "Keycode required for bookmark with category=" + categoryName
+ " packageName=" + packageName + " className=" + className
+ " role=" + roleName + " shiftName=" + shiftName
- + " shortcut=" + shortcut);
+ + " shortcut=" + shortcut + " modifierState=" + modifierState);
continue;
}
- final boolean isShiftShortcut = (shiftName != null && shiftName.toLowerCase(
- Locale.ROOT).equals("true"));
+ if (modifierState == 0) {
+ // Fetch modifierState using shiftName
+ boolean isShiftShortcut = shiftName != null && shiftName.toLowerCase(
+ Locale.ROOT).equals("true");
+ modifierState =
+ KeyEvent.META_META_ON | (isShiftShortcut ? KeyEvent.META_SHIFT_ON : 0);
+ }
AppLaunchData launchData = null;
if (!TextUtils.isEmpty(packageName) && !TextUtils.isEmpty(className)) {
launchData = AppLaunchData.createLaunchDataForComponent(packageName, className);
@@ -168,11 +182,9 @@
launchData = AppLaunchData.createLaunchDataForRole(roleName);
}
if (launchData != null) {
- Log.d(TAG, "adding shortcut " + launchData + "shift="
- + isShiftShortcut + " keycode=" + keycode);
+ Log.d(TAG, "adding shortcut " + launchData + " modifierState="
+ + modifierState + " keycode=" + keycode);
// All bookmarks are based on Action key
- int modifierState =
- KeyEvent.META_META_ON | (isShiftShortcut ? KeyEvent.META_SHIFT_ON : 0);
InputGestureData bookmark = new InputGestureData.Builder()
.setTrigger(InputGestureData.createKeyTrigger(keycode, modifierState))
.setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION)
diff --git a/services/core/java/com/android/server/input/InputGestureManager.java b/services/core/java/com/android/server/input/InputGestureManager.java
index 6f35402..73d5630 100644
--- a/services/core/java/com/android/server/input/InputGestureManager.java
+++ b/services/core/java/com/android/server/input/InputGestureManager.java
@@ -228,6 +228,28 @@
KeyEvent.META_META_ON | KeyEvent.META_ALT_ON,
KeyGestureEvent.KEY_GESTURE_TYPE_ACTIVATE_SELECT_TO_SPEAK));
}
+ if (enableTaskResizingKeyboardShortcuts()) {
+ systemShortcuts.add(createKeyGesture(
+ KeyEvent.KEYCODE_LEFT_BRACKET,
+ KeyEvent.META_META_ON,
+ KeyGestureEvent.KEY_GESTURE_TYPE_SNAP_LEFT_FREEFORM_WINDOW
+ ));
+ systemShortcuts.add(createKeyGesture(
+ KeyEvent.KEYCODE_RIGHT_BRACKET,
+ KeyEvent.META_META_ON,
+ KeyGestureEvent.KEY_GESTURE_TYPE_SNAP_RIGHT_FREEFORM_WINDOW
+ ));
+ systemShortcuts.add(createKeyGesture(
+ KeyEvent.KEYCODE_EQUALS,
+ KeyEvent.META_META_ON,
+ KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_MAXIMIZE_FREEFORM_WINDOW
+ ));
+ systemShortcuts.add(createKeyGesture(
+ KeyEvent.KEYCODE_MINUS,
+ KeyEvent.META_META_ON,
+ KeyGestureEvent.KEY_GESTURE_TYPE_MINIMIZE_FREEFORM_WINDOW
+ ));
+ }
if (keyboardA11yShortcutControl()) {
if (InputSettings.isAccessibilityBounceKeysFeatureEnabled()) {
systemShortcuts.add(createKeyGesture(
@@ -257,28 +279,6 @@
KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_SLOW_KEYS
));
}
- if (enableTaskResizingKeyboardShortcuts()) {
- systemShortcuts.add(createKeyGesture(
- KeyEvent.KEYCODE_LEFT_BRACKET,
- KeyEvent.META_ALT_ON,
- KeyGestureEvent.KEY_GESTURE_TYPE_SNAP_LEFT_FREEFORM_WINDOW
- ));
- systemShortcuts.add(createKeyGesture(
- KeyEvent.KEYCODE_RIGHT_BRACKET,
- KeyEvent.META_ALT_ON,
- KeyGestureEvent.KEY_GESTURE_TYPE_SNAP_RIGHT_FREEFORM_WINDOW
- ));
- systemShortcuts.add(createKeyGesture(
- KeyEvent.KEYCODE_EQUALS,
- KeyEvent.META_ALT_ON,
- KeyGestureEvent.KEY_GESTURE_TYPE_MAXIMIZE_FREEFORM_WINDOW
- ));
- systemShortcuts.add(createKeyGesture(
- KeyEvent.KEYCODE_MINUS,
- KeyEvent.META_ALT_ON,
- KeyGestureEvent.KEY_GESTURE_TYPE_RESTORE_FREEFORM_WINDOW_SIZE
- ));
- }
}
synchronized (mGestureLock) {
for (InputGestureData systemShortcut : systemShortcuts) {
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index f4dd717..e5b077d 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -193,7 +193,6 @@
private DisplayManagerInternal mDisplayManagerInternal;
private WindowManagerInternal mWindowManagerInternal;
- private PackageManagerInternal mPackageManagerInternal;
private final File mDoubleTouchGestureEnableFile;
@@ -573,7 +572,6 @@
mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class);
- mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
mSettingsObserver.registerAndUpdate();
@@ -623,9 +621,10 @@
mKeyRemapper.systemRunning();
mPointerIconCache.systemRunning();
mKeyboardGlyphManager.systemRunning();
- mKeyGestureController.systemRunning();
-
- initKeyGestures();
+ if (useKeyGestureEventHandler()) {
+ mKeyGestureController.systemRunning();
+ initKeyGestures();
+ }
}
private void reloadDeviceAliases() {
@@ -2608,9 +2607,6 @@
}
private void initKeyGestures() {
- if (!useKeyGestureEventHandler()) {
- return;
- }
InputManager im = Objects.requireNonNull(mContext.getSystemService(InputManager.class));
im.registerKeyGestureEventHandler(new InputManager.KeyGestureEventHandler() {
@Override
@@ -2939,10 +2935,11 @@
private void enforceManageKeyGesturePermission() {
// TODO(b/361567988): Use @EnforcePermission to enforce permission once flag guarding the
// permission is rolled out
- if (mSystemReady) {
- String systemUIPackage = mContext.getString(R.string.config_systemUi);
- int systemUIAppId = UserHandle.getAppId(mPackageManagerInternal
- .getPackageUid(systemUIPackage, PackageManager.MATCH_SYSTEM_ONLY,
+ String systemUIPackage = mContext.getString(R.string.config_systemUi);
+ PackageManagerInternal pm = LocalServices.getService(PackageManagerInternal.class);
+ if (pm != null) {
+ int systemUIAppId = UserHandle.getAppId(
+ pm.getPackageUid(systemUIPackage, PackageManager.MATCH_SYSTEM_ONLY,
UserHandle.USER_SYSTEM));
if (UserHandle.getCallingAppId() == systemUIAppId) {
return;
@@ -3034,6 +3031,11 @@
return mKeyGestureController.getAppLaunchBookmarks();
}
+ @Override
+ public void resetLockedModifierState() {
+ mNative.resetLockedModifierState();
+ }
+
private void handleCurrentUserChanged(@UserIdInt int userId) {
mCurrentUserId = userId;
mKeyGestureController.setCurrentUserId(userId);
diff --git a/services/core/java/com/android/server/input/NativeInputManagerService.java b/services/core/java/com/android/server/input/NativeInputManagerService.java
index 8903c27..728e440 100644
--- a/services/core/java/com/android/server/input/NativeInputManagerService.java
+++ b/services/core/java/com/android/server/input/NativeInputManagerService.java
@@ -98,6 +98,8 @@
void toggleCapsLock(int deviceId);
+ void resetLockedModifierState();
+
void displayRemoved(int displayId);
void setInputDispatchMode(boolean enabled, boolean frozen);
@@ -370,6 +372,9 @@
public native void toggleCapsLock(int deviceId);
@Override
+ public native void resetLockedModifierState();
+
+ @Override
public native void displayRemoved(int displayId);
@Override
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index d8483f7..b7af9a4 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -1310,7 +1310,7 @@
// Do not reset the default (current) IME when it is a 3rd-party IME
String selectedMethodId = bindingController.getSelectedMethodId();
final InputMethodSettings settings = InputMethodSettingsRepository.get(userId);
- if (selectedMethodId != null
+ if (selectedMethodId != null && settings.getMethodMap().get(selectedMethodId) != null
&& !settings.getMethodMap().get(selectedMethodId).isSystem()) {
return;
}
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubClientManager.java b/services/core/java/com/android/server/location/contexthub/ContextHubClientManager.java
index 0fdd0ae..5248a05 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubClientManager.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubClientManager.java
@@ -237,15 +237,16 @@
if (message.isBroadcastMessage()) {
if (message.isReliable()) {
- Log.e(TAG, "Received reliable broadcast message from " + message.getNanoAppId());
+ Log.e(TAG, "Received reliable broadcast message from 0x"
+ + Long.toHexString(message.getNanoAppId()));
return ErrorCode.PERMANENT_ERROR;
}
// Broadcast messages shouldn't be sent with any permissions tagged per CHRE API
// requirements.
if (!messagePermissions.isEmpty()) {
- Log.e(TAG, "Received broadcast message with permissions from "
- + message.getNanoAppId());
+ Log.e(TAG, "Received broadcast message with permissions from 0x"
+ + Long.toHexString(message.getNanoAppId()));
return ErrorCode.PERMANENT_ERROR;
}
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubHalEndpointCallback.java b/services/core/java/com/android/server/location/contexthub/ContextHubHalEndpointCallback.java
new file mode 100644
index 0000000..c05f7a0
--- /dev/null
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubHalEndpointCallback.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.location.contexthub;
+
+import android.hardware.contexthub.EndpointId;
+import android.hardware.contexthub.HubEndpointInfo;
+import android.hardware.contexthub.IEndpointCallback;
+import android.hardware.contexthub.Message;
+import android.hardware.contexthub.MessageDeliveryStatus;
+import android.os.RemoteException;
+
+/** IEndpointCallback implementation. */
+public class ContextHubHalEndpointCallback
+ extends android.hardware.contexthub.IEndpointCallback.Stub {
+ private final IEndpointLifecycleCallback mEndpointLifecycleCallback;
+
+ /** Interface for listening for endpoint start and stop events. */
+ public interface IEndpointLifecycleCallback {
+ /** Called when a batch of endpoints started. */
+ void onEndpointStarted(HubEndpointInfo[] endpointInfos);
+
+ /** Called when a batch of endpoints stopped. */
+ void onEndpointStopped(HubEndpointInfo.HubEndpointIdentifier[] endpointIds, byte reason);
+ }
+
+ ContextHubHalEndpointCallback(IEndpointLifecycleCallback endpointLifecycleCallback) {
+ mEndpointLifecycleCallback = endpointLifecycleCallback;
+ }
+
+ @Override
+ public void onEndpointStarted(android.hardware.contexthub.EndpointInfo[] halEndpointInfos)
+ throws RemoteException {
+ if (halEndpointInfos.length == 0) {
+ return;
+ }
+ HubEndpointInfo[] endpointInfos = new HubEndpointInfo[halEndpointInfos.length];
+ for (int i = 0; i < halEndpointInfos.length; i++) {
+ endpointInfos[i++] = new HubEndpointInfo(halEndpointInfos[i]);
+ }
+ mEndpointLifecycleCallback.onEndpointStarted(endpointInfos);
+ }
+
+ @Override
+ public void onEndpointStopped(EndpointId[] halEndpointIds, byte reason) throws RemoteException {
+ HubEndpointInfo.HubEndpointIdentifier[] endpointIds =
+ new HubEndpointInfo.HubEndpointIdentifier[halEndpointIds.length];
+ for (int i = 0; i < halEndpointIds.length; i++) {
+ endpointIds[i] = new HubEndpointInfo.HubEndpointIdentifier(halEndpointIds[i]);
+ }
+ mEndpointLifecycleCallback.onEndpointStopped(endpointIds, reason);
+ }
+
+ @Override
+ public void onMessageReceived(int i, Message message) throws RemoteException {}
+
+ @Override
+ public void onMessageDeliveryStatusReceived(int i, MessageDeliveryStatus messageDeliveryStatus)
+ throws RemoteException {}
+
+ @Override
+ public void onEndpointSessionOpenRequest(
+ int i, EndpointId endpointId, EndpointId endpointId1, String s)
+ throws RemoteException {}
+
+ @Override
+ public void onCloseEndpointSession(int i, byte b) throws RemoteException {}
+
+ @Override
+ public void onEndpointSessionOpenComplete(int i) throws RemoteException {}
+
+ @Override
+ public int getInterfaceVersion() throws RemoteException {
+ return IEndpointCallback.VERSION;
+ }
+
+ @Override
+ public String getInterfaceHash() throws RemoteException {
+ return IEndpointCallback.HASH;
+ }
+}
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubService.java b/services/core/java/com/android/server/location/contexthub/ContextHubService.java
index f611c57..7c9d9c5 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubService.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubService.java
@@ -31,6 +31,9 @@
import android.hardware.SensorPrivacyManager;
import android.hardware.SensorPrivacyManagerInternal;
import android.hardware.contexthub.ErrorCode;
+import android.hardware.contexthub.HubEndpointInfo;
+import android.hardware.contexthub.IContextHubEndpoint;
+import android.hardware.contexthub.IContextHubEndpointCallback;
import android.hardware.contexthub.MessageDeliveryStatus;
import android.hardware.location.ContextHubInfo;
import android.hardware.location.ContextHubMessage;
@@ -249,6 +252,7 @@
public void handleServiceRestart() {
Log.i(TAG, "Recovering from Context Hub HAL restart...");
initExistingCallbacks();
+ mHubInfoRegistry.onHalRestart();
resetSettings();
if (Flags.reconnectHostEndpointsAfterHalRestart()) {
mClientManager.forEachClientOfHub(mContextHubId,
@@ -322,14 +326,22 @@
}
if (Flags.offloadApi()) {
- mHubInfoRegistry = new HubInfoRegistry(mContextHubWrapper);
- Log.i(TAG, "Enabling generic offload API");
+ HubInfoRegistry registry;
+ try {
+ registry = new HubInfoRegistry(mContextHubWrapper);
+ Log.i(TAG, "Enabling generic offload API");
+ } catch (UnsupportedOperationException e) {
+ registry = null;
+ Log.w(TAG, "Generic offload API not supported, disabling");
+ }
+ mHubInfoRegistry = registry;
} else {
mHubInfoRegistry = null;
Log.i(TAG, "Disabling generic offload API");
}
initDefaultClientMap();
+ initEndpointCallback();
initLocationSettingNotifications();
initWifiSettingNotifications();
@@ -508,6 +520,18 @@
mDefaultClientMap = Collections.unmodifiableMap(defaultClientMap);
}
+ private void initEndpointCallback() {
+ if (mHubInfoRegistry == null) {
+ return;
+ }
+ try {
+ mContextHubWrapper.registerEndpointCallback(
+ new ContextHubHalEndpointCallback(mHubInfoRegistry));
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException while registering IEndpointCallback", e);
+ }
+ }
+
/**
* Initializes existing callbacks with the mContextHubWrapper for every context hub
*/
@@ -739,6 +763,36 @@
return mHubInfoRegistry.getHubs();
}
+ @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
+ @Override
+ public List<HubEndpointInfo> findEndpoints(long endpointId) {
+ super.findEndpoints_enforcePermission();
+ if (mHubInfoRegistry == null) {
+ return Collections.emptyList();
+ }
+ return mHubInfoRegistry.findEndpoints(endpointId);
+ }
+
+ @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
+ @Override
+ public List<HubEndpointInfo> findEndpointsWithService(String serviceDescriptor) {
+ super.findEndpointsWithService_enforcePermission();
+ if (mHubInfoRegistry == null) {
+ return Collections.emptyList();
+ }
+ return mHubInfoRegistry.findEndpointsWithService(serviceDescriptor);
+ }
+
+ @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
+ @Override
+ public IContextHubEndpoint registerEndpoint(
+ HubEndpointInfo pendingHubEndpointInfo, IContextHubEndpointCallback callback)
+ throws RemoteException {
+ super.registerEndpoint_enforcePermission();
+ // TODO(b/375487784): Implement this
+ return null;
+ }
+
/**
* Creates an internal load transaction callback to be used for old API clients
*
diff --git a/services/core/java/com/android/server/location/contexthub/HubInfoRegistry.java b/services/core/java/com/android/server/location/contexthub/HubInfoRegistry.java
index 68de9db..d2b2331 100644
--- a/services/core/java/com/android/server/location/contexthub/HubInfoRegistry.java
+++ b/services/core/java/com/android/server/location/contexthub/HubInfoRegistry.java
@@ -16,45 +16,161 @@
package com.android.server.location.contexthub;
+import android.hardware.contexthub.HubEndpointInfo;
+import android.hardware.contexthub.HubServiceInfo;
import android.hardware.location.HubInfo;
import android.os.RemoteException;
+import android.util.ArrayMap;
import android.util.IndentingPrintWriter;
import android.util.Log;
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
-class HubInfoRegistry {
+class HubInfoRegistry implements ContextHubHalEndpointCallback.IEndpointLifecycleCallback {
private static final String TAG = "HubInfoRegistry";
+ private final Object mLock = new Object();
private final IContextHubWrapper mContextHubWrapper;
- private final List<HubInfo> mHubsInfo;
+ @GuardedBy("mLock")
+ private List<HubInfo> mHubsInfo;
+
+ @GuardedBy("mLock")
+ private final ArrayMap<HubEndpointInfo.HubEndpointIdentifier, HubEndpointInfo>
+ mHubEndpointInfos = new ArrayMap<>();
HubInfoRegistry(IContextHubWrapper contextHubWrapper) {
- List<HubInfo> hubInfos;
mContextHubWrapper = contextHubWrapper;
+ refreshCachedHubs();
+ refreshCachedEndpoints();
+ }
+
+ /** Retrieve the list of hubs available. */
+ List<HubInfo> getHubs() {
+ synchronized (mLock) {
+ return mHubsInfo;
+ }
+ }
+
+ private void refreshCachedHubs() {
+ List<HubInfo> hubInfos;
try {
hubInfos = mContextHubWrapper.getHubs();
} catch (RemoteException e) {
Log.e(TAG, "RemoteException while getting Hub info", e);
hubInfos = Collections.emptyList();
}
- mHubsInfo = hubInfos;
+
+ synchronized (mLock) {
+ mHubsInfo = hubInfos;
+ }
}
- /** Retrieve the list of hubs available. */
- List<HubInfo> getHubs() {
- return mHubsInfo;
+ private void refreshCachedEndpoints() {
+ List<HubEndpointInfo> endpointInfos;
+ try {
+ endpointInfos = mContextHubWrapper.getEndpoints();
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException while getting Hub info", e);
+ endpointInfos = Collections.emptyList();
+ }
+
+ synchronized (mLock) {
+ mHubEndpointInfos.clear();
+ for (HubEndpointInfo endpointInfo : endpointInfos) {
+ mHubEndpointInfos.put(endpointInfo.getIdentifier(), endpointInfo);
+ }
+ }
+ }
+
+ /** Invoked when HAL restarts */
+ public void onHalRestart() {
+ synchronized (mLock) {
+ refreshCachedHubs();
+ refreshCachedEndpoints();
+ }
+ }
+
+ @Override
+ public void onEndpointStarted(HubEndpointInfo[] endpointInfos) {
+ synchronized (mLock) {
+ for (HubEndpointInfo endpointInfo : endpointInfos) {
+ mHubEndpointInfos.remove(endpointInfo.getIdentifier());
+ mHubEndpointInfos.put(endpointInfo.getIdentifier(), endpointInfo);
+ }
+ }
+ }
+
+ @Override
+ public void onEndpointStopped(
+ HubEndpointInfo.HubEndpointIdentifier[] endpointIds, byte reason) {
+ synchronized (mLock) {
+ for (HubEndpointInfo.HubEndpointIdentifier endpointId : endpointIds) {
+ mHubEndpointInfos.remove(endpointId);
+ }
+ }
+ }
+
+ /** Return a list of {@link HubEndpointInfo} that represents endpoints with the matching id. */
+ public List<HubEndpointInfo> findEndpoints(long endpointIdQuery) {
+ List<HubEndpointInfo> searchResult = new ArrayList<>();
+ synchronized (mLock) {
+ for (HubEndpointInfo.HubEndpointIdentifier endpointId : mHubEndpointInfos.keySet()) {
+ if (endpointId.getEndpoint() == endpointIdQuery) {
+ searchResult.add(mHubEndpointInfos.get(endpointId));
+ }
+ }
+ }
+ return searchResult;
+ }
+
+ /**
+ * Return a list of {@link HubEndpointInfo} that represents endpoints with the matching service.
+ */
+ public List<HubEndpointInfo> findEndpointsWithService(String serviceDescriptor) {
+ List<HubEndpointInfo> searchResult = new ArrayList<>();
+ synchronized (mLock) {
+ for (HubEndpointInfo endpointInfo : mHubEndpointInfos.values()) {
+ for (HubServiceInfo serviceInfo : endpointInfo.getServiceInfoCollection()) {
+ if (serviceDescriptor.equals(serviceInfo.getServiceDescriptor())) {
+ searchResult.add(endpointInfo);
+ }
+ }
+ }
+ }
+ return searchResult;
}
void dump(IndentingPrintWriter ipw) {
+ synchronized (mLock) {
+ dumpLocked(ipw);
+ }
+ }
+
+ @GuardedBy("mLock")
+ private void dumpLocked(IndentingPrintWriter ipw) {
ipw.println(TAG);
ipw.increaseIndent();
+ ipw.println("Hubs");
for (HubInfo hubInfo : mHubsInfo) {
ipw.println(hubInfo);
}
ipw.decreaseIndent();
+
+ ipw.println();
+
+ ipw.increaseIndent();
+ ipw.println("Endpoints");
+ for (HubEndpointInfo endpointInfo : mHubEndpointInfos.values()) {
+ ipw.println(endpointInfo);
+ }
+ ipw.decreaseIndent();
+
+ ipw.println();
}
}
diff --git a/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java b/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java
index 6656a6f..9b729eb 100644
--- a/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java
+++ b/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java
@@ -19,6 +19,7 @@
import android.annotation.Nullable;
import android.chre.flags.Flags;
import android.hardware.contexthub.HostEndpointInfo;
+import android.hardware.contexthub.HubEndpointInfo;
import android.hardware.contexthub.MessageDeliveryStatus;
import android.hardware.contexthub.NanSessionRequest;
import android.hardware.contexthub.V1_0.ContextHub;
@@ -229,6 +230,15 @@
return Collections.emptyList();
}
+ /** Calls the appropriate getEndpoints function depending on the HAL version. */
+ public List<HubEndpointInfo> getEndpoints() throws RemoteException {
+ return Collections.emptyList();
+ }
+
+ /** Calls the appropriate registerEndpointCallback function depending on the HAL version. */
+ public void registerEndpointCallback(android.hardware.contexthub.IEndpointCallback cb)
+ throws RemoteException {}
+
/**
* @return True if this version of the Contexthub HAL supports Location setting notifications.
*/
@@ -622,6 +632,45 @@
return retVal;
}
+ @Override
+ public List<HubEndpointInfo> getEndpoints() throws RemoteException {
+ android.hardware.contexthub.IContextHub hub = getHub();
+ if (hub == null) {
+ return Collections.emptyList();
+ }
+
+ List<HubEndpointInfo> retVal = new ArrayList<>();
+ final List<android.hardware.contexthub.EndpointInfo> halEndpointInfos =
+ hub.getEndpoints();
+ for (android.hardware.contexthub.EndpointInfo halEndpointInfo : halEndpointInfos) {
+ /* HAL -> API Type conversion */
+ final HubEndpointInfo endpointInfo = new HubEndpointInfo(halEndpointInfo);
+ if (DEBUG) {
+ Log.i(TAG, "getEndpoints: endpointInfo=" + endpointInfo);
+ }
+ retVal.add(endpointInfo);
+ }
+
+ if (DEBUG) {
+ Log.i(TAG, "getEndpoints: total count=" + retVal.size());
+ }
+ return retVal;
+ }
+
+ @Override
+ public void registerEndpointCallback(android.hardware.contexthub.IEndpointCallback cb)
+ throws RemoteException {
+ android.hardware.contexthub.IContextHub hub = getHub();
+ if (hub == null) {
+ return;
+ }
+
+ if (DEBUG) {
+ Log.i(TAG, "registerEndpointCallback: cb=" + cb);
+ }
+ hub.registerEndpointCallback(cb);
+ }
+
public boolean supportsLocationSettingNotifications() {
return true;
}
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 436acba..df5ecf8 100644
--- a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
+++ b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
@@ -17,7 +17,6 @@
package com.android.server.media.projection;
import static android.Manifest.permission.MANAGE_MEDIA_PROJECTION;
-import static android.Manifest.permission.RECORD_SENSITIVE_CONTENT;
import static android.app.ActivityManagerInternal.MEDIA_PROJECTION_TOKEN_EVENT_CREATED;
import static android.app.ActivityManagerInternal.MEDIA_PROJECTION_TOKEN_EVENT_DESTROYED;
import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
@@ -28,7 +27,6 @@
import static android.media.projection.ReviewGrantedConsentResult.RECORD_CONTENT_DISPLAY;
import static android.media.projection.ReviewGrantedConsentResult.RECORD_CONTENT_TASK;
import static android.media.projection.ReviewGrantedConsentResult.UNKNOWN;
-import static android.provider.Settings.Global.DISABLE_SCREEN_SHARE_PROTECTIONS_FOR_APPS_AND_NOTIFICATIONS;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
@@ -41,10 +39,7 @@
import android.app.ActivityOptions.LaunchCookie;
import android.app.AppOpsManager;
import android.app.IProcessObserver;
-import android.app.KeyguardManager;
import android.app.compat.CompatChanges;
-import android.app.role.RoleManager;
-import android.companion.AssociationRequest;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledSince;
import android.content.ComponentName;
@@ -74,7 +69,6 @@
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
-import android.provider.Settings;
import android.util.ArrayMap;
import android.util.Slog;
import android.view.ContentRecordingSession;
@@ -85,7 +79,6 @@
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
import com.android.server.LocalServices;
-import com.android.server.SystemConfig;
import com.android.server.SystemService;
import com.android.server.Watchdog;
import com.android.server.wm.WindowManagerInternal;
@@ -140,12 +133,12 @@
private final ActivityManagerInternal mActivityManagerInternal;
private final PackageManager mPackageManager;
private final WindowManagerInternal mWmInternal;
- private final KeyguardManager mKeyguardManager;
- private final RoleManager mRoleManager;
+
private final MediaRouter mMediaRouter;
private final MediaRouterCallback mMediaRouterCallback;
private final MediaProjectionMetricsLogger mMediaProjectionMetricsLogger;
+ private final MediaProjectionStopController mMediaProjectionStopController;
private MediaRouter.RouteInfo mMediaRouteInfo;
@GuardedBy("mLock")
@@ -175,72 +168,16 @@
mMediaRouter = (MediaRouter) mContext.getSystemService(Context.MEDIA_ROUTER_SERVICE);
mMediaRouterCallback = new MediaRouterCallback();
mMediaProjectionMetricsLogger = injector.mediaProjectionMetricsLogger(context);
- mKeyguardManager = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
- mKeyguardManager.addKeyguardLockedStateListener(
- mContext.getMainExecutor(), this::onKeyguardLockedStateChanged);
- mRoleManager = mContext.getSystemService(RoleManager.class);
+ mMediaProjectionStopController = new MediaProjectionStopController(context,
+ this::maybeStopMediaProjection);
Watchdog.getInstance().addMonitor(this);
}
- /**
- * In order to record the keyguard, the MediaProjection package must be either:
- * - a holder of RECORD_SENSITIVE_CONTENT permission, or
- * - be one of the bugreport allowlisted packages, or
- * - hold the OP_PROJECT_MEDIA AppOp.
- */
- @SuppressWarnings("BooleanMethodIsAlwaysInverted")
- private boolean canCaptureKeyguard() {
- if (!android.companion.virtualdevice.flags.Flags.mediaProjectionKeyguardRestrictions()) {
- return true;
- }
+ private void maybeStopMediaProjection(int reason) {
synchronized (mLock) {
- if (mProjectionGrant == null || mProjectionGrant.packageName == null) {
- return false;
- }
- boolean disableScreenShareProtections = Settings.Global.getInt(
- getContext().getContentResolver(),
- DISABLE_SCREEN_SHARE_PROTECTIONS_FOR_APPS_AND_NOTIFICATIONS, 0) != 0;
- if (disableScreenShareProtections) {
- Slog.v(TAG,
- "Allowing keyguard capture as screenshare protections are disabled.");
- return true;
- }
-
- if (mPackageManager.checkPermission(RECORD_SENSITIVE_CONTENT,
- mProjectionGrant.packageName)
- == PackageManager.PERMISSION_GRANTED) {
- Slog.v(TAG,
- "Allowing keyguard capture for package with RECORD_SENSITIVE_CONTENT "
- + "permission");
- return true;
- }
- if (AppOpsManager.MODE_ALLOWED == mAppOps.noteOpNoThrow(AppOpsManager.OP_PROJECT_MEDIA,
- mProjectionGrant.uid, mProjectionGrant.packageName, /* attributionTag= */ null,
- "recording lockscreen")) {
- // Some tools use media projection by granting the OP_PROJECT_MEDIA app
- // op via a shell command. Those tools can be granted keyguard capture
- Slog.v(TAG,
- "Allowing keyguard capture for package with OP_PROJECT_MEDIA AppOp ");
- return true;
- }
- if (isProjectionAppHoldingAppStreamingRoleLocked()) {
- Slog.v(TAG,
- "Allowing keyguard capture for package holding app streaming role.");
- return true;
- }
- return SystemConfig.getInstance().getBugreportWhitelistedPackages()
- .contains(mProjectionGrant.packageName);
- }
- }
-
- @VisibleForTesting
- void onKeyguardLockedStateChanged(boolean isKeyguardLocked) {
- if (!isKeyguardLocked) return;
- synchronized (mLock) {
- if (mProjectionGrant != null && !canCaptureKeyguard()
- && mProjectionGrant.mVirtualDisplayId != INVALID_DISPLAY) {
- Slog.d(TAG, "Content Recording: Stopped MediaProjection"
- + " due to keyguard lock");
+ if (!mMediaProjectionStopController.isExemptFromStopping(mProjectionGrant, reason)) {
+ Slog.d(TAG, "Content Recording: Stopping MediaProjection due to "
+ + MediaProjectionStopController.stopReasonToString(reason));
mProjectionGrant.stop();
}
}
@@ -310,6 +247,8 @@
}
});
}
+
+ mMediaProjectionStopController.startTrackingStopReasons(mContext);
}
@Override
@@ -736,20 +675,6 @@
}
}
- /**
- * Application holding the app streaming role
- * ({@value AssociationRequest#DEVICE_PROFILE_APP_STREAMING}) are allowed to record the
- * lockscreen.
- *
- * @return true if the is held by the recording application.
- */
- @GuardedBy("mLock")
- private boolean isProjectionAppHoldingAppStreamingRoleLocked() {
- return mRoleManager.getRoleHoldersAsUser(AssociationRequest.DEVICE_PROFILE_APP_STREAMING,
- mContext.getUser())
- .contains(mProjectionGrant.packageName);
- }
-
private void dump(final PrintWriter pw) {
pw.println("MEDIA PROJECTION MANAGER (dumpsys media_projection)");
synchronized (mLock) {
@@ -957,18 +882,19 @@
public void requestConsentForInvalidProjection(@NonNull IMediaProjection projection) {
requestConsentForInvalidProjection_enforcePermission();
- if (android.companion.virtualdevice.flags.Flags.mediaProjectionKeyguardRestrictions()
- && mKeyguardManager.isKeyguardLocked()) {
- Slog.v(TAG, "Reusing token: Won't request consent while the keyguard is locked");
- return;
- }
-
synchronized (mLock) {
if (!isCurrentProjection(projection)) {
Slog.v(TAG, "Reusing token: Won't request consent again for a token that "
+ "isn't current");
return;
}
+
+ if (mMediaProjectionStopController.isStartForbidden(mProjectionGrant)) {
+ Slog.v(TAG,
+ "Reusing token: Won't request consent while MediaProjection is "
+ + "restricted");
+ return;
+ }
}
// Remove calling app identity before performing any privileged operations.
@@ -1076,14 +1002,13 @@
}
}
- @VisibleForTesting
final class MediaProjection extends IMediaProjection.Stub {
// Host app has 5 minutes to begin using the token before it is invalid.
// Some apps show a dialog for the user to interact with (selecting recording resolution)
// before starting capture, but after requesting consent.
- final long mDefaultTimeoutMs = Duration.ofMinutes(5).toMillis();
+ final long mDefaultTimeoutMillis = Duration.ofMinutes(5).toMillis();
// The creation timestamp in milliseconds, measured by {@link SystemClock#uptimeMillis}.
- private final long mCreateTimeMs;
+ private final long mCreateTimeMillis;
public final int uid;
public final String packageName;
public final UserHandle userHandle;
@@ -1092,7 +1017,7 @@
private final int mType;
// Values for tracking token validity.
// Timeout value to compare creation time against.
- private final long mTimeoutMs = mDefaultTimeoutMs;
+ private final long mTimeoutMillis = mDefaultTimeoutMillis;
private final int mDisplayId;
private IMediaProjectionCallback mCallback;
@@ -1123,7 +1048,7 @@
userHandle = new UserHandle(UserHandle.getUserId(uid));
mTargetSdkVersion = targetSdkVersion;
mIsPrivileged = isPrivileged;
- mCreateTimeMs = mClock.uptimeMillis();
+ mCreateTimeMillis = mClock.uptimeMillis();
mActivityManagerInternal.notifyMediaProjectionEvent(uid, asBinder(),
MEDIA_PROJECTION_TOKEN_EVENT_CREATED);
mDisplayId = displayId;
@@ -1284,7 +1209,7 @@
}
}
Slog.d(TAG, "Content Recording: handling stopping this projection token"
- + " createTime= " + mCreateTimeMs
+ + " createTime= " + mCreateTimeMillis
+ " countStarts= " + mCountStarts);
stopProjectionLocked(this);
mToken.unlinkToDeath(mDeathEater, 0);
@@ -1347,13 +1272,17 @@
return mDisplayId;
}
+ long getCreateTimeMillis() {
+ return mCreateTimeMillis;
+ }
+
@android.annotation.EnforcePermission(android.Manifest.permission.MANAGE_MEDIA_PROJECTION)
@Override
public boolean isValid() {
isValid_enforcePermission();
synchronized (mLock) {
- final long curMs = mClock.uptimeMillis();
- final boolean hasTimedOut = curMs - mCreateTimeMs > mTimeoutMs;
+ final long curMillis = mClock.uptimeMillis();
+ final boolean hasTimedOut = curMillis - mCreateTimeMillis > mTimeoutMillis;
final boolean virtualDisplayCreated = mVirtualDisplayId != INVALID_DISPLAY;
final boolean isValid =
!hasTimedOut && (mCountStarts <= 1) && !virtualDisplayCreated;
@@ -1381,12 +1310,15 @@
@Override
public void notifyVirtualDisplayCreated(int displayId) {
notifyVirtualDisplayCreated_enforcePermission();
- if (mKeyguardManager.isKeyguardLocked() && !canCaptureKeyguard()) {
- Slog.w(TAG, "Content Recording: Keyguard locked, aborting MediaProjection");
- stop();
- return;
- }
synchronized (mLock) {
+ if (mMediaProjectionStopController.isStartForbidden(mProjectionGrant)) {
+ Slog.w(TAG,
+ "Content Recording: MediaProjection start disallowed, aborting "
+ + "MediaProjection");
+ stop();
+ return;
+ }
+
mVirtualDisplayId = displayId;
// If prior session was does not have a valid display id, then update the display
diff --git a/services/core/java/com/android/server/media/projection/MediaProjectionStopController.java b/services/core/java/com/android/server/media/projection/MediaProjectionStopController.java
new file mode 100644
index 0000000..c018e6b
--- /dev/null
+++ b/services/core/java/com/android/server/media/projection/MediaProjectionStopController.java
@@ -0,0 +1,239 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.media.projection;
+
+import static android.Manifest.permission.RECORD_SENSITIVE_CONTENT;
+import static android.provider.Settings.Global.DISABLE_SCREEN_SHARE_PROTECTIONS_FOR_APPS_AND_NOTIFICATIONS;
+
+import android.app.AppOpsManager;
+import android.app.KeyguardManager;
+import android.app.role.RoleManager;
+import android.companion.AssociationRequest;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.Binder;
+import android.os.SystemClock;
+import android.provider.Settings;
+import android.telecom.TelecomManager;
+import android.telephony.TelephonyCallback;
+import android.telephony.TelephonyManager;
+import android.util.Slog;
+import android.view.Display;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.SystemConfig;
+
+import java.util.function.Consumer;
+
+/**
+ * Tracks events that should cause MediaProjection to stop
+ */
+public class MediaProjectionStopController {
+
+ private static final String TAG = "MediaProjectionStopController";
+ @VisibleForTesting
+ static final int STOP_REASON_UNKNOWN = 0;
+ @VisibleForTesting
+ static final int STOP_REASON_KEYGUARD = 1;
+ @VisibleForTesting
+ static final int STOP_REASON_CALL_END = 2;
+
+ private final TelephonyCallback mTelephonyCallback = new ProjectionTelephonyCallback();
+ private final Consumer<Integer> mStopReasonConsumer;
+ private final KeyguardManager mKeyguardManager;
+ private final TelecomManager mTelecomManager;
+ private final TelephonyManager mTelephonyManager;
+ private final AppOpsManager mAppOpsManager;
+ private final PackageManager mPackageManager;
+ private final RoleManager mRoleManager;
+ private final ContentResolver mContentResolver;
+
+ private boolean mIsInCall;
+ private long mLastCallStartTimeMillis;
+
+ public MediaProjectionStopController(Context context, Consumer<Integer> stopReasonConsumer) {
+ mStopReasonConsumer = stopReasonConsumer;
+ mKeyguardManager = context.getSystemService(KeyguardManager.class);
+ mTelecomManager = context.getSystemService(TelecomManager.class);
+ mTelephonyManager = context.getSystemService(TelephonyManager.class);
+ mAppOpsManager = context.getSystemService(AppOpsManager.class);
+ mPackageManager = context.getPackageManager();
+ mRoleManager = context.getSystemService(RoleManager.class);
+ mContentResolver = context.getContentResolver();
+ }
+
+ /**
+ * Start tracking stop reasons that may interrupt a MediaProjection session.
+ */
+ public void startTrackingStopReasons(Context context) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mKeyguardManager.addKeyguardLockedStateListener(context.getMainExecutor(),
+ this::onKeyguardLockedStateChanged);
+ if (com.android.media.projection.flags.Flags.stopMediaProjectionOnCallEnd()) {
+ callStateChanged();
+ mTelephonyManager.registerTelephonyCallback(context.getMainExecutor(),
+ mTelephonyCallback);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ /**
+ * Checks whether the given projection grant is exempt from stopping restrictions.
+ */
+ public boolean isExemptFromStopping(
+ MediaProjectionManagerService.MediaProjection projectionGrant, int stopReason) {
+ return isExempt(projectionGrant, stopReason, false);
+ }
+
+ /**
+ * Apps may disregard recording restrictions via MediaProjection for any stop reason if:
+ * - the "Disable Screenshare protections" developer option is enabled
+ * - the app is a holder of RECORD_SENSITIVE_CONTENT permission
+ * - the app holds the OP_PROJECT_MEDIA AppOp
+ * - the app holds the COMPANION_DEVICE_APP_STREAMING role
+ * - the app is one of the bugreport allowlisted packages
+ * - the current projection does not have an active VirtualDisplay associated with the
+ * MediaProjection session
+ */
+ private boolean isExempt(
+ MediaProjectionManagerService.MediaProjection projectionGrant, int stopReason,
+ boolean forStart) {
+ if (projectionGrant == null || projectionGrant.packageName == null) {
+ return true;
+ }
+ boolean disableScreenShareProtections = Settings.Global.getInt(mContentResolver,
+ DISABLE_SCREEN_SHARE_PROTECTIONS_FOR_APPS_AND_NOTIFICATIONS, 0) != 0;
+ if (disableScreenShareProtections) {
+ Slog.v(TAG, "Continuing MediaProjection as screenshare protections are disabled.");
+ return true;
+ }
+
+ if (mPackageManager.checkPermission(RECORD_SENSITIVE_CONTENT, projectionGrant.packageName)
+ == PackageManager.PERMISSION_GRANTED) {
+ Slog.v(TAG,
+ "Continuing MediaProjection for package with RECORD_SENSITIVE_CONTENT "
+ + "permission");
+ return true;
+ }
+ if (AppOpsManager.MODE_ALLOWED == mAppOpsManager.noteOpNoThrow(
+ AppOpsManager.OP_PROJECT_MEDIA, projectionGrant.uid,
+ projectionGrant.packageName, /* attributionTag= */ null, "recording lockscreen")) {
+ // Some tools use media projection by granting the OP_PROJECT_MEDIA app
+ // op via a shell command.
+ Slog.v(TAG, "Continuing MediaProjection for package with OP_PROJECT_MEDIA AppOp ");
+ return true;
+ }
+ if (mRoleManager.getRoleHoldersAsUser(AssociationRequest.DEVICE_PROFILE_APP_STREAMING,
+ projectionGrant.userHandle).contains(projectionGrant.packageName)) {
+ Slog.v(TAG, "Continuing MediaProjection for package holding app streaming role.");
+ return true;
+ }
+ if (SystemConfig.getInstance().getBugreportWhitelistedPackages().contains(
+ projectionGrant.packageName)) {
+ Slog.v(TAG, "Continuing MediaProjection for package allowlisted for bugreporting.");
+ return true;
+ }
+ if (!forStart && projectionGrant.getVirtualDisplayId() == Display.INVALID_DISPLAY) {
+ Slog.v(TAG, "Continuing MediaProjection as current projection has no VirtualDisplay.");
+ return true;
+ }
+
+ if (stopReason == STOP_REASON_CALL_END
+ && projectionGrant.getCreateTimeMillis() < mLastCallStartTimeMillis) {
+ Slog.v(TAG,
+ "Continuing MediaProjection as (phone) call started after MediaProjection was"
+ + " created.");
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * @return {@code true} if a MediaProjection session is currently in a restricted state.
+ */
+ public boolean isStartForbidden(
+ MediaProjectionManagerService.MediaProjection projectionGrant) {
+ if (!android.companion.virtualdevice.flags.Flags.mediaProjectionKeyguardRestrictions()) {
+ return false;
+ }
+
+ if (!mKeyguardManager.isKeyguardLocked()) {
+ return false;
+ }
+
+ if (isExempt(projectionGrant, STOP_REASON_UNKNOWN, true)) {
+ return false;
+ }
+ return true;
+ }
+
+ @VisibleForTesting
+ void onKeyguardLockedStateChanged(boolean isKeyguardLocked) {
+ if (!isKeyguardLocked) return;
+ if (!android.companion.virtualdevice.flags.Flags.mediaProjectionKeyguardRestrictions()) {
+ return;
+ }
+ mStopReasonConsumer.accept(STOP_REASON_KEYGUARD);
+ }
+
+ @VisibleForTesting
+ void callStateChanged() {
+ if (!com.android.media.projection.flags.Flags.stopMediaProjectionOnCallEnd()) {
+ return;
+ }
+ boolean isInCall = mTelecomManager.isInCall();
+ if (isInCall) {
+ mLastCallStartTimeMillis = SystemClock.uptimeMillis();
+ }
+ if (isInCall == mIsInCall) {
+ return;
+ }
+
+ if (mIsInCall && !isInCall) {
+ mStopReasonConsumer.accept(STOP_REASON_CALL_END);
+ }
+ mIsInCall = isInCall;
+ }
+
+ /**
+ * @return a String representation of the stop reason interrupting MediaProjection.
+ */
+ public static String stopReasonToString(int stopReason) {
+ switch (stopReason) {
+ case STOP_REASON_KEYGUARD -> {
+ return "STOP_REASON_KEYGUARD";
+ }
+ case STOP_REASON_CALL_END -> {
+ return "STOP_REASON_CALL_END";
+ }
+ }
+ return "";
+ }
+
+ private final class ProjectionTelephonyCallback extends TelephonyCallback implements
+ TelephonyCallback.CallStateListener {
+ @Override
+ public void onCallStateChanged(int state) {
+ callStateChanged();
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/media/quality/MediaQualityService.java b/services/core/java/com/android/server/media/quality/MediaQualityService.java
index c5c8a5e..8495b6c 100644
--- a/services/core/java/com/android/server/media/quality/MediaQualityService.java
+++ b/services/core/java/com/android/server/media/quality/MediaQualityService.java
@@ -25,11 +25,12 @@
import android.media.quality.IMediaQualityManager;
import android.media.quality.IPictureProfileCallback;
import android.media.quality.ISoundProfileCallback;
-import android.media.quality.MediaQualityContract.PictureQuality;
+import android.media.quality.MediaQualityContract;
import android.media.quality.ParamCapability;
import android.media.quality.PictureProfile;
+import android.media.quality.PictureProfileHandle;
import android.media.quality.SoundProfile;
-import android.os.Bundle;
+import android.os.PersistableBundle;
import android.util.Log;
import com.android.server.SystemService;
@@ -74,10 +75,10 @@
SQLiteDatabase db = mMediaQualityDbHelper.getWritableDatabase();
ContentValues values = new ContentValues();
- values.put(PictureQuality.PARAMETER_TYPE, pp.getProfileType());
- values.put(PictureQuality.PARAMETER_NAME, pp.getName());
- values.put(PictureQuality.PARAMETER_PACKAGE, pp.getPackageName());
- values.put(PictureQuality.PARAMETER_INPUT_ID, pp.getInputId());
+ values.put(MediaQualityContract.BaseParameters.PARAMETER_TYPE, pp.getProfileType());
+ values.put(MediaQualityContract.BaseParameters.PARAMETER_NAME, pp.getName());
+ values.put(MediaQualityContract.BaseParameters.PARAMETER_PACKAGE, pp.getPackageName());
+ values.put(MediaQualityContract.BaseParameters.PARAMETER_INPUT_ID, pp.getInputId());
values.put(mMediaQualityDbHelper.SETTINGS, bundleToJson(pp.getParameters()));
// id is auto-generated by SQLite upon successful insertion of row
@@ -98,8 +99,8 @@
public PictureProfile getPictureProfile(int type, String name) {
SQLiteDatabase db = mMediaQualityDbHelper.getReadableDatabase();
- String selection = PictureQuality.PARAMETER_TYPE + " = ? AND "
- + PictureQuality.PARAMETER_NAME + " = ?";
+ String selection = MediaQualityContract.BaseParameters.PARAMETER_TYPE + " = ? AND "
+ + MediaQualityContract.BaseParameters.PARAMETER_NAME + " = ?";
String[] selectionArguments = {Integer.toString(type), name};
try (
@@ -127,7 +128,7 @@
}
}
- private String bundleToJson(Bundle bundle) {
+ private String bundleToJson(PersistableBundle bundle) {
JSONObject jsonObject = new JSONObject();
if (bundle == null) {
return jsonObject.toString();
@@ -142,9 +143,9 @@
return jsonObject.toString();
}
- private Bundle jsonToBundle(String jsonString) {
+ private PersistableBundle jsonToBundle(String jsonString) {
JSONObject jsonObject = null;
- Bundle bundle = new Bundle();
+ PersistableBundle bundle = new PersistableBundle();
try {
jsonObject = new JSONObject(jsonString);
@@ -175,26 +176,26 @@
private String[] getAllPictureProfileColumns() {
return new String[]{
- PictureQuality.PARAMETER_ID,
- PictureQuality.PARAMETER_TYPE,
- PictureQuality.PARAMETER_NAME,
- PictureQuality.PARAMETER_INPUT_ID,
- PictureQuality.PARAMETER_PACKAGE,
+ MediaQualityContract.BaseParameters.PARAMETER_ID,
+ MediaQualityContract.BaseParameters.PARAMETER_TYPE,
+ MediaQualityContract.BaseParameters.PARAMETER_NAME,
+ MediaQualityContract.BaseParameters.PARAMETER_INPUT_ID,
+ MediaQualityContract.BaseParameters.PARAMETER_PACKAGE,
mMediaQualityDbHelper.SETTINGS
};
}
private PictureProfile getPictureProfileFromCursor(Cursor cursor) {
- String returnId = cursor.getString(
- cursor.getColumnIndexOrThrow(PictureQuality.PARAMETER_ID));
- int type = cursor.getInt(
- cursor.getColumnIndexOrThrow(PictureQuality.PARAMETER_TYPE));
- String name = cursor.getString(
- cursor.getColumnIndexOrThrow(PictureQuality.PARAMETER_NAME));
- String inputId = cursor.getString(
- cursor.getColumnIndexOrThrow(PictureQuality.PARAMETER_INPUT_ID));
- String packageName = cursor.getString(
- cursor.getColumnIndexOrThrow(PictureQuality.PARAMETER_PACKAGE));
+ String returnId = cursor.getString(cursor.getColumnIndexOrThrow(
+ MediaQualityContract.BaseParameters.PARAMETER_ID));
+ int type = cursor.getInt(cursor.getColumnIndexOrThrow(
+ MediaQualityContract.BaseParameters.PARAMETER_TYPE));
+ String name = cursor.getString(cursor.getColumnIndexOrThrow(
+ MediaQualityContract.BaseParameters.PARAMETER_NAME));
+ String inputId = cursor.getString(cursor.getColumnIndexOrThrow(
+ MediaQualityContract.BaseParameters.PARAMETER_INPUT_ID));
+ String packageName = cursor.getString(cursor.getColumnIndexOrThrow(
+ MediaQualityContract.BaseParameters.PARAMETER_PACKAGE));
String settings = cursor.getString(
cursor.getColumnIndexOrThrow(mMediaQualityDbHelper.SETTINGS));
return new PictureProfile(returnId, type, name, inputId,
@@ -203,7 +204,7 @@
@Override
public List<PictureProfile> getPictureProfilesByPackage(String packageName) {
- String selection = PictureQuality.PARAMETER_PACKAGE + " = ?";
+ String selection = MediaQualityContract.BaseParameters.PARAMETER_PACKAGE + " = ?";
String[] selectionArguments = {packageName};
return getPictureProfilesBasedOnConditions(getAllPictureProfileColumns(), selection,
selectionArguments);
@@ -216,7 +217,7 @@
@Override
public List<String> getPictureProfilePackageNames() {
- String [] column = {PictureQuality.PARAMETER_NAME};
+ String [] column = {MediaQualityContract.BaseParameters.PARAMETER_NAME};
List<PictureProfile> pictureProfiles = getPictureProfilesBasedOnConditions(column,
null, null);
List<String> packageNames = new ArrayList<>();
@@ -249,6 +250,11 @@
}
@Override
+ public PictureProfileHandle getPictureProfileHandle(String id) {
+ return null;
+ }
+
+ @Override
public SoundProfile createSoundProfile(SoundProfile pp) {
// TODO: implement
return pp;
@@ -262,7 +268,7 @@
// TODO: implement
}
@Override
- public SoundProfile getSoundProfileById(String id) {
+ public SoundProfile getSoundProfile(int type, String id) {
return null;
}
@Override
@@ -313,6 +319,15 @@
}
@Override
+ public List<String> getSoundProfileAllowList() {
+ return new ArrayList<>();
+ }
+
+ @Override
+ public void setSoundProfileAllowList(List<String> packages) {
+ }
+
+ @Override
public boolean isSupported() {
return false;
}
diff --git a/services/core/java/com/android/server/notification/DefaultDeviceEffectsApplier.java b/services/core/java/com/android/server/notification/DefaultDeviceEffectsApplier.java
index 925ba17..3e96afe 100644
--- a/services/core/java/com/android/server/notification/DefaultDeviceEffectsApplier.java
+++ b/services/core/java/com/android/server/notification/DefaultDeviceEffectsApplier.java
@@ -39,9 +39,11 @@
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.Keep;
/** Default implementation for {@link DeviceEffectsApplier}. */
-class DefaultDeviceEffectsApplier implements DeviceEffectsApplier {
+@Keep
+public class DefaultDeviceEffectsApplier implements DeviceEffectsApplier {
private static final String TAG = "DeviceEffectsApplier";
private static final String SUPPRESS_AMBIENT_DISPLAY_TOKEN =
"DefaultDeviceEffectsApplier:SuppressAmbientDisplay";
@@ -63,10 +65,10 @@
@GuardedBy("mRegisterReceiverLock")
private boolean mIsScreenOffReceiverRegistered;
- private ZenDeviceEffects mLastAppliedEffects = new ZenDeviceEffects.Builder().build();
+ protected ZenDeviceEffects mLastAppliedEffects = new ZenDeviceEffects.Builder().build();
private boolean mPendingNightMode;
- DefaultDeviceEffectsApplier(Context context) {
+ public DefaultDeviceEffectsApplier(Context context) {
mContext = context;
mColorDisplayManager = context.getSystemService(ColorDisplayManager.class);
mKeyguardManager = context.getSystemService(KeyguardManager.class);
@@ -79,58 +81,71 @@
@Override
public void apply(ZenDeviceEffects effects, @ConfigOrigin int origin) {
- Binder.withCleanCallingIdentity(() -> {
- if (mLastAppliedEffects.shouldSuppressAmbientDisplay()
- != effects.shouldSuppressAmbientDisplay()) {
- try {
- traceApplyDeviceEffect("suppressAmbientDisplay",
- effects.shouldSuppressAmbientDisplay());
- mPowerManager.suppressAmbientDisplay(SUPPRESS_AMBIENT_DISPLAY_TOKEN,
- effects.shouldSuppressAmbientDisplay());
- } catch (Exception e) {
- Slog.e(TAG, "Could not change AOD override", e);
- }
- }
-
- if (mLastAppliedEffects.shouldDisplayGrayscale() != effects.shouldDisplayGrayscale()) {
- if (mColorDisplayManager != null) {
- try {
- traceApplyDeviceEffect("displayGrayscale",
- effects.shouldDisplayGrayscale());
- mColorDisplayManager.setSaturationLevel(
- effects.shouldDisplayGrayscale() ? SATURATION_LEVEL_GRAYSCALE
- : SATURATION_LEVEL_FULL_COLOR);
- } catch (Exception e) {
- Slog.e(TAG, "Could not change grayscale override", e);
- }
- }
- }
-
- if (mLastAppliedEffects.shouldDimWallpaper() != effects.shouldDimWallpaper()) {
- if (mWallpaperManager != null) {
- try {
- traceApplyDeviceEffect("dimWallpaper", effects.shouldDimWallpaper());
- mWallpaperManager.setWallpaperDimAmount(
- effects.shouldDimWallpaper() ? WALLPAPER_DIM_AMOUNT_DIMMED
- : WALLPAPER_DIM_AMOUNT_NORMAL);
- } catch (Exception e) {
- Slog.e(TAG, "Could not change wallpaper override", e);
- }
- }
- }
-
- if (mLastAppliedEffects.shouldUseNightMode() != effects.shouldUseNightMode()) {
- try {
- updateOrScheduleNightMode(effects.shouldUseNightMode(), origin);
- } catch (Exception e) {
- Slog.e(TAG, "Could not change dark theme override", e);
- }
- }
- });
+ Binder.withCleanCallingIdentity(
+ () -> {
+ maybeSuppressAmbientDisplay(effects.shouldSuppressAmbientDisplay());
+ maybeDisplayGrayscale(effects.shouldDisplayGrayscale());
+ maybeDimWallpaper(effects.shouldDimWallpaper());
+ maybeUseNightMode(effects.shouldUseNightMode(), origin);
+ });
mLastAppliedEffects = effects;
}
+ protected void maybeSuppressAmbientDisplay(boolean shouldSuppressAmbientDisplay) {
+ if (mLastAppliedEffects.shouldSuppressAmbientDisplay() != shouldSuppressAmbientDisplay) {
+ try {
+ traceApplyDeviceEffect("suppressAmbientDisplay", shouldSuppressAmbientDisplay);
+ mPowerManager.suppressAmbientDisplay(
+ SUPPRESS_AMBIENT_DISPLAY_TOKEN, shouldSuppressAmbientDisplay);
+ } catch (Exception e) {
+ Slog.e(TAG, "Could not change AOD override", e);
+ }
+ }
+ }
+
+ protected void maybeDisplayGrayscale(boolean shouldDisplayGrayscale) {
+ if (mLastAppliedEffects.shouldDisplayGrayscale() != shouldDisplayGrayscale) {
+ if (mColorDisplayManager != null) {
+ try {
+ traceApplyDeviceEffect("displayGrayscale", shouldDisplayGrayscale);
+ mColorDisplayManager.setSaturationLevel(
+ shouldDisplayGrayscale
+ ? SATURATION_LEVEL_GRAYSCALE
+ : SATURATION_LEVEL_FULL_COLOR);
+ } catch (Exception e) {
+ Slog.e(TAG, "Could not change grayscale override", e);
+ }
+ }
+ }
+ }
+
+ protected void maybeDimWallpaper(boolean shouldDimWallpaper) {
+ if (mLastAppliedEffects.shouldDimWallpaper() != shouldDimWallpaper) {
+ if (mWallpaperManager != null) {
+ try {
+ traceApplyDeviceEffect("dimWallpaper", shouldDimWallpaper);
+ mWallpaperManager.setWallpaperDimAmount(
+ shouldDimWallpaper
+ ? WALLPAPER_DIM_AMOUNT_DIMMED
+ : WALLPAPER_DIM_AMOUNT_NORMAL);
+ } catch (Exception e) {
+ Slog.e(TAG, "Could not change wallpaper override", e);
+ }
+ }
+ }
+ }
+
+ protected void maybeUseNightMode(boolean shouldUseNightMode, @ConfigOrigin int origin) {
+ if (mLastAppliedEffects.shouldUseNightMode() != shouldUseNightMode) {
+ try {
+ updateOrScheduleNightMode(shouldUseNightMode, origin);
+ } catch (Exception e) {
+ Slog.e(TAG, "Could not change dark theme override", e);
+ }
+ }
+ }
+
private void updateOrScheduleNightMode(boolean useNightMode, @ConfigOrigin int origin) {
mPendingNightMode = useNightMode;
diff --git a/services/core/java/com/android/server/notification/GroupHelper.java b/services/core/java/com/android/server/notification/GroupHelper.java
index 5914dbe..7fd9620 100644
--- a/services/core/java/com/android/server/notification/GroupHelper.java
+++ b/services/core/java/com/android/server/notification/GroupHelper.java
@@ -92,10 +92,16 @@
static final int REGROUP_REASON_CHANNEL_UPDATE = 0;
// Regrouping needed because of notification bundling
static final int REGROUP_REASON_BUNDLE = 1;
+ // Regrouping needed because of notification unbundling
+ static final int REGROUP_REASON_UNBUNDLE = 2;
+ // Regrouping needed because of notification unbundling + the original group summary exists
+ static final int REGROUP_REASON_UNBUNDLE_ORIGINAL_GROUP = 3;
@IntDef(prefix = { "REGROUP_REASON_" }, value = {
REGROUP_REASON_CHANNEL_UPDATE,
REGROUP_REASON_BUNDLE,
+ REGROUP_REASON_UNBUNDLE,
+ REGROUP_REASON_UNBUNDLE_ORIGINAL_GROUP,
})
@Retention(RetentionPolicy.SOURCE)
@interface RegroupingReason {}
@@ -103,7 +109,6 @@
private final Callback mCallback;
private final int mAutoGroupAtCount;
private final int mAutogroupSparseGroupsAtCount;
- private final int mAutoGroupRegroupingAtCount;
private final Context mContext;
private final PackageManager mPackageManager;
private boolean mIsTestHarnessExempted;
@@ -190,11 +195,6 @@
mContext = context;
mPackageManager = packageManager;
mAutogroupSparseGroupsAtCount = autoGroupSparseGroupsAtCount;
- if (notificationRegroupOnClassification()) {
- mAutoGroupRegroupingAtCount = 1;
- } else {
- mAutoGroupRegroupingAtCount = mAutoGroupAtCount;
- }
NOTIFICATION_SHADE_SECTIONS = getNotificationShadeSections();
}
@@ -797,7 +797,8 @@
Slog.v(TAG, "isGroupChildInDifferentBundleThanSummary: " + record);
}
moveNotificationsToNewSection(record.getUserId(), pkgName,
- List.of(new NotificationMoveOp(record, null, fullAggregateGroupKey)));
+ List.of(new NotificationMoveOp(record, null, fullAggregateGroupKey)),
+ REGROUP_REASON_BUNDLE);
return;
}
}
@@ -945,6 +946,27 @@
}
}
+ /**
+ * Called when a notification that was classified (bundled) is restored to its original channel.
+ * The notification will be restored to its original group, if any/if summary still exists.
+ * Otherwise it will be moved to the appropriate section as an ungrouped notification.
+ *
+ * @param record the notification which had its channel updated
+ * @param originalSummaryExists the original group summary exists
+ */
+ @FlaggedApi(android.service.notification.Flags.FLAG_NOTIFICATION_FORCE_GROUPING)
+ public void onNotificationUnbundled(final NotificationRecord record,
+ final boolean originalSummaryExists) {
+ synchronized (mAggregatedNotifications) {
+ ArrayMap<String, NotificationRecord> notificationsToCheck = new ArrayMap<>();
+ notificationsToCheck.put(record.getKey(), record);
+ regroupNotifications(record.getUserId(), record.getSbn().getPackageName(),
+ notificationsToCheck,
+ originalSummaryExists ? REGROUP_REASON_UNBUNDLE_ORIGINAL_GROUP
+ : REGROUP_REASON_UNBUNDLE);
+ }
+ }
+
@GuardedBy("mAggregatedNotifications")
private void regroupNotifications(int userId, String pkgName,
ArrayMap<String, NotificationRecord> notificationsToCheck,
@@ -973,7 +995,7 @@
// Batch move to new section
if (!notificationsToMove.isEmpty()) {
- moveNotificationsToNewSection(userId, pkgName, notificationsToMove);
+ moveNotificationsToNewSection(userId, pkgName, notificationsToMove, regroupingReason);
}
}
@@ -1093,7 +1115,7 @@
@GuardedBy("mAggregatedNotifications")
private void moveNotificationsToNewSection(final int userId, final String pkgName,
- final List<NotificationMoveOp> notificationsToMove) {
+ final List<NotificationMoveOp> notificationsToMove, int regroupingReason) {
record GroupUpdateOp(FullyQualifiedGroupKey groupKey, NotificationRecord record,
boolean hasSummary) { }
// Bundled operations to apply to groups affected by the channel update
@@ -1111,7 +1133,8 @@
if (DEBUG) {
Log.i(TAG,
"moveNotificationToNewSection: " + record + " " + newFullAggregateGroupKey
- + " from: " + oldFullAggregateGroupKey);
+ + " from: " + oldFullAggregateGroupKey + " regroupingReason: "
+ + regroupingReason);
}
// Update/remove aggregate summary for old group
@@ -1140,28 +1163,35 @@
// Add moved notifications to the ungrouped list for new group and do grouping
// after all notifications have been handled
if (newFullAggregateGroupKey != null) {
- final ArrayMap<String, NotificationAttributes> newAggregatedNotificationsAttrs =
+ if (notificationRegroupOnClassification()
+ && regroupingReason == REGROUP_REASON_UNBUNDLE_ORIGINAL_GROUP) {
+ // Just reset override group key, original summary exists
+ // => will be grouped back to its original group
+ record.setOverrideGroupKey(null);
+ } else {
+ final ArrayMap<String, NotificationAttributes> newAggregatedNotificationsAttrs =
mAggregatedNotifications.getOrDefault(newFullAggregateGroupKey,
new ArrayMap<>());
- boolean hasSummary = !newAggregatedNotificationsAttrs.isEmpty();
- ArrayMap<String, NotificationAttributes> ungrouped =
+ boolean hasSummary = !newAggregatedNotificationsAttrs.isEmpty();
+ ArrayMap<String, NotificationAttributes> ungrouped =
mUngroupedAbuseNotifications.getOrDefault(newFullAggregateGroupKey,
new ArrayMap<>());
- ungrouped.put(record.getKey(), new NotificationAttributes(
+ ungrouped.put(record.getKey(), new NotificationAttributes(
record.getFlags(),
record.getNotification().getSmallIcon(),
record.getNotification().color,
record.getNotification().visibility,
record.getNotification().getGroupAlertBehavior(),
record.getChannel().getId()));
- mUngroupedAbuseNotifications.put(newFullAggregateGroupKey, ungrouped);
+ mUngroupedAbuseNotifications.put(newFullAggregateGroupKey, ungrouped);
- record.setOverrideGroupKey(null);
+ record.setOverrideGroupKey(null);
- // Only add once, for triggering notification
- if (!groupsToUpdate.containsKey(newFullAggregateGroupKey)) {
- groupsToUpdate.put(newFullAggregateGroupKey,
- new GroupUpdateOp(newFullAggregateGroupKey, record, hasSummary));
+ // Only add once, for triggering notification
+ if (!groupsToUpdate.containsKey(newFullAggregateGroupKey)) {
+ groupsToUpdate.put(newFullAggregateGroupKey,
+ new GroupUpdateOp(newFullAggregateGroupKey, record, hasSummary));
+ }
}
}
}
@@ -1176,7 +1206,7 @@
NotificationRecord triggeringNotification = groupsToUpdate.get(groupKey).record;
boolean hasSummary = groupsToUpdate.get(groupKey).hasSummary;
//Group needs to be created/updated
- if (ungrouped.size() >= mAutoGroupRegroupingAtCount
+ if (ungrouped.size() >= mAutoGroupAtCount
|| (hasSummary && !aggregatedNotificationsAttrs.isEmpty())) {
NotificationSectioner sectioner = getSection(triggeringNotification);
if (sectioner == null) {
@@ -1436,7 +1466,8 @@
}
}
- private ArrayMap<String, NotificationRecord> getSparseGroups(
+ @VisibleForTesting
+ protected ArrayMap<String, NotificationRecord> getSparseGroups(
final FullyQualifiedGroupKey fullAggregateGroupKey,
final List<NotificationRecord> notificationList,
final Map<String, NotificationRecord> summaryByGroupKey,
@@ -1448,8 +1479,8 @@
&& summary.getUserId() == fullAggregateGroupKey.userId
&& summary.getSbn().isAppGroup()
&& !summary.getGroupKey().equals(fullAggregateGroupKey.toString())) {
- int numChildren = getNumChildrenForGroup(summary.getSbn().getGroup(),
- notificationList);
+ int numChildren = getNumChildrenForGroupWithSection(summary.getSbn().getGroup(),
+ notificationList, sectioner);
if (numChildren > 0 && numChildren < MIN_CHILD_COUNT_TO_AVOID_FORCE_GROUPING) {
sparseGroups.put(summary.getGroupKey(), summary);
}
@@ -1459,6 +1490,43 @@
return sparseGroups;
}
+ /**
+ * Get the number of children of a group if all match a certain section.
+ * Used for force grouping sparse groups, where the summary may match a section but the
+ * child notifications do not: ie. conversations
+ *
+ * @param groupKey the group key (name)
+ * @param notificationList all notifications list
+ * @param sectioner the section to match
+ * @return number of children in that group or -1 if section does not match
+ */
+ private int getNumChildrenForGroupWithSection(final String groupKey,
+ final List<NotificationRecord> notificationList,
+ final NotificationSectioner sectioner) {
+ int numChildren = 0;
+ for (NotificationRecord r : notificationList) {
+ if (!r.getNotification().isGroupSummary() && groupKey.equals(r.getSbn().getGroup())) {
+ NotificationSectioner childSection = getSection(r);
+ if (childSection == null || childSection != sectioner) {
+ if (DEBUG) {
+ Slog.i(TAG,
+ "getNumChildrenForGroupWithSection skip because invalid section: "
+ + groupKey + " r: " + r);
+ }
+ return -1;
+ } else {
+ numChildren++;
+ }
+ }
+ }
+
+ if (DEBUG) {
+ Slog.i(TAG,
+ "getNumChildrenForGroupWithSection " + groupKey + " numChild: " + numChildren);
+ }
+ return numChildren;
+ }
+
@GuardedBy("mAggregatedNotifications")
private void cacheCanceledSummary(NotificationRecord record) {
final FullyQualifiedGroupKey groupKey = new FullyQualifiedGroupKey(record.getUserId(),
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 207764b..5182dfe 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -7180,13 +7180,16 @@
Slog.i(TAG, "Removing app summary (all children bundled): "
+ groupSummary);
}
- canceledSummary = groupSummary;
- mSummaryByGroupKey.remove(oldGroupKey);
- cancelNotification(Binder.getCallingUid(), Binder.getCallingPid(),
+ if (convertSummaryToNotificationLocked(groupSummary.getKey())) {
+ groupSummary.isCanceled = true;
+ canceledSummary = groupSummary;
+ mSummaryByGroupKey.remove(oldGroupKey);
+ cancelNotification(Binder.getCallingUid(), Binder.getCallingPid(),
groupSummary.getSbn().getPackageName(),
groupSummary.getSbn().getTag(),
groupSummary.getSbn().getId(), 0, 0, false, groupSummary.getUserId(),
NotificationListenerService.REASON_GROUP_OPTIMIZATION, null);
+ }
}
}
}
@@ -11937,16 +11940,19 @@
TrimCache trimCache = new TrimCache(sbn);
final INotificationListener assistant = (INotificationListener) info.service;
final StatusBarNotification sbnToPost = trimCache.ForListener(info);
- final StatusBarNotificationHolder sbnHolder =
- new StatusBarNotificationHolder(sbnToPost);
+ final NotificationRankingUpdate update = makeRankingUpdateLocked(info);
+
try {
- if (debug) {
- Slog.v(TAG,
- "calling onNotificationEnqueuedWithChannel " + sbnHolder);
+ if (android.app.Flags.noSbnholder()) {
+ assistant.onNotificationEnqueuedWithChannelFull(sbnToPost,
+ r.getChannel(), update);
+ } else {
+ final StatusBarNotificationHolder sbnHolder =
+ new StatusBarNotificationHolder(sbnToPost);
+
+ assistant.onNotificationEnqueuedWithChannel(sbnHolder, r.getChannel(),
+ update);
}
- final NotificationRankingUpdate update = makeRankingUpdateLocked(info);
- assistant.onNotificationEnqueuedWithChannel(sbnHolder, r.getChannel(),
- update);
} catch (RemoteException ex) {
Slog.e(TAG, "unable to notify assistant (enqueued): " + assistant, ex);
}
@@ -11966,7 +11972,7 @@
r.getSbn(),
r.getNotificationType(),
true /* sameUserOnly */,
- (assistant, sbnHolder) -> {
+ (assistant, unused) -> {
try {
assistant.onNotificationVisibilityChanged(key, isVisible);
} catch (RemoteException ex) {
@@ -11986,7 +11992,7 @@
sbn,
notificationType,
true /* sameUserOnly */,
- (assistant, sbnHolder) -> {
+ (assistant, unused) -> {
try {
assistant.onNotificationExpansionChanged(key, isUserAction, isExpanded);
} catch (RemoteException ex) {
@@ -12003,7 +12009,7 @@
r.getSbn(),
r.getNotificationType(),
true /* sameUserOnly */,
- (assistant, sbnHolder) -> {
+ (assistant, unused) -> {
try {
assistant.onNotificationDirectReply(key);
} catch (RemoteException ex) {
@@ -12021,7 +12027,7 @@
sbn,
notificationType,
true /* sameUserOnly */,
- (assistant, sbnHolder) -> {
+ (assistant, unused) -> {
try {
assistant.onSuggestedReplySent(
key,
@@ -12044,7 +12050,7 @@
r.getSbn(),
r.getNotificationType(),
true /* sameUserOnly */,
- (assistant, sbnHolder) -> {
+ (assistant, unused) -> {
try {
assistant.onActionClicked(
key,
@@ -12069,10 +12075,17 @@
r.getSbn(),
r.getNotificationType(),
true /* sameUserOnly */,
- (assistant, sbnHolder) -> {
+ (assistant, sbnToPost) -> {
try {
- assistant.onNotificationSnoozedUntilContext(
- sbnHolder, snoozeCriterionId);
+ if (android.app.Flags.noSbnholder()) {
+ assistant.onNotificationSnoozedUntilContextFull(
+ sbnToPost, snoozeCriterionId);
+ } else {
+ final StatusBarNotificationHolder sbnHolder =
+ new StatusBarNotificationHolder(sbnToPost);
+ assistant.onNotificationSnoozedUntilContext(
+ sbnHolder, snoozeCriterionId);
+ }
} catch (RemoteException ex) {
Slog.e(TAG, "unable to notify assistant (snoozed): " + assistant, ex);
}
@@ -12086,7 +12099,7 @@
r.getSbn(),
r.getNotificationType(),
true /* sameUserOnly */,
- (assistant, sbnHolder) -> {
+ (assistant, unused) -> {
try {
assistant.onNotificationClicked(key);
} catch (RemoteException ex) {
@@ -12129,7 +12142,7 @@
final StatusBarNotification sbn,
int notificationType,
boolean sameUserOnly,
- BiConsumer<INotificationListener, StatusBarNotificationHolder> callback) {
+ BiConsumer<INotificationListener, StatusBarNotification> callback) {
TrimCache trimCache = new TrimCache(sbn);
// There should be only one, but it's a list, so while we enforce
// singularity elsewhere, we keep it general here, to avoid surprises.
@@ -12151,9 +12164,7 @@
}
final INotificationListener assistant = (INotificationListener) info.service;
final StatusBarNotification sbnToPost = trimCache.ForListener(info);
- final StatusBarNotificationHolder sbnHolder =
- new StatusBarNotificationHolder(sbnToPost);
- mHandler.post(() -> callback.accept(assistant, sbnHolder));
+ mHandler.post(() -> callback.accept(assistant, sbnToPost));
}
}
@@ -13399,9 +13410,13 @@
private void notifyPosted(final ManagedServiceInfo info,
final StatusBarNotification sbn, NotificationRankingUpdate rankingUpdate) {
final INotificationListener listener = (INotificationListener) info.service;
- StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
try {
- listener.onNotificationPosted(sbnHolder, rankingUpdate);
+ if (android.app.Flags.noSbnholder()) {
+ listener.onNotificationPostedFull(sbn, rankingUpdate);
+ } else {
+ StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
+ listener.onNotificationPosted(sbnHolder, rankingUpdate);
+ }
} catch (DeadObjectException ex) {
Slog.wtf(TAG, "unable to notify listener (posted): " + info, ex);
} catch (RemoteException ex) {
@@ -13412,7 +13427,6 @@
private void notifyRemoved(ManagedServiceInfo info, StatusBarNotification sbn,
NotificationRankingUpdate rankingUpdate, NotificationStats stats, int reason) {
final INotificationListener listener = (INotificationListener) info.service;
- StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
try {
if (!CompatChanges.isChangeEnabled(NOTIFICATION_CANCELLATION_REASONS, info.uid)
&& (reason == REASON_CHANNEL_REMOVED || reason == REASON_CLEAR_DATA)) {
@@ -13424,7 +13438,12 @@
&& reason == REASON_ASSISTANT_CANCEL) {
reason = REASON_LISTENER_CANCEL;
}
- listener.onNotificationRemoved(sbnHolder, rankingUpdate, stats, reason);
+ if (android.app.Flags.noSbnholder()) {
+ listener.onNotificationRemovedFull(sbn, rankingUpdate, stats, reason);
+ } else {
+ StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
+ listener.onNotificationRemoved(sbnHolder, rankingUpdate, stats, reason);
+ }
} catch (DeadObjectException ex) {
Slog.wtf(TAG, "unable to notify listener (removed): " + info, ex);
} catch (RemoteException ex) {
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index ca4f83f..81dc38a 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -1015,7 +1015,13 @@
private static void applyConditionAndReconsiderOverride(ZenRule rule, Condition condition,
int origin) {
if (Flags.modesApi() && Flags.modesUi()) {
- if (origin == ORIGIN_USER_IN_SYSTEMUI && condition != null
+ if (isImplicitRuleId(rule.id)) {
+ // Implicit rules do not use overrides, and always apply conditions directly.
+ // This is compatible with the previous behavior (where the package set the
+ // interruption filter, and no "snoozing" took place if the user changed it later).
+ rule.condition = condition;
+ rule.resetConditionOverride();
+ } else if (origin == ORIGIN_USER_IN_SYSTEMUI && condition != null
&& condition.source == SOURCE_USER_ACTION) {
// Apply as override, instead of actual condition.
// If the new override is the reverse of a previous (still active) override, try
diff --git a/services/core/java/com/android/server/pm/BroadcastHelper.java b/services/core/java/com/android/server/pm/BroadcastHelper.java
index 9f4b9f1..6d54be8 100644
--- a/services/core/java/com/android/server/pm/BroadcastHelper.java
+++ b/services/core/java/com/android/server/pm/BroadcastHelper.java
@@ -58,6 +58,7 @@
import android.os.storage.VolumeInfo;
import android.provider.DeviceConfig;
import android.stats.storage.StorageEnums;
+import android.text.TextUtils;
import android.util.IntArray;
import android.util.Log;
import android.util.Pair;
@@ -355,7 +356,8 @@
@Nullable int[] userIds,
@Nullable int[] instantUserIds,
@Nullable SparseArray<int[]> broadcastAllowList,
- @NonNull AndroidPackage pkg) {
+ @NonNull AndroidPackage pkg,
+ @NonNull String[] sharedUidPackages) {
final boolean isForWholeApp = componentNames.contains(packageName);
if (isForWholeApp || !android.content.pm.Flags.reduceBroadcastsForComponentStateChanges()) {
sendPackageChangedBroadcastWithPermissions(packageName, dontKillApp, componentNames,
@@ -374,20 +376,36 @@
exportedComponentNames.removeAll(notExportedComponentNames);
if (!notExportedComponentNames.isEmpty()) {
- // Limit sending of the PACKAGE_CHANGED broadcast to only the system and the
- // application itself when the component is not exported.
+ // Limit sending of the PACKAGE_CHANGED broadcast to only the system, the application
+ // itself and applications with the same UID when the component is not exported.
// First, send the PACKAGE_CHANGED broadcast to the system.
- sendPackageChangedBroadcastWithPermissions(packageName, dontKillApp,
- notExportedComponentNames, packageUid, reason, userIds, instantUserIds,
- broadcastAllowList, "android" /* targetPackageName */,
- new String[]{PERMISSION_PACKAGE_CHANGED_BROADCAST_ON_COMPONENT_STATE_CHANGED});
+ if (!TextUtils.equals(packageName, "android")) {
+ sendPackageChangedBroadcastWithPermissions(packageName, dontKillApp,
+ notExportedComponentNames, packageUid, reason, userIds, instantUserIds,
+ broadcastAllowList, "android" /* targetPackageName */,
+ new String[]{
+ PERMISSION_PACKAGE_CHANGED_BROADCAST_ON_COMPONENT_STATE_CHANGED});
+ }
// Second, send the PACKAGE_CHANGED broadcast to the application itself.
sendPackageChangedBroadcastWithPermissions(packageName, dontKillApp,
notExportedComponentNames, packageUid, reason, userIds, instantUserIds,
broadcastAllowList, packageName /* targetPackageName */,
null /* requiredPermissions */);
+
+ // Third, send the PACKAGE_CHANGED broadcast to the applications with the same UID.
+ for (int i = 0; i < sharedUidPackages.length; i++) {
+ final String sharedPackage = sharedUidPackages[i];
+ if (TextUtils.equals(packageName, sharedPackage)) {
+ continue;
+ }
+ sendPackageChangedBroadcastWithPermissions(packageName, dontKillApp,
+ notExportedComponentNames, packageUid, reason, userIds, instantUserIds,
+ broadcastAllowList, sharedPackage /* targetPackageName */,
+ null /* requiredPermissions */);
+ }
+
}
if (!exportedComponentNames.isEmpty()) {
@@ -936,7 +954,8 @@
isInstantApp ? null : snapshot.getVisibilityAllowLists(packageName, userIds);
mHandler.post(() -> sendPackageChangedBroadcastInternal(
packageName, dontKillApp, componentNames, packageUid, reason, userIds,
- instantUserIds, broadcastAllowList, setting.getPkg()));
+ instantUserIds, broadcastAllowList, setting.getPkg(),
+ snapshot.getSharedUserPackagesForPackage(packageName, userId)));
mPackageMonitorCallbackHelper.notifyPackageChanged(packageName, dontKillApp, componentNames,
packageUid, reason, userIds, instantUserIds, broadcastAllowList, mHandler);
}
diff --git a/services/core/java/com/android/server/pm/InstallDependencyHelper.java b/services/core/java/com/android/server/pm/InstallDependencyHelper.java
index 745665b..527d680 100644
--- a/services/core/java/com/android/server/pm/InstallDependencyHelper.java
+++ b/services/core/java/com/android/server/pm/InstallDependencyHelper.java
@@ -17,51 +17,240 @@
package com.android.server.pm;
import static android.content.pm.PackageManager.INSTALL_FAILED_MISSING_SHARED_LIBRARY;
+import static android.os.Process.SYSTEM_UID;
+import android.annotation.NonNull;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ResolveInfo;
import android.content.pm.SharedLibraryInfo;
+import android.content.pm.dependencyinstaller.DependencyInstallerCallback;
+import android.content.pm.dependencyinstaller.IDependencyInstallerCallback;
+import android.content.pm.dependencyinstaller.IDependencyInstallerService;
import android.content.pm.parsing.PackageLite;
+import android.os.Handler;
import android.os.OutcomeReceiver;
+import android.os.Process;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.infra.AndroidFuture;
+import com.android.internal.infra.ServiceConnector;
import java.util.List;
+import java.util.concurrent.TimeUnit;
/**
* Helper class to interact with SDK Dependency Installer service.
*/
public class InstallDependencyHelper {
- private final SharedLibrariesImpl mSharedLibraries;
+ private static final String TAG = InstallDependencyHelper.class.getSimpleName();
+ private static final boolean DEBUG = true;
+ private static final String ACTION_INSTALL_DEPENDENCY =
+ "android.intent.action.INSTALL_DEPENDENCY";
+ // The maximum amount of time to wait before the system unbinds from the verifier.
+ private static final long UNBIND_TIMEOUT_MILLIS = TimeUnit.HOURS.toMillis(6);
+ private static final long REQUEST_TIMEOUT_MILLIS = TimeUnit.MINUTES.toMillis(1);
- InstallDependencyHelper(SharedLibrariesImpl sharedLibraries) {
+ private final SharedLibrariesImpl mSharedLibraries;
+ private final Context mContext;
+ private final Object mRemoteServiceLock = new Object();
+
+ @GuardedBy("mRemoteServiceLock")
+ private ServiceConnector<IDependencyInstallerService> mRemoteService = null;
+
+ InstallDependencyHelper(Context context, SharedLibrariesImpl sharedLibraries) {
+ mContext = context;
mSharedLibraries = sharedLibraries;
}
- void resolveLibraryDependenciesIfNeeded(PackageLite pkg,
- OutcomeReceiver<Void, PackageManagerException> callback) {
- final List<SharedLibraryInfo> missing;
+ void resolveLibraryDependenciesIfNeeded(PackageLite pkg, Computer snapshot, int userId,
+ Handler handler, OutcomeReceiver<Void, PackageManagerException> origCallback) {
+ CallOnceProxy callback = new CallOnceProxy(handler, origCallback);
try {
- missing = mSharedLibraries.collectMissingSharedLibraryInfos(pkg);
+ resolveLibraryDependenciesIfNeededInternal(pkg, snapshot, userId, handler, callback);
} catch (PackageManagerException e) {
callback.onError(e);
- return;
+ } catch (Exception e) {
+ onError(callback, e.getMessage());
}
+ }
+
+
+ private void resolveLibraryDependenciesIfNeededInternal(PackageLite pkg, Computer snapshot,
+ int userId, Handler handler, CallOnceProxy callback) throws PackageManagerException {
+ final List<SharedLibraryInfo> missing =
+ mSharedLibraries.collectMissingSharedLibraryInfos(pkg);
if (missing.isEmpty()) {
+ if (DEBUG) {
+ Slog.i(TAG, "No missing dependency for " + pkg);
+ }
// No need for dependency resolution. Move to installation directly.
callback.onResult(null);
return;
}
- try {
- bindToDependencyInstaller();
- } catch (Exception e) {
- PackageManagerException pe = new PackageManagerException(
- INSTALL_FAILED_MISSING_SHARED_LIBRARY, e.getMessage());
- callback.onError(pe);
+ if (!bindToDependencyInstallerIfNeeded(userId, handler, snapshot)) {
+ onError(callback, "Dependency Installer Service not found");
+ return;
+ }
+
+ IDependencyInstallerCallback serviceCallback = new IDependencyInstallerCallback.Stub() {
+ @Override
+ public void onAllDependenciesResolved(int[] sessionIds) throws RemoteException {
+ // TODO(b/372862145): Implement waiting for sessions to finish installation
+ callback.onResult(null);
+ }
+
+ @Override
+ public void onFailureToResolveAllDependencies() throws RemoteException {
+ onError(callback, "Failed to resolve all dependencies automatically");
+ }
+ };
+
+ boolean scheduleSuccess;
+ synchronized (mRemoteServiceLock) {
+ scheduleSuccess = mRemoteService.run(service -> {
+ service.onDependenciesRequired(missing,
+ new DependencyInstallerCallback(serviceCallback.asBinder()));
+ });
+ }
+ if (!scheduleSuccess) {
+ onError(callback, "Failed to schedule job on Dependency Installer Service");
}
}
- private void bindToDependencyInstaller() {
- throw new IllegalStateException("Failed to bind to Dependency Installer");
+ private void onError(CallOnceProxy callback, String msg) {
+ PackageManagerException pe = new PackageManagerException(
+ INSTALL_FAILED_MISSING_SHARED_LIBRARY, msg);
+ callback.onError(pe);
}
+ private boolean bindToDependencyInstallerIfNeeded(int userId, Handler handler,
+ Computer snapshot) {
+ synchronized (mRemoteServiceLock) {
+ if (mRemoteService != null) {
+ if (DEBUG) {
+ Slog.i(TAG, "DependencyInstallerService already bound");
+ }
+ return true;
+ }
+ }
+ Intent serviceIntent = new Intent(ACTION_INSTALL_DEPENDENCY);
+ // TODO(b/372862145): Use RoleManager to find the package name
+ List<ResolveInfo> resolvedIntents = snapshot.queryIntentServicesInternal(
+ serviceIntent, /*resolvedType=*/ null, /*flags=*/0,
+ userId, SYSTEM_UID, Process.INVALID_PID,
+ /*includeInstantApps*/ false, /*resolveForStart*/ false);
+
+ if (resolvedIntents.isEmpty()) {
+ return false;
+ }
+
+
+ ResolveInfo resolveInfo = resolvedIntents.getFirst();
+ ComponentName componentName = resolveInfo.getComponentInfo().getComponentName();
+ serviceIntent.setComponent(componentName);
+
+ ServiceConnector<IDependencyInstallerService> serviceConnector =
+ new ServiceConnector.Impl<IDependencyInstallerService>(mContext, serviceIntent,
+ Context.BIND_AUTO_CREATE, userId,
+ IDependencyInstallerService.Stub::asInterface) {
+ @Override
+ protected Handler getJobHandler() {
+ return handler;
+ }
+
+ @Override
+ protected long getRequestTimeoutMs() {
+ return REQUEST_TIMEOUT_MILLIS;
+ }
+
+ @Override
+ protected long getAutoDisconnectTimeoutMs() {
+ return UNBIND_TIMEOUT_MILLIS;
+ }
+ };
+
+
+ synchronized (mRemoteServiceLock) {
+ // Some other thread managed to connect to the service first
+ if (mRemoteService != null) {
+ return true;
+ }
+ mRemoteService = serviceConnector;
+ mRemoteService.setServiceLifecycleCallbacks(
+ new ServiceConnector.ServiceLifecycleCallbacks<>() {
+ @Override
+ public void onDisconnected(@NonNull IDependencyInstallerService service) {
+ Slog.w(TAG,
+ "DependencyInstallerService " + componentName + " is disconnected");
+ destroy();
+ }
+
+ @Override
+ public void onBinderDied() {
+ Slog.w(TAG, "DependencyInstallerService " + componentName + " has died");
+ destroy();
+ }
+
+ private void destroy() {
+ synchronized (mRemoteServiceLock) {
+ if (mRemoteService != null) {
+ mRemoteService.unbind();
+ mRemoteService = null;
+ }
+ }
+ }
+
+ });
+ AndroidFuture<IDependencyInstallerService> unusedFuture = mRemoteService.connect();
+ }
+ return true;
+ }
+
+ /**
+ * Ensure we call one of the outcomes only once, on the right handler.
+ *
+ * Repeated calls will be no-op.
+ */
+ private static class CallOnceProxy implements OutcomeReceiver<Void, PackageManagerException> {
+ private final Handler mHandler;
+ private final OutcomeReceiver<Void, PackageManagerException> mCallback;
+ @GuardedBy("this")
+ private boolean mCalled = false;
+
+ CallOnceProxy(Handler handler, OutcomeReceiver<Void, PackageManagerException> callback) {
+ mHandler = handler;
+ mCallback = callback;
+ }
+
+ @Override
+ public void onResult(Void result) {
+ synchronized (this) {
+ if (!mCalled) {
+ mHandler.post(() -> {
+ mCallback.onResult(null);
+ });
+ mCalled = true;
+ }
+ }
+ }
+
+ @Override
+ public void onError(@NonNull PackageManagerException error) {
+ synchronized (this) {
+ if (!mCalled) {
+ mHandler.post(() -> {
+ mCallback.onError(error);
+ });
+ mCalled = true;
+ }
+ }
+ }
+ }
}
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index d9e7696..8168c54 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -1029,12 +1029,14 @@
if (reconciledPackages == null) {
return;
}
- if (Flags.improveInstallFreeze()) {
- prepPerformDexoptIfNeeded(reconciledPackages);
- }
- if (renameAndUpdatePaths(requests)
- && commitInstallPackages(reconciledPackages)) {
- success = true;
+ if (renameAndUpdatePaths(requests)) {
+ // rename before dexopt because art will encoded the path in the odex/vdex file
+ if (Flags.improveInstallFreeze()) {
+ prepPerformDexoptIfNeeded(reconciledPackages);
+ }
+ if (commitInstallPackages(reconciledPackages)) {
+ success = true;
+ }
}
}
} finally {
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 2c09423..286333c 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -96,7 +96,6 @@
import android.os.Bundle;
import android.os.Handler;
import android.os.IInterface;
-import android.os.Looper;
import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.os.RemoteCallbackList;
@@ -2667,6 +2666,7 @@
}
final String[] packagesNullExtras = packagesWithoutExtras.toArray(
new String[packagesWithoutExtras.size()]);
+
final int n = mListeners.beginBroadcast();
try {
for (int i = 0; i < n; i++) {
@@ -2852,7 +2852,7 @@
class SecureSettingsObserver extends ContentObserver {
SecureSettingsObserver() {
- super(new Handler(Looper.getMainLooper()));
+ super(mCallbackHandler);
}
@Override
@@ -2866,32 +2866,29 @@
if (privateProfile.getIdentifier() == UserHandle.USER_NULL) {
return;
}
-
final int n = mListeners.beginBroadcast();
try {
for (int i = 0; i < n; i++) {
- final IOnAppsChangedListener listener =
- mListeners.getBroadcastItem(i);
+ final IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
final BroadcastCookie cookie =
- (BroadcastCookie) mListeners.getBroadcastCookie(
- i);
+ (BroadcastCookie) mListeners.getBroadcastCookie(i);
if (!isEnabledProfileOf(cookie, privateProfile,
"onSecureSettingsChange")) {
Log.d(TAG, "onSecureSettingsChange: Skipping - profile not enabled"
+ " or not accessible for package=" + cookie.packageName
+ ", packageUid=" + cookie.callingUid);
- } else {
- try {
- Log.d(TAG,
- "onUserConfigChanged: triggering onUserConfigChanged");
- listener.onUserConfigChanged(
- mUserManagerInternal.getLauncherUserInfo(
- privateProfile.getIdentifier()));
- } catch (RemoteException re) {
- Slog.d(TAG, "onUserConfigChanged: Callback failed ", re);
- }
+ continue;
+ }
+ try {
+ Log.d(TAG, "onUserConfigChanged: triggering onUserConfigChanged");
+ listener.onUserConfigChanged(
+ mUserManagerInternal.getLauncherUserInfo(
+ privateProfile.getIdentifier()));
+ } catch (RemoteException re) {
+ Slog.d(TAG, "onUserConfigChanged: Callback failed ", re);
}
}
+
} finally {
mListeners.finishBroadcast();
}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index eb70748..9b44f93 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -347,7 +347,7 @@
synchronized (mVerificationPolicyPerUser) {
mVerificationPolicyPerUser.put(USER_SYSTEM, DEFAULT_VERIFICATION_POLICY);
}
- mInstallDependencyHelper = new InstallDependencyHelper(
+ mInstallDependencyHelper = new InstallDependencyHelper(mContext,
mPm.mInjector.getSharedLibrariesImpl());
LocalServices.getService(SystemServiceManager.class).startService(
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index e156b31..505b7e6 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -3428,8 +3428,8 @@
private void resolveLibraryDependenciesIfNeeded() {
synchronized (mLock) {
- // TODO(b/372862145): Callback should be called on a handler passed as parameter
mInstallDependencyHelper.resolveLibraryDependenciesIfNeeded(mPackageLite,
+ mPm.snapshotComputer(), userId, mHandler,
new OutcomeReceiver<>() {
@Override
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 7ef3582..961b4b3c 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -3606,6 +3606,13 @@
case "--force-verification":
sessionParams.setForceVerification();
break;
+ case "--disable-auto-install-dependencies":
+ if (Flags.sdkDependencyInstaller()) {
+ sessionParams.setEnableAutoInstallDependencies(false);
+ } else {
+ throw new IllegalArgumentException("Unknown option " + opt);
+ }
+ break;
default:
throw new IllegalArgumentException("Unknown option " + opt);
}
@@ -4894,6 +4901,10 @@
+ "#compiler_filters");
pw.println(" or 'skip'");
pw.println(" --force-verification: if set, enable the verification for this install");
+ if (Flags.sdkDependencyInstaller()) {
+ pw.println(" --disable-auto-install-dependencies: if set, any missing shared");
+ pw.println(" library dependencies will not be auto-installed");
+ }
pw.println("");
pw.println(" install-existing [--user USER_ID|all|current]");
pw.println(" [--instant] [--full] [--wait] [--restrict-permissions] PACKAGE");
diff --git a/services/core/java/com/android/server/pm/SharedLibrariesImpl.java b/services/core/java/com/android/server/pm/SharedLibrariesImpl.java
index fc54f68..17d7a14 100644
--- a/services/core/java/com/android/server/pm/SharedLibrariesImpl.java
+++ b/services/core/java/com/android/server/pm/SharedLibrariesImpl.java
@@ -1017,10 +1017,11 @@
boolean isSdkOrStatic = libraryType.equals(LIBRARY_TYPE_SDK)
|| libraryType.equals(LIBRARY_TYPE_STATIC);
if (isSdkOrStatic && outMissingSharedLibraryInfos != null) {
- // TODO(b/372862145): Pass the CertDigest too
// If Dependency Installation is supported, try that instead of failing.
+ final List<String> libCertDigests = Arrays.asList(requiredCertDigests[i]);
SharedLibraryInfo missingLibrary = new SharedLibraryInfo(
- libName, libVersion, SharedLibraryInfo.TYPE_SDK_PACKAGE
+ libName, libVersion, SharedLibraryInfo.TYPE_SDK_PACKAGE,
+ libCertDigests
);
outMissingSharedLibraryInfos.add(missingLibrary);
} else {
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 06e29c2..b2b8aaf 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -4984,7 +4984,10 @@
res.getValue(com.android.internal.R.string.owner_name, mOwnerNameTypedValue, true);
final CharSequence ownerName = mOwnerNameTypedValue.coerceToString();
mOwnerName.set(ownerName != null ? ownerName.toString() : null);
+ // Invalidate when owners name changes due to config change.
+ UserManager.invalidateCacheOnUserDataChanged();
}
+
}
private void scheduleWriteUserList() {
@@ -4997,6 +5000,8 @@
Message msg = mHandler.obtainMessage(WRITE_USER_LIST_MSG);
mHandler.sendMessageDelayed(msg, WRITE_USER_DELAY);
}
+ // Invalidate cache when {@link UserData} changed, but write was scheduled for later.
+ UserManager.invalidateCacheOnUserDataChanged();
}
private void scheduleWriteUser(@UserIdInt int userId) {
@@ -5009,6 +5014,8 @@
Message msg = mHandler.obtainMessage(WRITE_USER_MSG, userId);
mHandler.sendMessageDelayed(msg, WRITE_USER_DELAY);
}
+ // Invalidate cache when {@link Data} changed, but write was scheduled for later.
+ UserManager.invalidateCacheOnUserDataChanged();
}
private ResilientAtomicFile getUserFile(int userId) {
@@ -5032,6 +5039,9 @@
if (DBG) {
debug("writeUserLP " + userData);
}
+ // invalidate caches related to any {@link UserData} change.
+ UserManager.invalidateCacheOnUserDataChanged();
+
try (ResilientAtomicFile userFile = getUserFile(userData.info.id)) {
FileOutputStream fos = null;
try {
@@ -5196,6 +5206,8 @@
if (DBG) {
debug("writeUserList");
}
+ // invalidate caches related to any {@link UserData} change.
+ UserManager.invalidateCacheOnUserDataChanged();
try (ResilientAtomicFile file = getUserListFile()) {
FileOutputStream fos = null;
@@ -7958,7 +7970,7 @@
Settings.Secure.getIntForUser(mContext.getContentResolver(),
HIDE_PRIVATESPACE_ENTRY_POINT, parentId) == 1);
} catch (Settings.SettingNotFoundException e) {
- throw new RuntimeException(e);
+ config.putBoolean(PRIVATE_SPACE_ENTRYPOINT_HIDDEN, false);
}
}
return new LauncherUserInfo.Builder(userDetails.getName(),
diff --git a/services/core/java/com/android/server/policy/ModifierShortcutManager.java b/services/core/java/com/android/server/policy/ModifierShortcutManager.java
index 4f67318..c9f66eb 100644
--- a/services/core/java/com/android/server/policy/ModifierShortcutManager.java
+++ b/services/core/java/com/android/server/policy/ModifierShortcutManager.java
@@ -29,6 +29,7 @@
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
+import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.graphics.drawable.Icon;
import android.hardware.input.AppLaunchData;
@@ -65,6 +66,7 @@
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
+import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
@@ -84,6 +86,7 @@
private static final String ATTRIBUTE_PACKAGE = "package";
private static final String ATTRIBUTE_CLASS = "class";
private static final String ATTRIBUTE_SHORTCUT = "shortcut";
+ private static final String ATTRIBUTE_KEYCODE = "keycode";
private static final String ATTRIBUTE_CATEGORY = "category";
private static final String ATTRIBUTE_SHIFT = "shift";
private static final String ATTRIBUTE_ROLE = "role";
@@ -167,6 +170,9 @@
}, UserHandle.ALL);
mCurrentUser = currentUser;
mInputManagerInternal = LocalServices.getService(InputManagerInternal.class);
+ }
+
+ void onSystemReady() {
loadShortcuts();
}
@@ -335,6 +341,7 @@
try {
XmlResourceParser parser = mContext.getResources().getXml(R.xml.bookmarks);
XmlUtils.beginDocument(parser, TAG_BOOKMARKS);
+ KeyCharacterMap virtualKcm = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD);
while (true) {
XmlUtils.nextElement(parser);
@@ -353,15 +360,36 @@
String categoryName = parser.getAttributeValue(null, ATTRIBUTE_CATEGORY);
String shiftName = parser.getAttributeValue(null, ATTRIBUTE_SHIFT);
String roleName = parser.getAttributeValue(null, ATTRIBUTE_ROLE);
+ final int keycode;
+ final int modifierState;
+ TypedArray a = mContext.getResources().obtainAttributes(parser,
+ R.styleable.Bookmark);
+ try {
+ keycode = a.getInt(R.styleable.Bookmark_keycode, KeyEvent.KEYCODE_UNKNOWN);
+ modifierState = a.getInt(R.styleable.Bookmark_modifierState, 0);
+ } finally {
+ a.recycle();
+ }
+ if (TextUtils.isEmpty(shortcutName) && keycode != KeyEvent.KEYCODE_UNKNOWN) {
+ // Try to find shortcutChar using keycode
+ shortcutName = String.valueOf(virtualKcm.getDisplayLabel(keycode)).toLowerCase(
+ Locale.ROOT);
+ }
if (TextUtils.isEmpty(shortcutName)) {
Log.w(TAG, "Shortcut required for bookmark with category=" + categoryName
+ " packageName=" + packageName + " className=" + className
- + " role=" + roleName + "shiftName=" + shiftName);
+ + " role=" + roleName + " shiftName=" + shiftName + " keycode= "
+ + keycode + " modifierState= " + modifierState);
continue;
}
- final boolean isShiftShortcut = (shiftName != null && shiftName.equals("true"));
+ final boolean isShiftShortcut;
+ if (!TextUtils.isEmpty(shiftName)) {
+ isShiftShortcut = shiftName.equals("true");
+ } else {
+ isShiftShortcut = (modifierState & KeyEvent.META_SHIFT_ON) != 0;
+ }
if (modifierShortcutManagerRefactor()) {
final char shortcutChar = shortcutName.charAt(0);
@@ -376,7 +404,7 @@
bookmark = new RoleBookmark(shortcutChar, isShiftShortcut, roleName);
}
if (bookmark != null) {
- Log.d(TAG, "adding shortcut " + bookmark + "shift="
+ Log.d(TAG, "adding shortcut " + bookmark + " shift="
+ isShiftShortcut + " char=" + shortcutChar);
mBookmarks.put(new Pair<>(shortcutChar, isShiftShortcut), bookmark);
}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index dda5bcf..85e7cfe 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -6610,6 +6610,7 @@
// In normal flow, systemReady is called before other system services are ready.
// So it is better not to bind keyguard here.
mKeyguardDelegate.onSystemReady();
+ mModifierShortcutManager.onSystemReady();
mVrManagerInternal = LocalServices.getService(VrManagerInternal.class);
if (mVrManagerInternal != null) {
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 0acfe92..c573293 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -625,14 +625,6 @@
boolean mIsFaceDown = false;
private long mLastFlipTime = 0L;
- // The screen brightness setting override from the window manager
- // to allow the current foreground activity to override the brightness.
- private float mScreenBrightnessOverrideFromWindowManager =
- PowerManager.BRIGHTNESS_INVALID_FLOAT;
-
- // Tag identifying the window/activity that requested the brightness override.
- private CharSequence mScreenBrightnessOverrideFromWmTag = null;
-
// The window manager has determined the user to be inactive via other means.
// Set this to false to disable.
private boolean mUserInactiveOverrideFromWindowManager;
@@ -2332,6 +2324,8 @@
Trace.traceBegin(Trace.TRACE_TAG_POWER, traceMethodName);
try {
// Phase 2: Handle wakefulness change and bookkeeping.
+ // Under lock, invalidate before set ensures caches won't return stale values.
+ mInjector.invalidateIsInteractiveCaches();
mWakefulnessRaw = newWakefulness;
mWakefulnessChanging = true;
mDirty |= DIRTY_WAKEFULNESS;
@@ -2429,7 +2423,6 @@
void onPowerGroupEventLocked(int event, PowerGroup powerGroup) {
mWakefulnessChanging = true;
mDirty |= DIRTY_WAKEFULNESS;
- mInjector.invalidateIsInteractiveCaches();
final int groupId = powerGroup.getGroupId();
if (event == DisplayGroupPowerChangeListener.DISPLAY_GROUP_REMOVED) {
mPowerGroups.delete(groupId);
@@ -3662,9 +3655,7 @@
// Keep the brightness steady during boot. This requires the
// bootloader brightness and the default brightness to be identical.
screenBrightnessOverride = mScreenBrightnessDefault;
- } else if (isValidBrightness(mScreenBrightnessOverrideFromWindowManager)) {
- screenBrightnessOverride = mScreenBrightnessOverrideFromWindowManager;
- overrideTag = mScreenBrightnessOverrideFromWmTag;
+ overrideTag = "boot";
} else {
screenBrightnessOverride = PowerManager.BRIGHTNESS_INVALID_FLOAT;
}
@@ -4448,19 +4439,6 @@
}
}
- private void setScreenBrightnessOverrideFromWindowManagerInternal(
- float brightness, CharSequence tag) {
- synchronized (mLock) {
- if (!BrightnessSynchronizer.floatEquals(mScreenBrightnessOverrideFromWindowManager,
- brightness)) {
- mScreenBrightnessOverrideFromWindowManager = brightness;
- mScreenBrightnessOverrideFromWmTag = tag;
- mDirty |= DIRTY_SETTINGS;
- updatePowerStateLocked();
- }
- }
- }
-
private void setUserInactiveOverrideFromWindowManagerInternal() {
synchronized (mLock) {
mUserInactiveOverrideFromWindowManager = true;
@@ -4799,10 +4777,6 @@
+ mMaximumScreenOffTimeoutFromDeviceAdmin + " (enforced="
+ isMaximumScreenOffTimeoutFromDeviceAdminEnforcedLocked() + ")");
pw.println(" mStayOnWhilePluggedInSetting=" + mStayOnWhilePluggedInSetting);
- pw.println(" mScreenBrightnessOverrideFromWindowManager="
- + mScreenBrightnessOverrideFromWindowManager);
- pw.println(" mScreenBrightnessOverrideFromWmTag="
- + mScreenBrightnessOverrideFromWmTag);
pw.println(" mUserActivityTimeoutOverrideFromWindowManager="
+ mUserActivityTimeoutOverrideFromWindowManager);
pw.println(" mUserInactiveOverrideFromWindowManager="
@@ -5192,10 +5166,6 @@
proto.write(
PowerServiceSettingsAndConfigurationDumpProto
- .SCREEN_BRIGHTNESS_OVERRIDE_FROM_WINDOW_MANAGER,
- mScreenBrightnessOverrideFromWindowManager);
- proto.write(
- PowerServiceSettingsAndConfigurationDumpProto
.USER_ACTIVITY_TIMEOUT_OVERRIDE_FROM_WINDOW_MANAGER_MS,
mUserActivityTimeoutOverrideFromWindowManager);
proto.write(
@@ -7135,17 +7105,6 @@
@VisibleForTesting
final class LocalService extends PowerManagerInternal {
@Override
- public void setScreenBrightnessOverrideFromWindowManager(
- float screenBrightness, CharSequence tag) {
- if (screenBrightness < PowerManager.BRIGHTNESS_MIN
- || screenBrightness > PowerManager.BRIGHTNESS_MAX) {
- screenBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT;
- tag = null;
- }
- setScreenBrightnessOverrideFromWindowManagerInternal(screenBrightness, tag);
- }
-
- @Override
public void setDozeOverrideFromDreamManager(
int screenState, int reason, float screenBrightnessFloat, int screenBrightnessInt,
boolean useNormalBrightnessForDoze) {
diff --git a/services/core/java/com/android/server/power/hint/HintManagerService.java b/services/core/java/com/android/server/power/hint/HintManagerService.java
index 2c0ce25..17459df 100644
--- a/services/core/java/com/android/server/power/hint/HintManagerService.java
+++ b/services/core/java/com/android/server/power/hint/HintManagerService.java
@@ -33,11 +33,15 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.hardware.power.ChannelConfig;
+import android.hardware.power.CpuHeadroomParams;
+import android.hardware.power.GpuHeadroomParams;
import android.hardware.power.IPower;
import android.hardware.power.SessionConfig;
import android.hardware.power.SessionTag;
import android.hardware.power.WorkDuration;
import android.os.Binder;
+import android.os.CpuHeadroomParamsInternal;
+import android.os.GpuHeadroomParamsInternal;
import android.os.Handler;
import android.os.IBinder;
import android.os.IHintManager;
@@ -90,6 +94,10 @@
private static final int EVENT_CLEAN_UP_UID = 3;
@VisibleForTesting static final int CLEAN_UP_UID_DELAY_MILLIS = 1000;
+ private static final int DEFAULT_GPU_HEADROOM_INTERVAL_MILLIS = 1000;
+ private static final int DEFAULT_CPU_HEADROOM_INTERVAL_MILLIS = 1000;
+ private static final int HEADROOM_INTERVAL_UNSUPPORTED = -1;
+ @VisibleForTesting static final int DEFAULT_HEADROOM_PID = -1;
@VisibleForTesting final long mHintSessionPreferredRate;
@@ -160,10 +168,76 @@
private static final String PROPERTY_SF_ENABLE_CPU_HINT = "debug.sf.enable_adpf_cpu_hint";
private static final String PROPERTY_HWUI_ENABLE_HINT_MANAGER = "debug.hwui.use_hint_manager";
+ private static final String PROPERTY_USE_HAL_HEADROOMS = "persist.hms.use_hal_headrooms";
private Boolean mFMQUsesIntegratedEventFlag = false;
- @VisibleForTesting final IHintManager.Stub mService = new BinderService();
+ private final Object mCpuHeadroomLock = new Object();
+
+ private static class CpuHeadroomCacheItem {
+ long mExpiredTimeMillis;
+ CpuHeadroomParamsInternal mParams;
+ float[] mHeadroom;
+ long mPid;
+
+ CpuHeadroomCacheItem(long expiredTimeMillis, CpuHeadroomParamsInternal params,
+ float[] headroom, long pid) {
+ mExpiredTimeMillis = expiredTimeMillis;
+ mParams = params;
+ mPid = pid;
+ mHeadroom = headroom;
+ }
+
+ private boolean match(CpuHeadroomParamsInternal params, long pid) {
+ if (mParams == null && params == null) return true;
+ if (mParams != null) {
+ return mParams.equals(params) && pid == mPid;
+ }
+ return false;
+ }
+
+ private boolean isExpired() {
+ return System.currentTimeMillis() > mExpiredTimeMillis;
+ }
+ }
+
+ @GuardedBy("mCpuHeadroomLock")
+ private final List<CpuHeadroomCacheItem> mCpuHeadroomCache;
+ private final long mCpuHeadroomIntervalMillis;
+
+ private final Object mGpuHeadroomLock = new Object();
+
+ private static class GpuHeadroomCacheItem {
+ long mExpiredTimeMillis;
+ GpuHeadroomParamsInternal mParams;
+ float mHeadroom;
+
+ GpuHeadroomCacheItem(long expiredTimeMillis, GpuHeadroomParamsInternal params,
+ float headroom) {
+ mExpiredTimeMillis = expiredTimeMillis;
+ mParams = params;
+ mHeadroom = headroom;
+ }
+
+ private boolean match(GpuHeadroomParamsInternal params) {
+ if (mParams == null && params == null) return true;
+ if (mParams != null) {
+ return mParams.equals(params);
+ }
+ return false;
+ }
+
+ private boolean isExpired() {
+ return System.currentTimeMillis() > mExpiredTimeMillis;
+ }
+ }
+
+ @GuardedBy("mGpuHeadroomLock")
+ private final List<GpuHeadroomCacheItem> mGpuHeadroomCache;
+ private final long mGpuHeadroomIntervalMillis;
+
+ @VisibleForTesting
+ final IHintManager.Stub mService = new BinderService();
public HintManagerService(Context context) {
this(context, new Injector());
@@ -197,13 +271,72 @@
mPowerHal = injector.createIPower();
mPowerHalVersion = 0;
mUsesFmq = false;
+ long cpuHeadroomIntervalMillis = HEADROOM_INTERVAL_UNSUPPORTED;
+ long gpuHeadroomIntervalMillis = HEADROOM_INTERVAL_UNSUPPORTED;
if (mPowerHal != null) {
try {
mPowerHalVersion = mPowerHal.getInterfaceVersion();
+ if (mPowerHal.getInterfaceVersion() >= 6) {
+ if (SystemProperties.getBoolean(PROPERTY_USE_HAL_HEADROOMS, true)) {
+ cpuHeadroomIntervalMillis = checkCpuHeadroomSupport();
+ gpuHeadroomIntervalMillis = checkGpuHeadroomSupport();
+ }
+ }
} catch (RemoteException e) {
throw new IllegalStateException("Could not contact PowerHAL!", e);
}
}
+ mCpuHeadroomIntervalMillis = cpuHeadroomIntervalMillis;
+ mGpuHeadroomIntervalMillis = gpuHeadroomIntervalMillis;
+ if (mCpuHeadroomIntervalMillis > 0) {
+ mCpuHeadroomCache = new ArrayList<>(4);
+ } else {
+ mCpuHeadroomCache = null;
+ }
+ if (mGpuHeadroomIntervalMillis > 0) {
+ mGpuHeadroomCache = new ArrayList<>(2);
+ } else {
+ mGpuHeadroomCache = null;
+ }
+ }
+
+ private long checkCpuHeadroomSupport() {
+ try {
+ synchronized (mCpuHeadroomLock) {
+ final CpuHeadroomParams defaultParams = new CpuHeadroomParams();
+ defaultParams.pid = Process.myPid();
+ float[] ret = mPowerHal.getCpuHeadroom(defaultParams);
+ if (ret != null && ret.length > 0) {
+ return Math.max(
+ DEFAULT_CPU_HEADROOM_INTERVAL_MILLIS,
+ mPowerHal.getCpuHeadroomMinIntervalMillis());
+ }
+ }
+
+ } catch (UnsupportedOperationException e) {
+ Slog.w(TAG, "getCpuHeadroom HAL API is not supported", e);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "getCpuHeadroom HAL API fails, disabling the API", e);
+ }
+ return HEADROOM_INTERVAL_UNSUPPORTED;
+ }
+
+ private long checkGpuHeadroomSupport() {
+ try {
+ synchronized (mGpuHeadroomLock) {
+ float ret = mPowerHal.getGpuHeadroom(new GpuHeadroomParams());
+ if (!Float.isNaN(ret)) {
+ return Math.max(
+ DEFAULT_GPU_HEADROOM_INTERVAL_MILLIS,
+ mPowerHal.getGpuHeadroomMinIntervalMillis());
+ }
+ }
+ } catch (UnsupportedOperationException e) {
+ Slog.w(TAG, "getGpuHeadroom HAL API is not supported", e);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "getGpuHeadroom HAL API fails, disabling the API", e);
+ }
+ return HEADROOM_INTERVAL_UNSUPPORTED;
}
private ServiceThread createCleanUpThread() {
@@ -738,7 +871,7 @@
mLinked = false;
}
if (mConfig != null) {
- try {
+ try {
mPowerHal.closeSessionChannel(mTgid, mUid);
} catch (RemoteException e) {
throw new IllegalStateException("Failed to close session channel!", e);
@@ -982,13 +1115,13 @@
}
// returns the first invalid tid or null if not found
- private Integer checkTidValid(int uid, int tgid, int [] tids, IntArray nonIsolated) {
+ private Integer checkTidValid(int uid, int tgid, int[] tids, IntArray nonIsolated) {
// Make sure all tids belongs to the same UID (including isolated UID),
// tids can belong to different application processes.
List<Integer> isolatedPids = null;
for (int i = 0; i < tids.length; i++) {
int tid = tids[i];
- final String[] procStatusKeys = new String[] {
+ final String[] procStatusKeys = new String[]{
"Uid:",
"Tgid:"
};
@@ -1058,7 +1191,7 @@
Slogf.w(TAG, errMsg);
throw new SecurityException(errMsg);
}
- if (resetOnForkEnabled()){
+ if (resetOnForkEnabled()) {
try {
for (int tid : tids) {
int policy = Process.getThreadScheduler(tid);
@@ -1214,6 +1347,124 @@
}
@Override
+ public float[] getCpuHeadroom(@Nullable CpuHeadroomParamsInternal params) {
+ if (mCpuHeadroomIntervalMillis <= 0) {
+ throw new UnsupportedOperationException();
+ }
+ CpuHeadroomParams halParams = new CpuHeadroomParams();
+ halParams.pid = Binder.getCallingPid();
+ if (params != null) {
+ halParams.calculationType = params.calculationType;
+ halParams.selectionType = params.selectionType;
+ if (params.usesDeviceHeadroom) {
+ halParams.pid = DEFAULT_HEADROOM_PID;
+ }
+ }
+ synchronized (mCpuHeadroomLock) {
+ while (!mCpuHeadroomCache.isEmpty()) {
+ if (mCpuHeadroomCache.getFirst().isExpired()) {
+ mCpuHeadroomCache.removeFirst();
+ } else {
+ break;
+ }
+ }
+ for (int i = 0; i < mCpuHeadroomCache.size(); ++i) {
+ final CpuHeadroomCacheItem item = mCpuHeadroomCache.get(i);
+ if (item.match(params, halParams.pid)) {
+ item.mExpiredTimeMillis =
+ System.currentTimeMillis() + mCpuHeadroomIntervalMillis;
+ mCpuHeadroomCache.remove(i);
+ mCpuHeadroomCache.add(item);
+ return item.mHeadroom;
+ }
+ }
+ }
+ // return from HAL directly
+ try {
+ float[] headroom = mPowerHal.getCpuHeadroom(halParams);
+ if (headroom == null || headroom.length == 0) {
+ Slog.wtf(TAG,
+ "CPU headroom from Power HAL is invalid: " + Arrays.toString(headroom));
+ return new float[]{Float.NaN};
+ }
+ synchronized (mCpuHeadroomLock) {
+ mCpuHeadroomCache.add(new CpuHeadroomCacheItem(
+ System.currentTimeMillis() + mCpuHeadroomIntervalMillis,
+ params, headroom, halParams.pid
+ ));
+ }
+ return headroom;
+
+ } catch (RemoteException e) {
+ return new float[]{Float.NaN};
+ }
+ }
+
+ @Override
+ public float getGpuHeadroom(@Nullable GpuHeadroomParamsInternal params) {
+ if (mGpuHeadroomIntervalMillis <= 0) {
+ throw new UnsupportedOperationException();
+ }
+ GpuHeadroomParams halParams = new GpuHeadroomParams();
+ if (params != null) {
+ halParams.calculationType = params.calculationType;
+ }
+ synchronized (mGpuHeadroomLock) {
+ while (!mGpuHeadroomCache.isEmpty()) {
+ if (mGpuHeadroomCache.getFirst().isExpired()) {
+ mGpuHeadroomCache.removeFirst();
+ } else {
+ break;
+ }
+ }
+ for (int i = 0; i < mGpuHeadroomCache.size(); ++i) {
+ final GpuHeadroomCacheItem item = mGpuHeadroomCache.get(i);
+ if (item.match(params)) {
+ item.mExpiredTimeMillis =
+ System.currentTimeMillis() + mGpuHeadroomIntervalMillis;
+ mGpuHeadroomCache.remove(i);
+ mGpuHeadroomCache.add(item);
+ return item.mHeadroom;
+ }
+ }
+ }
+ // return from HAL directly
+ try {
+ float headroom = mPowerHal.getGpuHeadroom(halParams);
+ if (Float.isNaN(headroom)) {
+ Slog.wtf(TAG,
+ "GPU headroom from Power HAL is NaN");
+ return Float.NaN;
+ }
+ synchronized (mGpuHeadroomLock) {
+ mGpuHeadroomCache.add(new GpuHeadroomCacheItem(
+ System.currentTimeMillis() + mGpuHeadroomIntervalMillis,
+ params, headroom
+ ));
+ }
+ return headroom;
+ } catch (RemoteException e) {
+ return Float.NaN;
+ }
+ }
+
+ @Override
+ public long getCpuHeadroomMinIntervalMillis() throws RemoteException {
+ if (mCpuHeadroomIntervalMillis <= 0) {
+ throw new UnsupportedOperationException();
+ }
+ return mCpuHeadroomIntervalMillis;
+ }
+
+ @Override
+ public long getGpuHeadroomMinIntervalMillis() throws RemoteException {
+ if (mGpuHeadroomIntervalMillis <= 0) {
+ throw new UnsupportedOperationException();
+ }
+ return mGpuHeadroomIntervalMillis;
+ }
+
+ @Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) {
return;
@@ -1235,6 +1486,25 @@
}
}
}
+ pw.println("CPU Headroom Interval: " + mCpuHeadroomIntervalMillis);
+ pw.println("GPU Headroom Interval: " + mGpuHeadroomIntervalMillis);
+ try {
+ CpuHeadroomParamsInternal params = new CpuHeadroomParamsInternal();
+ params.selectionType = CpuHeadroomParams.SelectionType.ALL;
+ params.usesDeviceHeadroom = true;
+ pw.println("CPU headroom: " + Arrays.toString(getCpuHeadroom(params)));
+ params = new CpuHeadroomParamsInternal();
+ params.selectionType = CpuHeadroomParams.SelectionType.PER_CORE;
+ params.usesDeviceHeadroom = true;
+ pw.println("CPU headroom per core: " + Arrays.toString(getCpuHeadroom(params)));
+ } catch (Exception e) {
+ pw.println("CPU headroom: N/A");
+ }
+ try {
+ pw.println("GPU headroom: " + getGpuHeadroom(null));
+ } catch (Exception e) {
+ pw.println("GPU headroom: N/A");
+ }
}
private void logPerformanceHintSessionAtom(int uid, long sessionId,
@@ -1467,7 +1737,7 @@
Slogf.w(TAG, errMsg);
throw new SecurityException(errMsg);
}
- if (resetOnForkEnabled()){
+ if (resetOnForkEnabled()) {
try {
for (int tid : tids) {
int policy = Process.getThreadScheduler(tid);
diff --git a/services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionService.java b/services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionService.java
index 1260eee..e780be4 100644
--- a/services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionService.java
+++ b/services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionService.java
@@ -46,6 +46,7 @@
import com.android.server.pm.UserManagerInternal;
import com.android.server.security.advancedprotection.features.AdvancedProtectionHook;
import com.android.server.security.advancedprotection.features.AdvancedProtectionProvider;
+import com.android.server.security.advancedprotection.features.DisallowInstallUnknownSourcesAdvancedProtectionHook;
import java.io.FileDescriptor;
import java.util.ArrayList;
@@ -76,10 +77,9 @@
}
private void initFeatures(boolean enabled) {
- // Empty until features are added.
- // Examples:
- // mHooks.add(new SideloadingAdvancedProtectionHook(mContext, enabled));
- // mProviders.add(new WifiAdvancedProtectionProvider());
+ if (android.security.Flags.aapmFeatureDisableInstallUnknownSources()) {
+ mHooks.add(new DisallowInstallUnknownSourcesAdvancedProtectionHook(mContext, enabled));
+ }
}
// Only for tests
diff --git a/services/core/java/com/android/server/security/advancedprotection/features/DisallowInstallUnknownSourcesAdvancedProtectionHook.java b/services/core/java/com/android/server/security/advancedprotection/features/DisallowInstallUnknownSourcesAdvancedProtectionHook.java
new file mode 100644
index 0000000..21752e5
--- /dev/null
+++ b/services/core/java/com/android/server/security/advancedprotection/features/DisallowInstallUnknownSourcesAdvancedProtectionHook.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.security.advancedprotection.features;
+
+import static android.security.advancedprotection.AdvancedProtectionManager.ADVANCED_PROTECTION_SYSTEM_ENTITY;
+import static android.security.advancedprotection.AdvancedProtectionManager.FEATURE_ID_DISALLOW_INSTALL_UNKNOWN_SOURCES;
+
+import android.annotation.NonNull;
+import android.app.admin.DevicePolicyManager;
+import android.content.Context;
+import android.os.UserManager;
+import android.security.advancedprotection.AdvancedProtectionFeature;
+import android.util.Slog;
+
+/** @hide */
+public final class DisallowInstallUnknownSourcesAdvancedProtectionHook
+ extends AdvancedProtectionHook {
+ private static final String TAG = "AdvancedProtectionDisallowInstallUnknown";
+
+ private final AdvancedProtectionFeature mFeature = new AdvancedProtectionFeature(
+ FEATURE_ID_DISALLOW_INSTALL_UNKNOWN_SOURCES);
+ private final DevicePolicyManager mDevicePolicyManager;
+
+ public DisallowInstallUnknownSourcesAdvancedProtectionHook(@NonNull Context context,
+ boolean enabled) {
+ super(context, enabled);
+ mDevicePolicyManager = context.getSystemService(DevicePolicyManager.class);
+ onAdvancedProtectionChanged(enabled);
+ }
+
+ @NonNull
+ @Override
+ public AdvancedProtectionFeature getFeature() {
+ return mFeature;
+ }
+
+ @Override
+ public boolean isAvailable() {
+ return true;
+ }
+
+ @Override
+ public void onAdvancedProtectionChanged(boolean enabled) {
+ if (enabled) {
+ Slog.d(TAG, "Setting DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY restriction");
+ mDevicePolicyManager.addUserRestrictionGlobally(ADVANCED_PROTECTION_SYSTEM_ENTITY,
+ UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY);
+ return;
+ }
+ Slog.d(TAG, "Clearing DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY restriction");
+ mDevicePolicyManager.clearUserRestrictionGlobally(ADVANCED_PROTECTION_SYSTEM_ENTITY,
+ UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY);
+
+ // TODO(b/369361373):
+ // 1. After clearing the restriction, set AppOpsManager.OP_REQUEST_INSTALL_PACKAGES to
+ // disabled.
+ // 2. Update dialog strings.
+ }
+}
diff --git a/services/core/java/com/android/server/security/adaptiveauthentication/AdaptiveAuthenticationService.java b/services/core/java/com/android/server/security/authenticationpolicy/AuthenticationPolicyService.java
similarity index 81%
rename from services/core/java/com/android/server/security/adaptiveauthentication/AdaptiveAuthenticationService.java
rename to services/core/java/com/android/server/security/authenticationpolicy/AuthenticationPolicyService.java
index b129fdc..6798a61 100644
--- a/services/core/java/com/android/server/security/adaptiveauthentication/AdaptiveAuthenticationService.java
+++ b/services/core/java/com/android/server/security/authenticationpolicy/AuthenticationPolicyService.java
@@ -14,10 +14,13 @@
* limitations under the License.
*/
-package com.android.server.security.adaptiveauthentication;
+package com.android.server.security.authenticationpolicy;
+
+import static android.Manifest.permission.MANAGE_SECURE_LOCK_DEVICE;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_ADAPTIVE_AUTH_REQUEST;
+import android.annotation.EnforcePermission;
import android.app.KeyguardManager;
import android.content.Context;
import android.content.pm.PackageManager;
@@ -32,9 +35,14 @@
import android.hardware.biometrics.events.AuthenticationSucceededInfo;
import android.os.Build;
import android.os.Handler;
+import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.SystemClock;
+import android.security.authenticationpolicy.AuthenticationPolicyManager;
+import android.security.authenticationpolicy.DisableSecureLockDeviceParams;
+import android.security.authenticationpolicy.EnableSecureLockDeviceParams;
+import android.security.authenticationpolicy.IAuthenticationPolicyService;
import android.util.Log;
import android.util.Slog;
import android.util.SparseIntArray;
@@ -55,8 +63,8 @@
/**
* @hide
*/
-public class AdaptiveAuthenticationService extends SystemService {
- private static final String TAG = "AdaptiveAuthenticationService";
+public class AuthenticationPolicyService extends SystemService {
+ private static final String TAG = "AuthenticationPolicyService";
private static final boolean DEBUG = Build.IS_DEBUGGABLE && Log.isLoggable(TAG, Log.DEBUG);
@VisibleForTesting
@@ -74,16 +82,17 @@
private final KeyguardManager mKeyguardManager;
private final WindowManagerInternal mWindowManager;
private final UserManagerInternal mUserManager;
+ private SecureLockDeviceServiceInternal mSecureLockDeviceService;
@VisibleForTesting
final SparseIntArray mFailedAttemptsForUser = new SparseIntArray();
private final SparseLongArray mLastLockedTimestamp = new SparseLongArray();
- public AdaptiveAuthenticationService(Context context) {
+ public AuthenticationPolicyService(Context context) {
this(context, new LockPatternUtils(context));
}
@VisibleForTesting
- public AdaptiveAuthenticationService(Context context, LockPatternUtils lockPatternUtils) {
+ public AuthenticationPolicyService(Context context, LockPatternUtils lockPatternUtils) {
super(context);
mLockPatternUtils = lockPatternUtils;
mLockSettings = Objects.requireNonNull(
@@ -94,10 +103,16 @@
mWindowManager = Objects.requireNonNull(
LocalServices.getService(WindowManagerInternal.class));
mUserManager = Objects.requireNonNull(LocalServices.getService(UserManagerInternal.class));
+ if (android.security.Flags.secureLockdown()) {
+ mSecureLockDeviceService = Objects.requireNonNull(
+ LocalServices.getService(SecureLockDeviceServiceInternal.class));
+ }
}
@Override
- public void onStart() {}
+ public void onStart() {
+ publishBinderService(Context.AUTHENTICATION_POLICY_SERVICE, mService);
+ }
@Override
public void onBootPhase(int phase) {
@@ -294,4 +309,36 @@
// next successful primary or biometric auth happens
mLastLockedTimestamp.put(userId, SystemClock.elapsedRealtime());
}
+
+ private final IBinder mService = new IAuthenticationPolicyService.Stub() {
+ /**
+ * @see AuthenticationPolicyManager#enableSecureLockDevice(EnableSecureLockDeviceParams)
+ * @param params EnableSecureLockDeviceParams for caller to supply params related
+ * to the secure lock device request
+ * @return @EnableSecureLockDeviceRequestStatus int indicating the result of the Secure
+ * Lock Device request
+ */
+ @Override
+ @EnforcePermission(MANAGE_SECURE_LOCK_DEVICE)
+ @AuthenticationPolicyManager.EnableSecureLockDeviceRequestStatus
+ public int enableSecureLockDevice(EnableSecureLockDeviceParams params) {
+ enableSecureLockDevice_enforcePermission();
+ return mSecureLockDeviceService.enableSecureLockDevice(params);
+ }
+
+ /**
+ * @see AuthenticationPolicyManager#disableSecureLockDevice(DisableSecureLockDeviceParams)
+ * @param params @DisableSecureLockDeviceParams for caller to supply params related
+ * to the secure lock device request
+ * @return @DisableSecureLockDeviceRequestStatus int indicating the result of the Secure
+ * Lock Device request
+ */
+ @Override
+ @EnforcePermission(MANAGE_SECURE_LOCK_DEVICE)
+ @AuthenticationPolicyManager.DisableSecureLockDeviceRequestStatus
+ public int disableSecureLockDevice(DisableSecureLockDeviceParams params) {
+ disableSecureLockDevice_enforcePermission();
+ return mSecureLockDeviceService.disableSecureLockDevice(params);
+ }
+ };
}
diff --git a/services/core/java/com/android/server/security/adaptiveauthentication/OWNERS b/services/core/java/com/android/server/security/authenticationpolicy/OWNERS
similarity index 100%
rename from services/core/java/com/android/server/security/adaptiveauthentication/OWNERS
rename to services/core/java/com/android/server/security/authenticationpolicy/OWNERS
diff --git a/services/core/java/com/android/server/security/authenticationpolicy/SecureLockDeviceService.java b/services/core/java/com/android/server/security/authenticationpolicy/SecureLockDeviceService.java
new file mode 100644
index 0000000..7b89723
--- /dev/null
+++ b/services/core/java/com/android/server/security/authenticationpolicy/SecureLockDeviceService.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.security.authenticationpolicy;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.security.authenticationpolicy.AuthenticationPolicyManager;
+import android.security.authenticationpolicy.AuthenticationPolicyManager.DisableSecureLockDeviceRequestStatus;
+import android.security.authenticationpolicy.AuthenticationPolicyManager.EnableSecureLockDeviceRequestStatus;
+import android.security.authenticationpolicy.DisableSecureLockDeviceParams;
+import android.security.authenticationpolicy.EnableSecureLockDeviceParams;
+import android.util.Slog;
+
+import com.android.server.LocalServices;
+import com.android.server.SystemService;
+
+/**
+ * System service for remotely calling secure lock on the device.
+ *
+ * Callers will access this class via
+ * {@link com.android.server.security.authenticationpolicy.AuthenticationPolicyService}.
+ *
+ * @see AuthenticationPolicyService
+ * @see AuthenticationPolicyManager#enableSecureLockDevice
+ * @see AuthenticationPolicyManager#disableSecureLockDevice
+ * @hide
+ */
+public class SecureLockDeviceService extends SecureLockDeviceServiceInternal {
+ private static final String TAG = "SecureLockDeviceService";
+ private final Context mContext;
+
+ public SecureLockDeviceService(@NonNull Context context) {
+ mContext = context;
+ }
+
+ private void start() {
+ // Expose private service for system components to use.
+ LocalServices.addService(SecureLockDeviceServiceInternal.class, this);
+ }
+
+ /**
+ * @see AuthenticationPolicyManager#enableSecureLockDevice
+ * @param params EnableSecureLockDeviceParams for caller to supply params related
+ * to the secure lock device request
+ * @return @EnableSecureLockDeviceRequestStatus int indicating the result of the
+ * secure lock device request
+ *
+ * @hide
+ */
+ @Override
+ @EnableSecureLockDeviceRequestStatus
+ public int enableSecureLockDevice(EnableSecureLockDeviceParams params) {
+ // (1) Call into system_server to lock device, configure allowed auth types
+ // for secure lock
+ // TODO: lock device, configure allowed authentication types for device entry
+ // (2) Call into framework to configure secure lock 2FA lockscreen
+ // update, UI & string updates
+ // TODO: implement 2FA lockscreen when SceneContainerFlag.isEnabled()
+ // TODO: implement 2FA lockscreen when !SceneContainerFlag.isEnabled()
+ // (3) Call into framework to configure keyguard security updates
+ // TODO: implement security updates
+ return AuthenticationPolicyManager.ERROR_UNSUPPORTED;
+ }
+
+ /**
+ * @see AuthenticationPolicyManager#disableSecureLockDevice
+ * @param params @DisableSecureLockDeviceParams for caller to supply params related
+ * to the secure lock device request
+ * @return @DisableSecureLockDeviceRequestStatus int indicating the result of the
+ * secure lock device request
+ *
+ * @hide
+ */
+ @Override
+ @DisableSecureLockDeviceRequestStatus
+ public int disableSecureLockDevice(DisableSecureLockDeviceParams params) {
+ // (1) Call into system_server to reset allowed auth types
+ // TODO: reset allowed authentication types for device entry;
+ // (2) Call into framework to disable secure lock 2FA lockscreen, reset UI
+ // & string updates
+ // TODO: implement reverting to normal lockscreen when SceneContainerFlag.isEnabled()
+ // TODO: implement reverting to normal lockscreen when !SceneContainerFlag.isEnabled()
+ // (3) Call into framework to revert keyguard security updates
+ // TODO: implement reverting security updates
+ return AuthenticationPolicyManager.ERROR_UNSUPPORTED;
+ }
+
+ /**
+ * System service lifecycle.
+ */
+ public static final class Lifecycle extends SystemService {
+ private final SecureLockDeviceService mService;
+
+ public Lifecycle(@NonNull Context context) {
+ super(context);
+ mService = new SecureLockDeviceService(context);
+ }
+
+ @Override
+ public void onStart() {
+ Slog.i(TAG, "Starting SecureLockDeviceService");
+ mService.start();
+ Slog.i(TAG, "Started SecureLockDeviceService");
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/security/authenticationpolicy/SecureLockDeviceServiceInternal.java b/services/core/java/com/android/server/security/authenticationpolicy/SecureLockDeviceServiceInternal.java
new file mode 100644
index 0000000..b903709
--- /dev/null
+++ b/services/core/java/com/android/server/security/authenticationpolicy/SecureLockDeviceServiceInternal.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.security.authenticationpolicy;
+
+import android.security.authenticationpolicy.AuthenticationPolicyManager;
+import android.security.authenticationpolicy.AuthenticationPolicyManager.DisableSecureLockDeviceRequestStatus;
+import android.security.authenticationpolicy.AuthenticationPolicyManager.EnableSecureLockDeviceRequestStatus;
+import android.security.authenticationpolicy.DisableSecureLockDeviceParams;
+import android.security.authenticationpolicy.EnableSecureLockDeviceParams;
+
+/**
+ * Local system service interface for {@link SecureLockDeviceService}.
+ *
+ * <p>No permission / argument checks will be performed inside.
+ * Callers must check the calling app permission and the calling package name.
+ *
+ * @hide
+ */
+public abstract class SecureLockDeviceServiceInternal {
+ private static final String TAG = "SecureLockDeviceServiceInternal";
+
+ /**
+ * @see AuthenticationPolicyManager#enableSecureLockDevice(EnableSecureLockDeviceParams)
+ * @param params EnableSecureLockDeviceParams for caller to supply params related
+ * to the secure lock request
+ * @return @EnableSecureLockDeviceRequestStatus int indicating the result of the
+ * secure lock request
+ */
+ @EnableSecureLockDeviceRequestStatus
+ public abstract int enableSecureLockDevice(EnableSecureLockDeviceParams params);
+
+ /**
+ * @see AuthenticationPolicyManager#disableSecureLockDevice(DisableSecureLockDeviceParams)
+ * @param params @DisableSecureLockDeviceParams for caller to supply params related
+ * to the secure lock device request
+ * @return @DisableSecureLockDeviceRequestStatus int indicating the result of the
+ * secure lock device request
+ */
+ @DisableSecureLockDeviceRequestStatus
+ public abstract int disableSecureLockDevice(DisableSecureLockDeviceParams params);
+}
diff --git a/services/core/java/com/android/server/security/forensic/BackupTransportConnection.java b/services/core/java/com/android/server/security/forensic/ForensicEventTransportConnection.java
similarity index 69%
rename from services/core/java/com/android/server/security/forensic/BackupTransportConnection.java
rename to services/core/java/com/android/server/security/forensic/ForensicEventTransportConnection.java
index caca011..b85199e 100644
--- a/services/core/java/com/android/server/security/forensic/BackupTransportConnection.java
+++ b/services/core/java/com/android/server/security/forensic/ForensicEventTransportConnection.java
@@ -16,15 +16,19 @@
package com.android.server.security.forensic;
+import static android.Manifest.permission.BIND_FORENSIC_EVENT_TRANSPORT_SERVICE;
+
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.ServiceInfo;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.UserHandle;
import android.security.forensic.ForensicEvent;
-import android.security.forensic.IBackupTransport;
+import android.security.forensic.IForensicEventTransport;
import android.text.TextUtils;
import android.util.Slog;
@@ -36,20 +40,20 @@
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
-public class BackupTransportConnection implements ServiceConnection {
- private static final String TAG = "BackupTransportConnection";
+public class ForensicEventTransportConnection implements ServiceConnection {
+ private static final String TAG = "ForensicEventTransportConnection";
private static final long FUTURE_TIMEOUT_MILLIS = 60 * 1000; // 1 mins
private final Context mContext;
- private String mForensicBackupTransportConfig;
- volatile IBackupTransport mService;
+ private String mForensicEventTransportConfig;
+ volatile IForensicEventTransport mService;
- public BackupTransportConnection(Context context) {
+ public ForensicEventTransportConnection(Context context) {
mContext = context;
mService = null;
}
/**
- * Initialize the BackupTransport binder service.
+ * Initialize the ForensicEventTransport binder service.
* @return Whether the initialization succeed.
*/
public boolean initialize() {
@@ -74,7 +78,7 @@
}
/**
- * Add data to the BackupTransport binder service.
+ * Add data to the ForensicEventTransport binder service.
* @param data List of ForensicEvent.
* @return Whether the data is added to the binder service.
*/
@@ -109,21 +113,37 @@
return future.get(FUTURE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
} catch (InterruptedException | ExecutionException | TimeoutException
| CancellationException e) {
- Slog.w(TAG, "Failed to get result from transport:", e);
+ Slog.e(TAG, "Failed to get result from transport:", e);
return null;
}
}
private boolean bindService() {
- mForensicBackupTransportConfig = mContext.getString(
- com.android.internal.R.string.config_forensicBackupTransport);
- if (TextUtils.isEmpty(mForensicBackupTransportConfig)) {
+ mForensicEventTransportConfig = mContext.getString(
+ com.android.internal.R.string.config_forensicEventTransport);
+ if (TextUtils.isEmpty(mForensicEventTransportConfig)) {
+ Slog.e(TAG, "config_forensicEventTransport is empty");
return false;
}
ComponentName serviceComponent =
- ComponentName.unflattenFromString(mForensicBackupTransportConfig);
+ ComponentName.unflattenFromString(mForensicEventTransportConfig);
if (serviceComponent == null) {
+ Slog.e(TAG, "Can't get serviceComponent name");
+ return false;
+ }
+
+ try {
+ ServiceInfo serviceInfo = mContext.getPackageManager().getServiceInfo(serviceComponent,
+ 0 /* flags */);
+ if (!BIND_FORENSIC_EVENT_TRANSPORT_SERVICE.equals(serviceInfo.permission)) {
+ Slog.e(TAG, serviceComponent.flattenToShortString()
+ + " is not declared with the permission "
+ + "\"" + BIND_FORENSIC_EVENT_TRANSPORT_SERVICE + "\"");
+ return false;
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ Slog.e(TAG, "Unable to find serviceComponent");
return false;
}
@@ -143,7 +163,7 @@
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
- mService = IBackupTransport.Stub.asInterface(service);
+ mService = IForensicEventTransport.Stub.asInterface(service);
}
@Override
diff --git a/services/core/java/com/android/server/security/forensic/ForensicService.java b/services/core/java/com/android/server/security/forensic/ForensicService.java
index 01f630b..2be068f 100644
--- a/services/core/java/com/android/server/security/forensic/ForensicService.java
+++ b/services/core/java/com/android/server/security/forensic/ForensicService.java
@@ -16,11 +16,16 @@
package com.android.server.security.forensic;
+import static android.Manifest.permission.MANAGE_FORENSIC_STATE;
+import static android.Manifest.permission.READ_FORENSIC_STATE;
+
+import android.annotation.EnforcePermission;
import android.annotation.NonNull;
import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
+import android.os.PermissionEnforcer;
import android.os.RemoteException;
import android.security.forensic.ForensicEvent;
import android.security.forensic.IForensicService;
@@ -41,16 +46,15 @@
public class ForensicService extends SystemService {
private static final String TAG = "ForensicService";
- private static final int MSG_MONITOR_STATE = 0;
- private static final int MSG_MAKE_VISIBLE = 1;
- private static final int MSG_MAKE_INVISIBLE = 2;
- private static final int MSG_ENABLE = 3;
- private static final int MSG_DISABLE = 4;
- private static final int MSG_BACKUP = 5;
+ private static final int MAX_STATE_CALLBACK_NUM = 16;
+ private static final int MSG_ADD_STATE_CALLBACK = 0;
+ private static final int MSG_REMOVE_STATE_CALLBACK = 1;
+ private static final int MSG_ENABLE = 2;
+ private static final int MSG_DISABLE = 3;
+ private static final int MSG_TRANSPORT = 4;
private static final int STATE_UNKNOWN = IForensicServiceStateCallback.State.UNKNOWN;
- private static final int STATE_INVISIBLE = IForensicServiceStateCallback.State.INVISIBLE;
- private static final int STATE_VISIBLE = IForensicServiceStateCallback.State.VISIBLE;
+ private static final int STATE_DISABLED = IForensicServiceStateCallback.State.DISABLED;
private static final int STATE_ENABLED = IForensicServiceStateCallback.State.ENABLED;
private static final int ERROR_UNKNOWN = IForensicServiceCommandCallback.ErrorCode.UNKNOWN;
@@ -58,19 +62,19 @@
IForensicServiceCommandCallback.ErrorCode.PERMISSION_DENIED;
private static final int ERROR_INVALID_STATE_TRANSITION =
IForensicServiceCommandCallback.ErrorCode.INVALID_STATE_TRANSITION;
- private static final int ERROR_BACKUP_TRANSPORT_UNAVAILABLE =
- IForensicServiceCommandCallback.ErrorCode.BACKUP_TRANSPORT_UNAVAILABLE;
+ private static final int ERROR_TRANSPORT_UNAVAILABLE =
+ IForensicServiceCommandCallback.ErrorCode.TRANSPORT_UNAVAILABLE;
private static final int ERROR_DATA_SOURCE_UNAVAILABLE =
IForensicServiceCommandCallback.ErrorCode.DATA_SOURCE_UNAVAILABLE;
private final Context mContext;
private final Handler mHandler;
- private final BackupTransportConnection mBackupTransportConnection;
+ private final ForensicEventTransportConnection mForensicEventTransportConnection;
private final DataAggregator mDataAggregator;
private final BinderService mBinderService;
- private final ArrayList<IForensicServiceStateCallback> mStateMonitors = new ArrayList<>();
- private volatile int mState = STATE_INVISIBLE;
+ private final ArrayList<IForensicServiceStateCallback> mStateCallbacks = new ArrayList<>();
+ private volatile int mState = STATE_DISABLED;
public ForensicService(@NonNull Context context) {
this(new InjectorImpl(context));
@@ -81,9 +85,9 @@
super(injector.getContext());
mContext = injector.getContext();
mHandler = new EventHandler(injector.getLooper(), this);
- mBackupTransportConnection = injector.getBackupTransportConnection();
+ mForensicEventTransportConnection = injector.getForensicEventransportConnection();
mDataAggregator = injector.getDataAggregator(this);
- mBinderService = new BinderService(this);
+ mBinderService = new BinderService(this, injector.getPermissionEnforcer());
}
@VisibleForTesting
@@ -94,32 +98,36 @@
private static final class BinderService extends IForensicService.Stub {
final ForensicService mService;
- BinderService(ForensicService service) {
+ BinderService(ForensicService service, @NonNull PermissionEnforcer permissionEnforcer) {
+ super(permissionEnforcer);
mService = service;
}
@Override
- public void monitorState(IForensicServiceStateCallback callback) {
- mService.mHandler.obtainMessage(MSG_MONITOR_STATE, callback).sendToTarget();
+ @EnforcePermission(READ_FORENSIC_STATE)
+ public void addStateCallback(IForensicServiceStateCallback callback) {
+ addStateCallback_enforcePermission();
+ mService.mHandler.obtainMessage(MSG_ADD_STATE_CALLBACK, callback).sendToTarget();
}
@Override
- public void makeVisible(IForensicServiceCommandCallback callback) {
- mService.mHandler.obtainMessage(MSG_MAKE_VISIBLE, callback).sendToTarget();
+ @EnforcePermission(READ_FORENSIC_STATE)
+ public void removeStateCallback(IForensicServiceStateCallback callback) {
+ removeStateCallback_enforcePermission();
+ mService.mHandler.obtainMessage(MSG_REMOVE_STATE_CALLBACK, callback).sendToTarget();
}
@Override
- public void makeInvisible(IForensicServiceCommandCallback callback) {
- mService.mHandler.obtainMessage(MSG_MAKE_INVISIBLE, callback).sendToTarget();
- }
-
- @Override
+ @EnforcePermission(MANAGE_FORENSIC_STATE)
public void enable(IForensicServiceCommandCallback callback) {
+ enable_enforcePermission();
mService.mHandler.obtainMessage(MSG_ENABLE, callback).sendToTarget();
}
@Override
+ @EnforcePermission(MANAGE_FORENSIC_STATE)
public void disable(IForensicServiceCommandCallback callback) {
+ disable_enforcePermission();
mService.mHandler.obtainMessage(MSG_DISABLE, callback).sendToTarget();
}
}
@@ -135,24 +143,18 @@
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
- case MSG_MONITOR_STATE:
+ case MSG_ADD_STATE_CALLBACK:
try {
- mService.monitorState(
+ mService.addStateCallback(
(IForensicServiceStateCallback) msg.obj);
} catch (RemoteException e) {
Slog.e(TAG, "RemoteException", e);
}
break;
- case MSG_MAKE_VISIBLE:
+ case MSG_REMOVE_STATE_CALLBACK:
try {
- mService.makeVisible((IForensicServiceCommandCallback) msg.obj);
- } catch (RemoteException e) {
- Slog.e(TAG, "RemoteException", e);
- }
- break;
- case MSG_MAKE_INVISIBLE:
- try {
- mService.makeInvisible((IForensicServiceCommandCallback) msg.obj);
+ mService.removeStateCallback(
+ (IForensicServiceStateCallback) msg.obj);
} catch (RemoteException e) {
Slog.e(TAG, "RemoteException", e);
}
@@ -171,8 +173,8 @@
Slog.e(TAG, "RemoteException", e);
}
break;
- case MSG_BACKUP:
- mService.backup((List<ForensicEvent>) msg.obj);
+ case MSG_TRANSPORT:
+ mService.transport((List<ForensicEvent>) msg.obj);
break;
default:
Slog.w(TAG, "Unknown message: " + msg.what);
@@ -180,103 +182,83 @@
}
}
- private void monitorState(IForensicServiceStateCallback callback) throws RemoteException {
- for (int i = 0; i < mStateMonitors.size(); i++) {
- if (mStateMonitors.get(i).asBinder() == callback.asBinder()) {
+ private void addStateCallback(IForensicServiceStateCallback callback) throws RemoteException {
+ for (int i = 0; i < mStateCallbacks.size(); i++) {
+ if (mStateCallbacks.get(i).asBinder() == callback.asBinder()) {
return;
}
}
- mStateMonitors.add(callback);
+ mStateCallbacks.add(callback);
callback.onStateChange(mState);
}
- private void notifyStateMonitors() throws RemoteException {
- for (int i = 0; i < mStateMonitors.size(); i++) {
- mStateMonitors.get(i).onStateChange(mState);
+ private void removeStateCallback(IForensicServiceStateCallback callback)
+ throws RemoteException {
+ for (int i = 0; i < mStateCallbacks.size(); i++) {
+ if (mStateCallbacks.get(i).asBinder() == callback.asBinder()) {
+ mStateCallbacks.remove(i);
+ return;
+ }
}
}
- private void makeVisible(IForensicServiceCommandCallback callback) throws RemoteException {
- switch (mState) {
- case STATE_INVISIBLE:
- if (!mDataAggregator.initialize()) {
- callback.onFailure(ERROR_DATA_SOURCE_UNAVAILABLE);
- break;
- }
- mState = STATE_VISIBLE;
- notifyStateMonitors();
- callback.onSuccess();
- break;
- case STATE_VISIBLE:
- callback.onSuccess();
- break;
- default:
- callback.onFailure(ERROR_INVALID_STATE_TRANSITION);
+ private void notifyStateMonitors() {
+ if (mStateCallbacks.size() >= MAX_STATE_CALLBACK_NUM) {
+ mStateCallbacks.removeFirst();
}
- }
- private void makeInvisible(IForensicServiceCommandCallback callback) throws RemoteException {
- switch (mState) {
- case STATE_VISIBLE:
- case STATE_ENABLED:
- mState = STATE_INVISIBLE;
- notifyStateMonitors();
- callback.onSuccess();
- break;
- case STATE_INVISIBLE:
- callback.onSuccess();
- break;
- default:
- callback.onFailure(ERROR_INVALID_STATE_TRANSITION);
+ for (int i = 0; i < mStateCallbacks.size(); i++) {
+ try {
+ mStateCallbacks.get(i).onStateChange(mState);
+ } catch (RemoteException e) {
+ mStateCallbacks.remove(i);
+ }
}
}
private void enable(IForensicServiceCommandCallback callback) throws RemoteException {
- switch (mState) {
- case STATE_VISIBLE:
- if (!mBackupTransportConnection.initialize()) {
- callback.onFailure(ERROR_BACKUP_TRANSPORT_UNAVAILABLE);
- break;
- }
- mDataAggregator.enable();
- mState = STATE_ENABLED;
- notifyStateMonitors();
- callback.onSuccess();
- break;
- case STATE_ENABLED:
- callback.onSuccess();
- break;
- default:
- callback.onFailure(ERROR_INVALID_STATE_TRANSITION);
+ if (mState == STATE_ENABLED) {
+ callback.onSuccess();
+ return;
}
+
+ // TODO: temporarily disable the following for the CTS ForensicManagerTest.
+ // Enable it when the transport component is ready.
+ // if (!mForensicEventTransportConnection.initialize()) {
+ // callback.onFailure(ERROR_TRANSPORT_UNAVAILABLE);
+ // return;
+ // }
+
+ mDataAggregator.enable();
+ mState = STATE_ENABLED;
+ notifyStateMonitors();
+ callback.onSuccess();
}
private void disable(IForensicServiceCommandCallback callback) throws RemoteException {
- switch (mState) {
- case STATE_ENABLED:
- mBackupTransportConnection.release();
- mDataAggregator.disable();
- mState = STATE_VISIBLE;
- notifyStateMonitors();
- callback.onSuccess();
- break;
- case STATE_VISIBLE:
- callback.onSuccess();
- break;
- default:
- callback.onFailure(ERROR_INVALID_STATE_TRANSITION);
+ if (mState == STATE_DISABLED) {
+ callback.onSuccess();
+ return;
}
+
+ // TODO: temporarily disable the following for the CTS ForensicManagerTest.
+ // Enable it when the transport component is ready.
+ // mForensicEventTransportConnection.release();
+ mDataAggregator.disable();
+ mState = STATE_DISABLED;
+ notifyStateMonitors();
+ callback.onSuccess();
}
/**
* Add a list of ForensicEvent.
*/
public void addNewData(List<ForensicEvent> events) {
- mHandler.obtainMessage(MSG_BACKUP, events).sendToTarget();
+ mHandler.obtainMessage(MSG_TRANSPORT, events).sendToTarget();
}
- private void backup(List<ForensicEvent> events) {
- mBackupTransportConnection.addData(events);
+ private void transport(List<ForensicEvent> events) {
+ mForensicEventTransportConnection.addData(events);
}
@Override
@@ -296,9 +278,11 @@
interface Injector {
Context getContext();
+ PermissionEnforcer getPermissionEnforcer();
+
Looper getLooper();
- BackupTransportConnection getBackupTransportConnection();
+ ForensicEventTransportConnection getForensicEventransportConnection();
DataAggregator getDataAggregator(ForensicService forensicService);
}
@@ -315,6 +299,10 @@
return mContext;
}
+ @Override
+ public PermissionEnforcer getPermissionEnforcer() {
+ return PermissionEnforcer.fromContext(mContext);
+ }
@Override
public Looper getLooper() {
@@ -326,8 +314,8 @@
}
@Override
- public BackupTransportConnection getBackupTransportConnection() {
- return new BackupTransportConnection(mContext);
+ public ForensicEventTransportConnection getForensicEventransportConnection() {
+ return new ForensicEventTransportConnection(mContext);
}
@Override
diff --git a/services/core/java/com/android/server/security/forensic/SecurityLogSource.java b/services/core/java/com/android/server/security/forensic/SecurityLogSource.java
index 0f1aa42..e1b49c4 100644
--- a/services/core/java/com/android/server/security/forensic/SecurityLogSource.java
+++ b/services/core/java/com/android/server/security/forensic/SecurityLogSource.java
@@ -22,9 +22,7 @@
import android.app.admin.SecurityLog.SecurityEvent;
import android.content.Context;
import android.security.forensic.ForensicEvent;
-import android.util.ArrayMap;
-import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
@@ -34,10 +32,6 @@
public class SecurityLogSource implements DataSource {
private static final String TAG = "Forensic SecurityLogSource";
- private static final String EVENT_TYPE = "SecurityEvent";
- private static final String EVENT_TAG = "TAG";
- private static final String EVENT_TIME = "TIME";
- private static final String EVENT_DATA = "DATA";
private SecurityEventCallback mEventCallback = new SecurityEventCallback();
private DevicePolicyManager mDpm;
@@ -94,46 +88,9 @@
List<ForensicEvent> forensicEvents =
events.stream()
.filter(event -> event != null)
- .map(event -> toForensicEvent(event))
+ .map(event -> new ForensicEvent(event))
.collect(Collectors.toList());
mDataAggregator.addBatchData(forensicEvents);
}
-
- private ForensicEvent toForensicEvent(SecurityEvent event) {
- ArrayMap<String, String> keyValuePairs = new ArrayMap<>();
- keyValuePairs.put(EVENT_TIME, String.valueOf(event.getTimeNanos()));
- // TODO: Map tag to corresponding string
- keyValuePairs.put(EVENT_TAG, String.valueOf(event.getTag()));
- keyValuePairs.put(EVENT_DATA, eventDataToString(event.getData()));
- return new ForensicEvent(EVENT_TYPE, keyValuePairs);
- }
-
- /**
- * Convert event data to a String.
- *
- * @param obj Object containing an Integer, Long, Float, String, null, or Object[] of the
- * same.
- * @return String representation of event data.
- */
- private String eventDataToString(Object obj) {
- if (obj == null) {
- return "";
- } else if (obj instanceof Integer
- || obj instanceof Long
- || obj instanceof Float
- || obj instanceof String) {
- return String.valueOf(obj);
- } else if (obj instanceof Object[]) {
- Object[] objArray = (Object[]) obj;
- String[] strArray = new String[objArray.length];
- for (int i = 0; i < objArray.length; ++i) {
- strArray[i] = eventDataToString(objArray[i]);
- }
- return Arrays.toString((String[]) strArray);
- } else {
- throw new IllegalArgumentException(
- "Unsupported data type: " + obj.getClass().getSimpleName());
- }
- }
}
}
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index 465ac2f..887e186 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -61,6 +61,7 @@
import android.os.Looper;
import android.os.Message;
import android.os.PersistableBundle;
+import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
@@ -84,6 +85,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.content.PackageMonitor;
import com.android.internal.infra.AndroidFuture;
+import com.android.internal.policy.IDeviceLockedStateListener;
import com.android.internal.util.DumpUtils;
import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.LockSettingsInternal;
@@ -105,6 +107,7 @@
import java.util.Collection;
import java.util.List;
import java.util.Objects;
+import java.util.stream.IntStream;
/**
* Manages trust agents and trust listeners.
@@ -253,6 +256,10 @@
new SparseArray<>();
private final SparseArray<TrustableTimeoutAlarmListener>
mIdleTrustableTimeoutAlarmListenerForUser = new SparseArray<>();
+
+ private final RemoteCallbackList<IDeviceLockedStateListener>
+ mDeviceLockedStateListeners = new RemoteCallbackList<>();
+
private AlarmManager mAlarmManager;
private final Object mAlarmLock = new Object();
@@ -1090,6 +1097,7 @@
if (changed) {
notifyTrustAgentsOfDeviceLockState(userId, locked);
notifyKeystoreOfDeviceLockState(userId, locked);
+ notifyDeviceLockedListenersForUser(userId, locked);
// Also update the user's profiles who have unified challenge, since they
// share the same unlocked state (see {@link #isDeviceLocked(int)})
for (int profileHandle : mUserManager.getEnabledProfileIds(userId)) {
@@ -1910,6 +1918,26 @@
return mIsInSignificantPlace;
}
+ @EnforcePermission(Manifest.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE)
+ @Override
+ public void registerDeviceLockedStateListener(IDeviceLockedStateListener listener,
+ int deviceId) {
+ super.registerDeviceLockedStateListener_enforcePermission();
+ if (deviceId != Context.DEVICE_ID_DEFAULT) {
+ // Virtual devices are considered insecure.
+ return;
+ }
+ mDeviceLockedStateListeners.register(listener,
+ Integer.valueOf(UserHandle.getUserId(Binder.getCallingUid())));
+ }
+
+ @EnforcePermission(Manifest.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE)
+ @Override
+ public void unregisterDeviceLockedStateListener(IDeviceLockedStateListener listener) {
+ super.unregisterDeviceLockedStateListener_enforcePermission();
+ mDeviceLockedStateListeners.unregister(listener);
+ }
+
private void enforceReportPermission() {
mContext.enforceCallingOrSelfPermission(
Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE, "reporting trust events");
@@ -2031,6 +2059,7 @@
}
notifyKeystoreOfDeviceLockState(userId, locked);
+ notifyDeviceLockedListenersForUser(userId, locked);
if (locked) {
try {
@@ -2497,4 +2526,24 @@
updateTrust(mUserId, 0 /* flags */);
}
}
+
+ private void notifyDeviceLockedListenersForUser(int userId, boolean locked) {
+ int numListeners = mDeviceLockedStateListeners.beginBroadcast();
+ try {
+ IntStream.range(0, numListeners).forEach(i -> {
+ try {
+ Integer uid = (Integer) mDeviceLockedStateListeners.getBroadcastCookie(i);
+ if (userId == uid.intValue()) {
+ mDeviceLockedStateListeners.getBroadcastItem(i)
+ .onDeviceLockedStateChanged(locked);
+ }
+ } catch (RemoteException re) {
+ Log.i(TAG, "Service died", re);
+ }
+ });
+
+ } finally {
+ mDeviceLockedStateListeners.finishBroadcast();
+ }
+ }
}
diff --git a/services/core/java/com/android/server/vibrator/AbstractVibratorStep.java b/services/core/java/com/android/server/vibrator/AbstractVibratorStep.java
index 42203b1..07d9ad1 100644
--- a/services/core/java/com/android/server/vibrator/AbstractVibratorStep.java
+++ b/services/core/java/com/android/server/vibrator/AbstractVibratorStep.java
@@ -112,6 +112,14 @@
}
protected void stopVibrating() {
+ if (conductor.isInSession) {
+ if (VibrationThread.DEBUG) {
+ Slog.d(VibrationThread.TAG,
+ "Vibration in session, skipping request to turn off vibrator "
+ + getVibratorId());
+ }
+ return;
+ }
if (VibrationThread.DEBUG) {
Slog.d(VibrationThread.TAG,
"Turning off vibrator " + getVibratorId());
diff --git a/services/core/java/com/android/server/vibrator/SplitPwleSegmentsAdapter.java b/services/core/java/com/android/server/vibrator/SplitPwleSegmentsAdapter.java
index ad44227..a8c4ac8 100644
--- a/services/core/java/com/android/server/vibrator/SplitPwleSegmentsAdapter.java
+++ b/services/core/java/com/android/server/vibrator/SplitPwleSegmentsAdapter.java
@@ -44,7 +44,7 @@
// The vibrator does not have PWLE v2 capability, so keep the segments unchanged.
return repeatIndex;
}
- int maxPwleDuration = info.getMaxEnvelopeEffectDurationMillis();
+ int maxPwleDuration = (int) info.getMaxEnvelopeEffectDurationMillis();
if (maxPwleDuration <= 0) {
// No limit set to PWLE primitive duration.
return repeatIndex;
diff --git a/services/core/java/com/android/server/vibrator/VendorVibrationSession.java b/services/core/java/com/android/server/vibrator/VendorVibrationSession.java
index 07478e3..9e75cf2 100644
--- a/services/core/java/com/android/server/vibrator/VendorVibrationSession.java
+++ b/services/core/java/com/android/server/vibrator/VendorVibrationSession.java
@@ -37,8 +37,11 @@
import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import java.util.ArrayList;
import java.util.Arrays;
+import java.util.List;
import java.util.Locale;
import java.util.NoSuchElementException;
@@ -60,6 +63,9 @@
* used for another vibration.
*/
void onSessionReleased(long sessionId);
+
+ /** Request the manager to trigger a vibration within this session. */
+ void vibrate(long sessionId, CallerInfo callerInfo, CombinedVibration vibration);
}
private final Object mLock = new Object();
@@ -71,7 +77,9 @@
private final IVibrationSessionCallback mCallback;
private final CallerInfo mCallerInfo;
private final VibratorManagerHooks mManagerHooks;
+ private final DeviceAdapter mDeviceAdapter;
private final Handler mHandler;
+ private final List<DebugInfo> mVibrations = new ArrayList<>();
@GuardedBy("mLock")
private Status mStatus = Status.RUNNING;
@@ -83,24 +91,28 @@
private long mEndUptime;
@GuardedBy("mLock")
private long mEndTime; // for debugging
+ @GuardedBy("mLock")
+ private VibrationStepConductor mConductor;
VendorVibrationSession(@NonNull CallerInfo callerInfo, @NonNull Handler handler,
- @NonNull VibratorManagerHooks managerHooks, @NonNull int[] vibratorIds,
+ @NonNull VibratorManagerHooks managerHooks, @NonNull DeviceAdapter deviceAdapter,
@NonNull IVibrationSessionCallback callback) {
mCreateUptime = SystemClock.uptimeMillis();
mCreateTime = System.currentTimeMillis();
- mVibratorIds = vibratorIds;
+ mVibratorIds = deviceAdapter.getAvailableVibratorIds();
mHandler = handler;
mCallback = callback;
mCallerInfo = callerInfo;
mManagerHooks = managerHooks;
+ mDeviceAdapter = deviceAdapter;
CancellationSignal.fromTransport(mCancellationSignal).setOnCancelListener(this);
}
@Override
public void vibrate(CombinedVibration vibration, String reason) {
- // TODO(b/345414356): implement vibration support
- throw new UnsupportedOperationException("Vendor session vibrations not yet implemented");
+ CallerInfo vibrationCallerInfo = new CallerInfo(mCallerInfo.attrs, mCallerInfo.uid,
+ mCallerInfo.deviceId, mCallerInfo.opPkg, reason);
+ mManagerHooks.vibrate(mSessionId, vibrationCallerInfo, vibration);
}
@Override
@@ -146,7 +158,7 @@
public DebugInfo getDebugInfo() {
synchronized (mLock) {
return new DebugInfoImpl(mStatus, mCallerInfo, mCreateUptime, mCreateTime, mStartTime,
- mEndUptime, mEndTime);
+ mEndUptime, mEndTime, mVibrations);
}
}
@@ -200,12 +212,12 @@
@Override
public void notifyVibratorCallback(int vibratorId, long vibrationId) {
- // TODO(b/345414356): implement vibration support
+ // Ignore it, the session vibration playback doesn't depend on HAL timings
}
@Override
public void notifySyncedVibratorsCallback(long vibrationId) {
- // TODO(b/345414356): implement vibration support
+ // Ignore it, the session vibration playback doesn't depend on HAL timings
}
@Override
@@ -214,8 +226,9 @@
// If end was not requested then the HAL has cancelled the session.
maybeSetEndRequestLocked(Status.CANCELLED_BY_UNKNOWN_REASON);
maybeSetStatusToRequestedLocked();
+ clearVibrationConductor();
}
- mManagerHooks.onSessionReleased(mSessionId);
+ mHandler.post(() -> mManagerHooks.onSessionReleased(mSessionId));
}
@Override
@@ -228,7 +241,8 @@
/* includeDate= */ true))
+ ", status: " + mStatus.name().toLowerCase(Locale.ROOT)
+ ", callerInfo: " + mCallerInfo
- + ", vibratorIds: " + Arrays.toString(mVibratorIds);
+ + ", vibratorIds: " + Arrays.toString(mVibratorIds)
+ + ", vibrations: " + mVibrations;
}
}
@@ -254,6 +268,13 @@
return mVibratorIds;
}
+ @VisibleForTesting
+ public List<DebugInfo> getVibrations() {
+ synchronized (mLock) {
+ return new ArrayList<>(mVibrations);
+ }
+ }
+
public ICancellationSignal getCancellationSignal() {
return mCancellationSignal;
}
@@ -278,7 +299,39 @@
}
if (isAlreadyEnded) {
// Session already ended, make sure we end it in the HAL.
- mManagerHooks.endSession(mSessionId, /* shouldAbort= */ true);
+ mHandler.post(() -> mManagerHooks.endSession(mSessionId, /* shouldAbort= */ true));
+ }
+ }
+
+ public void notifyVibrationAttempt(DebugInfo vibrationDebugInfo) {
+ mVibrations.add(vibrationDebugInfo);
+ }
+
+ @Nullable
+ public VibrationStepConductor clearVibrationConductor() {
+ synchronized (mLock) {
+ VibrationStepConductor conductor = mConductor;
+ if (conductor != null) {
+ mVibrations.add(conductor.getVibration().getDebugInfo());
+ }
+ mConductor = null;
+ return conductor;
+ }
+ }
+
+ public DeviceAdapter getDeviceAdapter() {
+ return mDeviceAdapter;
+ }
+
+ public boolean maybeSetVibrationConductor(VibrationStepConductor conductor) {
+ synchronized (mLock) {
+ if (mConductor != null) {
+ Slog.d(TAG, "Vibration session still dispatching previous vibration,"
+ + " new vibration ignored");
+ return false;
+ }
+ mConductor = conductor;
+ return true;
}
}
@@ -296,7 +349,7 @@
}
}
if (shouldTriggerSessionHook) {
- mManagerHooks.endSession(mSessionId, shouldAbort);
+ mHandler.post(() -> mManagerHooks.endSession(mSessionId, shouldAbort));
}
}
@@ -309,6 +362,11 @@
mEndStatusRequest = status;
mEndTime = System.currentTimeMillis();
mEndUptime = SystemClock.uptimeMillis();
+ if (mConductor != null) {
+ // Vibration is being dispatched when session end was requested, cancel it.
+ mConductor.notifyCancelled(new Vibration.EndInfo(status),
+ /* immediate= */ status != Status.FINISHED);
+ }
if (isStarted()) {
// Only trigger "finishing" callback if session started.
// Run client callback in separate thread.
@@ -377,6 +435,7 @@
static final class DebugInfoImpl implements VibrationSession.DebugInfo {
private final Status mStatus;
private final CallerInfo mCallerInfo;
+ private final List<DebugInfo> mVibrations;
private final long mCreateUptime;
private final long mCreateTime;
@@ -385,7 +444,7 @@
private final long mDurationMs;
DebugInfoImpl(Status status, CallerInfo callerInfo, long createUptime, long createTime,
- long startTime, long endUptime, long endTime) {
+ long startTime, long endUptime, long endTime, List<DebugInfo> vibrations) {
mStatus = status;
mCallerInfo = callerInfo;
mCreateUptime = createUptime;
@@ -393,6 +452,7 @@
mStartTime = startTime;
mEndTime = endTime;
mDurationMs = endUptime > 0 ? endUptime - createUptime : -1;
+ mVibrations = vibrations == null ? new ArrayList<>() : new ArrayList<>(vibrations);
}
@Override
@@ -418,6 +478,9 @@
@Override
public void logMetrics(VibratorFrameworkStatsLogger statsLogger) {
+ for (DebugInfo vibration : mVibrations) {
+ vibration.logMetrics(statsLogger);
+ }
}
@Override
@@ -448,6 +511,14 @@
pw.println("endTime = " + (mEndTime == 0 ? null
: formatTime(mEndTime, /*includeDate=*/ true)));
pw.println("callerInfo = " + mCallerInfo);
+
+ pw.println("vibrations:");
+ pw.increaseIndent();
+ for (DebugInfo vibration : mVibrations) {
+ vibration.dump(pw);
+ }
+ pw.decreaseIndent();
+
pw.decreaseIndent();
}
@@ -477,6 +548,12 @@
" | %s (uid=%d, deviceId=%d) | reason: %s",
mCallerInfo.opPkg, mCallerInfo.uid, mCallerInfo.deviceId, mCallerInfo.reason);
pw.println(timingsStr + paramStr + audioUsageStr + callerStr);
+
+ pw.increaseIndent();
+ for (DebugInfo vibration : mVibrations) {
+ vibration.dumpCompact(pw);
+ }
+ pw.decreaseIndent();
}
@Override
@@ -487,7 +564,8 @@
/* includeDate= */ true))
+ ", durationMs: " + mDurationMs
+ ", status: " + mStatus.name().toLowerCase(Locale.ROOT)
- + ", callerInfo: " + mCallerInfo;
+ + ", callerInfo: " + mCallerInfo
+ + ", vibrations: " + mVibrations;
}
}
}
diff --git a/services/core/java/com/android/server/vibrator/VibrationStepConductor.java b/services/core/java/com/android/server/vibrator/VibrationStepConductor.java
index 6a4790d..1e20deb 100644
--- a/services/core/java/com/android/server/vibrator/VibrationStepConductor.java
+++ b/services/core/java/com/android/server/vibrator/VibrationStepConductor.java
@@ -69,6 +69,7 @@
// Used within steps.
public final VibrationSettings vibrationSettings;
public final VibrationThread.VibratorManagerHooks vibratorManagerHooks;
+ public final boolean isInSession;
private final DeviceAdapter mDeviceAdapter;
private final VibrationScaler mVibrationScaler;
@@ -105,12 +106,13 @@
private int mRemainingStartSequentialEffectSteps;
private int mSuccessfulVibratorOnSteps;
- VibrationStepConductor(HalVibration vib, VibrationSettings vibrationSettings,
- DeviceAdapter deviceAdapter, VibrationScaler vibrationScaler,
- VibratorFrameworkStatsLogger statsLogger,
+ VibrationStepConductor(HalVibration vib, boolean isInSession,
+ VibrationSettings vibrationSettings, DeviceAdapter deviceAdapter,
+ VibrationScaler vibrationScaler, VibratorFrameworkStatsLogger statsLogger,
CompletableFuture<Void> requestVibrationParamsFuture,
VibrationThread.VibratorManagerHooks vibratorManagerHooks) {
this.mVibration = vib;
+ this.isInSession = isInSession;
this.vibrationSettings = vibrationSettings;
this.mDeviceAdapter = deviceAdapter;
mVibrationScaler = vibrationScaler;
@@ -286,6 +288,9 @@
if (nextStep == null) {
return true; // Finished
}
+ if (isInSession) {
+ return true; // Don't wait to play session vibration steps
+ }
long waitMillis = nextStep.calculateWaitTime();
if (waitMillis <= 0) {
return true; // Regular step ready
diff --git a/services/core/java/com/android/server/vibrator/VibratorManagerService.java b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
index 4764481..cc163db 100644
--- a/services/core/java/com/android/server/vibrator/VibratorManagerService.java
+++ b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
@@ -106,7 +106,7 @@
private static final String EXTERNAL_VIBRATOR_SERVICE = "external_vibrator_service";
private static final String VIBRATOR_CONTROL_SERVICE =
"android.frameworks.vibrator.IVibratorControlService/default";
- private static final boolean DEBUG = true;
+ private static final boolean DEBUG = false;
private static final VibrationAttributes DEFAULT_ATTRIBUTES =
new VibrationAttributes.Builder().build();
private static final int ATTRIBUTES_ALL_BYPASS_FLAGS =
@@ -610,6 +610,11 @@
logAndRecordVibrationAttempt(effect, callerInfo, Status.IGNORED_ERROR_TOKEN);
return null;
}
+ enforceUpdateAppOpsStatsPermission(uid);
+ if (!isEffectValid(effect)) {
+ logAndRecordVibrationAttempt(effect, callerInfo, Status.IGNORED_UNSUPPORTED);
+ return null;
+ }
if (effect.hasVendorEffects()) {
if (!Flags.vendorVibrationEffects()) {
Slog.e(TAG, "vibrate; vendor effects feature disabled");
@@ -622,11 +627,6 @@
return null;
}
}
- enforceUpdateAppOpsStatsPermission(uid);
- if (!isEffectValid(effect)) {
- logAndRecordVibrationAttempt(effect, callerInfo, Status.IGNORED_UNSUPPORTED);
- return null;
- }
// Create Vibration.Stats as close to the received request as possible, for tracking.
SingleVibrationSession session = new SingleVibrationSession(token, callerInfo, effect);
HalVibration vib = session.getVibration();
@@ -658,6 +658,7 @@
// If not ignored so far then try to start this vibration.
if (ignoreStatus == null) {
+ // TODO(b/378492007): Investigate if we can move this around AppOpsManager calls
final long ident = Binder.clearCallingIdentity();
try {
if (mCurrentSession != null) {
@@ -703,6 +704,7 @@
if (DEBUG) {
Slog.d(TAG, "Canceling vibration");
}
+ // TODO(b/378492007): Investigate if we can move this around AppOpsManager calls
final long ident = Binder.clearCallingIdentity();
try {
// TODO(b/370948466): investigate why token not checked on external vibrations.
@@ -762,8 +764,18 @@
vibratorIds = new int[0];
}
enforceUpdateAppOpsStatsPermission(uid);
+
+ // Create session with adapter that only uses the session vibrators.
+ SparseArray<VibratorController> sessionVibrators = new SparseArray<>(vibratorIds.length);
+ for (int vibratorId : vibratorIds) {
+ VibratorController controller = mVibrators.get(vibratorId);
+ if (controller != null) {
+ sessionVibrators.put(vibratorId, controller);
+ }
+ }
+ DeviceAdapter deviceAdapter = new DeviceAdapter(mVibrationSettings, sessionVibrators);
VendorVibrationSession session = new VendorVibrationSession(callerInfo, mHandler,
- mVendorVibrationSessionCallbacks, vibratorIds, callback);
+ mVendorVibrationSessionCallbacks, deviceAdapter, callback);
if (attrs.isFlagSet(VibrationAttributes.FLAG_INVALIDATE_SETTINGS_CACHE)) {
// Force update of user settings before checking if this vibration effect should
@@ -787,12 +799,15 @@
ignoreStatus = Status.IGNORED_UNSUPPORTED;
}
- // Check if any vibrator ID was requested.
- if (ignoreStatus == null && vibratorIds.length == 0) {
- if (DEBUG) {
- Slog.d(TAG, "Empty vibrator ids to start session, ignoring request");
+ // Check if vibrator IDs requested are available.
+ if (ignoreStatus == null) {
+ if (vibratorIds.length == 0
+ || vibratorIds.length != deviceAdapter.getAvailableVibratorIds().length) {
+ Slog.e(TAG, "Bad vibrator ids to start session, ignoring request."
+ + " requested=" + Arrays.toString(vibratorIds)
+ + " available=" + Arrays.toString(mVibratorIds));
+ ignoreStatus = Status.IGNORED_UNSUPPORTED;
}
- ignoreStatus = Status.IGNORED_UNSUPPORTED;
}
// Check if user settings or DnD is set to ignore this session.
@@ -810,6 +825,7 @@
}
if (ignoreStatus == null) {
+ // TODO(b/378492007): Investigate if we can move this around AppOpsManager calls
final long ident = Binder.clearCallingIdentity();
try {
// If not ignored so far then stop ongoing sessions before starting this one.
@@ -839,22 +855,40 @@
private Status startVendorSessionLocked(VendorVibrationSession session) {
Trace.traceBegin(TRACE_TAG_VIBRATOR, "startSessionLocked");
try {
+ long sessionId = session.getSessionId();
+ if (DEBUG) {
+ Slog.d(TAG, "Starting session " + sessionId + " in HAL");
+ }
if (session.isEnded()) {
// Session already ended, possibly cancelled by app cancellation signal.
return session.getStatus();
}
- if (!session.linkToDeath()) {
- return Status.IGNORED_ERROR_TOKEN;
+ int mode = startAppOpModeLocked(session.getCallerInfo());
+ switch (mode) {
+ case AppOpsManager.MODE_ALLOWED:
+ Trace.asyncTraceBegin(TRACE_TAG_VIBRATOR, "vibration", 0);
+ // Make sure mCurrentVibration is set while triggering the HAL.
+ mCurrentSession = session;
+ if (!session.linkToDeath()) {
+ mCurrentSession = null;
+ return Status.IGNORED_ERROR_TOKEN;
+ }
+ if (!mNativeWrapper.startSession(sessionId, session.getVibratorIds())) {
+ Slog.e(TAG, "Error starting session " + sessionId + " on vibrators "
+ + Arrays.toString(session.getVibratorIds()));
+ session.unlinkToDeath();
+ mCurrentSession = null;
+ return Status.IGNORED_UNSUPPORTED;
+ }
+ session.notifyStart();
+ return null;
+ case AppOpsManager.MODE_ERRORED:
+ Slog.w(TAG, "Start AppOpsManager operation errored for uid "
+ + session.getCallerInfo().uid);
+ return Status.IGNORED_ERROR_APP_OPS;
+ default:
+ return Status.IGNORED_APP_OPS;
}
- if (!mNativeWrapper.startSession(session.getSessionId(), session.getVibratorIds())) {
- Slog.e(TAG, "Error starting session " + session.getSessionId()
- + " on vibrators " + Arrays.toString(session.getVibratorIds()));
- session.unlinkToDeath();
- return Status.IGNORED_UNSUPPORTED;
- }
- session.notifyStart();
- mCurrentSession = session;
- return null;
} finally {
Trace.traceEnd(TRACE_TAG_VIBRATOR);
}
@@ -1045,6 +1079,9 @@
@GuardedBy("mLock")
@Nullable
private Status startVibrationOnThreadLocked(SingleVibrationSession session) {
+ if (DEBUG) {
+ Slog.d(TAG, "Starting vibration " + session.getVibration().id + " on thread");
+ }
VibrationStepConductor conductor = createVibrationStepConductor(session.getVibration());
session.setVibrationConductor(conductor);
int mode = startAppOpModeLocked(session.getCallerInfo());
@@ -1080,12 +1117,18 @@
mNextSession = null;
Status errorStatus = startVibrationOnThreadLocked(session);
if (errorStatus != null) {
+ if (DEBUG) {
+ Slog.d(TAG, "Error starting next vibration " + session.getVibration().id);
+ }
endSessionLocked(session, errorStatus);
}
} else if (mNextSession instanceof VendorVibrationSession session) {
mNextSession = null;
Status errorStatus = startVendorSessionLocked(session);
if (errorStatus != null) {
+ if (DEBUG) {
+ Slog.d(TAG, "Error starting next session " + session.getSessionId());
+ }
endSessionLocked(session, errorStatus);
}
} // External vibrations cannot be started asynchronously.
@@ -1103,6 +1146,16 @@
}
private VibrationStepConductor createVibrationStepConductor(HalVibration vib) {
+ return createVibrationStepConductor(vib, mDeviceAdapter, /* isInSession= */ false);
+ }
+
+ private VibrationStepConductor createSessionVibrationStepConductor(HalVibration vib,
+ DeviceAdapter deviceAdapter) {
+ return createVibrationStepConductor(vib, deviceAdapter, /* isInSession= */ true);
+ }
+
+ private VibrationStepConductor createVibrationStepConductor(HalVibration vib,
+ DeviceAdapter deviceAdapter, boolean isInSession) {
CompletableFuture<Void> requestVibrationParamsFuture = null;
if (Flags.adaptiveHapticsEnabled()
@@ -1114,8 +1167,9 @@
mVibrationSettings.getRequestVibrationParamsTimeoutMs());
}
- return new VibrationStepConductor(vib, mVibrationSettings, mDeviceAdapter, mVibrationScaler,
- mFrameworkStatsLogger, requestVibrationParamsFuture, mVibrationThreadCallbacks);
+ return new VibrationStepConductor(vib, isInSession, mVibrationSettings,
+ deviceAdapter, mVibrationScaler, mFrameworkStatsLogger,
+ requestVibrationParamsFuture, mVibrationThreadCallbacks);
}
private Status startVibrationOnInputDevicesLocked(HalVibration vib) {
@@ -1135,18 +1189,15 @@
private void logAndRecordVibrationAttempt(@Nullable CombinedVibration effect,
CallerInfo callerInfo, Status status) {
- logAndRecordVibration(
- new Vibration.DebugInfoImpl(status, callerInfo,
- VibrationStats.StatsInfo.findVibrationType(effect), new VibrationStats(),
- effect, /* originalEffect= */ null, VibrationScaler.SCALE_NONE,
- VibrationScaler.ADAPTIVE_SCALE_NONE));
+ logAndRecordVibration(createVibrationAttemptDebugInfo(effect, callerInfo, status));
}
private void logAndRecordSessionAttempt(CallerInfo callerInfo, Status status) {
logAndRecordVibration(
new VendorVibrationSession.DebugInfoImpl(status, callerInfo,
SystemClock.uptimeMillis(), System.currentTimeMillis(),
- /* startTime= */ 0, /* endUptime= */ 0, /* endTime= */ 0));
+ /* startTime= */ 0, /* endUptime= */ 0, /* endTime= */ 0,
+ /* vibrations= */ null));
}
private void logAndRecordVibration(DebugInfo info) {
@@ -1155,6 +1206,14 @@
mVibratorManagerRecords.record(info);
}
+ private DebugInfo createVibrationAttemptDebugInfo(@Nullable CombinedVibration effect,
+ CallerInfo callerInfo, Status status) {
+ return new Vibration.DebugInfoImpl(status, callerInfo,
+ VibrationStats.StatsInfo.findVibrationType(effect), new VibrationStats(),
+ effect, /* originalEffect= */ null, VibrationScaler.SCALE_NONE,
+ VibrationScaler.ADAPTIVE_SCALE_NONE);
+ }
+
private void logVibrationStatus(int uid, VibrationAttributes attrs, Status status) {
switch (status) {
case IGNORED_BACKGROUND:
@@ -1765,25 +1824,35 @@
Trace.traceBegin(TRACE_TAG_VIBRATOR, "onVibrationThreadReleased");
try {
synchronized (mLock) {
- if (!(mCurrentSession instanceof SingleVibrationSession session)) {
- if (Build.IS_DEBUGGABLE) {
- Slog.wtf(TAG, "VibrationSession invalid on vibration thread release."
- + " currentSession=" + mCurrentSession);
+ if (mCurrentSession instanceof SingleVibrationSession session) {
+ if (Build.IS_DEBUGGABLE && (session.getVibration().id != vibrationId)) {
+ Slog.wtf(TAG, TextUtils.formatSimple(
+ "VibrationId mismatch on vibration thread release."
+ + " expected=%d, released=%d",
+ session.getVibration().id, vibrationId));
}
- // Only single vibration sessions are ended by thread being released. Abort.
- return;
+ finishAppOpModeLocked(mCurrentSession.getCallerInfo());
+ clearCurrentSessionLocked();
+ Trace.asyncTraceEnd(Trace.TRACE_TAG_VIBRATOR, "vibration", 0);
+ // Start next vibration if it's waiting for the thread.
+ maybeStartNextSessionLocked();
+ } else if (mCurrentSession instanceof VendorVibrationSession session) {
+ VibrationStepConductor conductor = session.clearVibrationConductor();
+ if (Build.IS_DEBUGGABLE) {
+ if (conductor == null) {
+ Slog.wtf(TAG, "Vendor session without ongoing vibration on"
+ + " thread release. currentSession=" + mCurrentSession);
+ } else if (conductor.getVibration().id != vibrationId) {
+ Slog.wtf(TAG, TextUtils.formatSimple(
+ "VibrationId mismatch on vibration thread release."
+ + " expected=%d, released=%d",
+ conductor.getVibration().id, vibrationId));
+ }
+ }
+ } else if (Build.IS_DEBUGGABLE) {
+ Slog.wtf(TAG, "VibrationSession invalid on vibration thread release."
+ + " currentSession=" + mCurrentSession);
}
- if (Build.IS_DEBUGGABLE && (session.getVibration().id != vibrationId)) {
- Slog.wtf(TAG, TextUtils.formatSimple(
- "VibrationId mismatch on vibration thread release."
- + " expected=%d, released=%d",
- session.getVibration().id, vibrationId));
- }
- finishAppOpModeLocked(mCurrentSession.getCallerInfo());
- clearCurrentSessionLocked();
- Trace.asyncTraceEnd(Trace.TRACE_TAG_VIBRATOR, "vibration", 0);
- // Start next vibration if it's waiting for the thread.
- maybeStartNextSessionLocked();
}
} finally {
Trace.traceEnd(TRACE_TAG_VIBRATOR);
@@ -1838,6 +1907,86 @@
implements VendorVibrationSession.VibratorManagerHooks {
@Override
+ public void vibrate(long sessionId, CallerInfo callerInfo, CombinedVibration effect) {
+ if (DEBUG) {
+ Slog.d(TAG, "Vibration session " + sessionId + " vibration requested");
+ }
+ Trace.traceBegin(TRACE_TAG_VIBRATOR, "sessionVibrate");
+ try {
+ synchronized (mLock) {
+ if (!(mCurrentSession instanceof VendorVibrationSession session)) {
+ if (Build.IS_DEBUGGABLE) {
+ Slog.wtf(TAG, "VibrationSession invalid on session vibrate."
+ + " currentSession=" + mCurrentSession);
+ }
+ // Only vendor vibration sessions can handle this call. Abort.
+ return;
+ }
+ if (session.getSessionId() != sessionId) {
+ if (Build.IS_DEBUGGABLE) {
+ Slog.wtf(TAG, TextUtils.formatSimple(
+ "SessionId mismatch on vendor vibration session vibrate."
+ + " expected=%d, released=%d",
+ session.getSessionId(), sessionId));
+ }
+ // Only the ongoing vendor vibration sessions can handle this call. Abort.
+ return;
+ }
+ if (session.wasEndRequested()) {
+ if (DEBUG) {
+ Slog.d(TAG, "session vibrate; session is ending, vibration ignored");
+ }
+ session.notifyVibrationAttempt(createVibrationAttemptDebugInfo(effect,
+ callerInfo, Status.IGNORED_ERROR_SCHEDULING));
+ return;
+ }
+ if (!isEffectValid(effect)) {
+ session.notifyVibrationAttempt(createVibrationAttemptDebugInfo(effect,
+ callerInfo, Status.IGNORED_UNSUPPORTED));
+ return;
+ }
+ if (effect.getDuration() == Long.MAX_VALUE) {
+ // Repeating effects cannot be played by the service in a session.
+ session.notifyVibrationAttempt(createVibrationAttemptDebugInfo(effect,
+ callerInfo, Status.IGNORED_UNSUPPORTED));
+ return;
+ }
+ // Create Vibration.Stats as close to the request as possible, for tracking.
+ HalVibration vib = new HalVibration(callerInfo, effect);
+ vib.fillFallbacks(mVibrationSettings::getFallbackEffect);
+
+ if (callerInfo.attrs.isFlagSet(
+ VibrationAttributes.FLAG_INVALIDATE_SETTINGS_CACHE)) {
+ // Force update of user settings before checking if this vibration effect
+ // should be ignored or scaled.
+ mVibrationSettings.update();
+ }
+
+ if (DEBUG) {
+ Slog.d(TAG, "Starting vibrate for vibration " + vib.id
+ + " in session " + sessionId);
+ }
+
+ VibrationStepConductor conductor =
+ createSessionVibrationStepConductor(vib, session.getDeviceAdapter());
+ if (session.maybeSetVibrationConductor(conductor)) {
+ if (!mVibrationThread.runVibrationOnVibrationThread(conductor)) {
+ // Shouldn't happen. The method call already logs.
+ vib.end(new Vibration.EndInfo(Status.IGNORED_ERROR_SCHEDULING));
+ session.clearVibrationConductor(); // Rejected by thread, clear it.
+ }
+ } else {
+ // Cannot set vibration in session, log failed attempt.
+ session.notifyVibrationAttempt(createVibrationAttemptDebugInfo(effect,
+ callerInfo, Status.IGNORED_ERROR_SCHEDULING));
+ }
+ }
+ } finally {
+ Trace.traceEnd(TRACE_TAG_VIBRATOR);
+ }
+ }
+
+ @Override
public void endSession(long sessionId, boolean shouldAbort) {
if (DEBUG) {
Slog.d(TAG, "Vibration session " + sessionId
@@ -1873,6 +2022,12 @@
+ " expected=%d, released=%d",
session.getSessionId(), sessionId));
}
+ // Make sure all controllers in session are reset after session ended.
+ // This will update the vibrator state to isVibrating = false for listeners.
+ for (int vibratorId : session.getVibratorIds()) {
+ mVibrators.get(vibratorId).off();
+ }
+ finishAppOpModeLocked(mCurrentSession.getCallerInfo());
clearCurrentSessionLocked();
// Start next vibration if it's waiting for the HAL session to be over.
maybeStartNextSessionLocked();
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index c6e6e76..f70dec1 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -630,8 +630,8 @@
// The locusId associated with this activity, if set.
private LocusId mLocusId;
- // Whether the activity is requesting to limit the system's educational dialogs
- public boolean mShouldLimitSystemEducationDialogs;
+ // The timestamp of the last request to show the "Open in browser" education
+ public long mRequestOpenInBrowserEducationTimestamp;
// Whether the activity was launched from a bubble.
private boolean mLaunchedFromBubble;
@@ -1623,6 +1623,11 @@
newParent.setResumedActivity(this, "onParentChanged");
}
mAppCompatController.getTransparentPolicy().start();
+ if (mState == INITIALIZING && isRestrictedFixedOrientation(info.screenOrientation)) {
+ Slog.i(TAG, "Ignoring manifest-declared fixed orientation "
+ + ActivityInfo.screenOrientationToString(info.screenOrientation)
+ + " of " + this + " since target sdk 36");
+ }
}
if (rootTask != null && rootTask.topRunningActivity() == this) {
@@ -3192,6 +3197,17 @@
}
/**
+ * Returns {@code true} if the orientation will be ignored for {@link #isUniversalResizeable()}.
+ */
+ private boolean isRestrictedFixedOrientation(
+ @ActivityInfo.ScreenOrientation int orientation) {
+ // Exclude "locked" because it is not explicit portrait or landscape.
+ return orientation != ActivityInfo.SCREEN_ORIENTATION_LOCKED
+ && ActivityInfo.isFixedOrientation(orientation)
+ && isUniversalResizeable();
+ }
+
+ /**
* Returns {@code true} if the fixed orientation, aspect ratio, resizability of this activity
* will be ignored.
*/
@@ -7326,9 +7342,8 @@
return mLocusId;
}
- void setLimitSystemEducationDialogs(boolean limitSystemEducationDialogs) {
- if (mShouldLimitSystemEducationDialogs == limitSystemEducationDialogs) return;
- mShouldLimitSystemEducationDialogs = limitSystemEducationDialogs;
+ void requestOpenInBrowserEducation() {
+ mRequestOpenInBrowserEducationTimestamp = System.currentTimeMillis();
final Task task = getTask();
if (task != null) {
final boolean force = isVisibleRequested() && this == task.getTopNonFinishingActivity();
@@ -8123,7 +8138,13 @@
ProtoLog.v(WM_DEBUG_ORIENTATION,
"Setting requested orientation %s for %s",
ActivityInfo.screenOrientationToString(requestedOrientation), this);
- setOrientation(requestedOrientation, this);
+ final int resolvedOrientation = setOrientation(requestedOrientation, this);
+ if (resolvedOrientation != requestedOrientation
+ && isRestrictedFixedOrientation(requestedOrientation)) {
+ Slog.i(TAG, "Ignoring requested fixed orientation "
+ + ActivityInfo.screenOrientationToString(requestedOrientation)
+ + " of " + this + " since target sdk 36");
+ }
// Push the new configuration to the requested app in case where it's not pushed, e.g. when
// the request is handled at task level with letterbox.
@@ -8214,9 +8235,7 @@
@ActivityInfo.ScreenOrientation
protected int getOverrideOrientation() {
int candidateOrientation = super.getOverrideOrientation();
- if (candidateOrientation != ActivityInfo.SCREEN_ORIENTATION_LOCKED
- && ActivityInfo.isFixedOrientation(candidateOrientation)
- && isUniversalResizeable()) {
+ if (isRestrictedFixedOrientation(candidateOrientation)) {
candidateOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
}
return mAppCompatController.getOrientationPolicy()
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 198e14a..8ff0818 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -3915,12 +3915,11 @@
}
@Override
- public void setLimitSystemEducationDialogs(
- IBinder appToken, boolean limitSystemEducationDialogs) {
+ public void requestOpenInBrowserEducation(IBinder appToken) {
synchronized (mGlobalLock) {
final ActivityRecord r = ActivityRecord.isInRootTaskLocked(appToken);
if (r != null) {
- r.setLimitSystemEducationDialogs(limitSystemEducationDialogs);
+ r.requestOpenInBrowserEducation();
}
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index 4857b02e..70a8f56 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -344,6 +344,11 @@
private ActivityRecord mTopResumedActivity;
/**
+ * Cached value of the topmost resumed activity that reported to the client.
+ */
+ private ActivityRecord mLastReportedTopResumedActivity;
+
+ /**
* Flag indicating whether we're currently waiting for the previous top activity to handle the
* loss of the state and report back before making new activity top resumed.
*/
@@ -2287,15 +2292,13 @@
* sent to the new top resumed activity.
*/
ActivityRecord updateTopResumedActivityIfNeeded(String reason) {
- if (!readyToResume()) {
- return mTopResumedActivity;
- }
final ActivityRecord prevTopActivity = mTopResumedActivity;
final Task topRootTask = mRootWindowContainer.getTopDisplayFocusedRootTask();
if (topRootTask == null || topRootTask.getTopResumedActivity() == prevTopActivity) {
if (topRootTask == null) {
// There's no focused task and there won't have any resumed activity either.
scheduleTopResumedActivityStateLossIfNeeded();
+ mTopResumedActivity = null;
}
if (mService.isSleepingLocked()) {
// There won't be a next resumed activity. The top process should still be updated
@@ -2339,25 +2342,27 @@
/** Schedule current top resumed activity state loss */
private void scheduleTopResumedActivityStateLossIfNeeded() {
- if (mTopResumedActivity == null) {
+ if (mLastReportedTopResumedActivity == null) {
return;
}
// mTopResumedActivityWaitingForPrev == true at this point would mean that an activity
// before the prevTopActivity one hasn't reported back yet. So server never sent the top
// resumed state change message to prevTopActivity.
- if (!mTopResumedActivityWaitingForPrev
- && mTopResumedActivity.scheduleTopResumedActivityChanged(false /* onTop */)) {
- scheduleTopResumedStateLossTimeout(mTopResumedActivity);
+ if (!mTopResumedActivityWaitingForPrev && readyToResume()
+ && mLastReportedTopResumedActivity.scheduleTopResumedActivityChanged(
+ false /* onTop */)) {
+ scheduleTopResumedStateLossTimeout(mLastReportedTopResumedActivity);
mTopResumedActivityWaitingForPrev = true;
+ mLastReportedTopResumedActivity = null;
}
- mTopResumedActivity = null;
}
/** Schedule top resumed state change if previous top activity already reported back. */
private void scheduleTopResumedActivityStateIfNeeded() {
- if (mTopResumedActivity != null && !mTopResumedActivityWaitingForPrev) {
+ if (mTopResumedActivity != null && !mTopResumedActivityWaitingForPrev && readyToResume()) {
mTopResumedActivity.scheduleTopResumedActivityChanged(true /* onTop */);
+ mLastReportedTopResumedActivity = mTopResumedActivity;
}
}
@@ -2611,6 +2616,10 @@
*/
void endDeferResume() {
mDeferResumeCount--;
+ if (readyToResume() && mLastReportedTopResumedActivity != null
+ && mTopResumedActivity != mLastReportedTopResumedActivity) {
+ scheduleTopResumedActivityStateLossIfNeeded();
+ }
}
/** @return True if resume can be called. */
diff --git a/services/core/java/com/android/server/wm/AppCompatCameraOverrides.java b/services/core/java/com/android/server/wm/AppCompatCameraOverrides.java
index fbf9478..9754595 100644
--- a/services/core/java/com/android/server/wm/AppCompatCameraOverrides.java
+++ b/services/core/java/com/android/server/wm/AppCompatCameraOverrides.java
@@ -16,10 +16,9 @@
package com.android.server.wm;
-import static android.app.CameraCompatTaskInfo.CAMERA_COMPAT_FREEFORM_NONE;
import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_FORCE_ROTATION;
-import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_FREEFORM_WINDOWING_TREATMENT;
import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_REFRESH;
+import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT;
import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE;
import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_ONLY_FOR_CAMERA;
import static android.content.pm.ActivityInfo.OVERRIDE_ORIENTATION_ONLY_FOR_CAMERA;
@@ -33,7 +32,6 @@
import static com.android.server.wm.AppCompatUtils.isChangeEnabled;
import android.annotation.NonNull;
-import android.app.CameraCompatTaskInfo.FreeformCameraCompatMode;
import com.android.server.wm.utils.OptPropFactory;
import com.android.window.flags.Flags;
@@ -165,13 +163,13 @@
*
* <p>The treatment is enabled when the following conditions are met:
* <ul>
- * <li>Property gating the camera compatibility free-form treatment is enabled.
- * <li>Activity isn't opted out by the device manufacturer with override.
+ * <li>Feature flag gating the camera compatibility free-form treatment is enabled.
+ * <li>Activity is opted in by the device manufacturer with override.
* </ul>
*/
boolean shouldApplyFreeformTreatmentForCameraCompat() {
- return Flags.enableCameraCompatForDesktopWindowing() && !isChangeEnabled(mActivityRecord,
- OVERRIDE_CAMERA_COMPAT_DISABLE_FREEFORM_WINDOWING_TREATMENT);
+ return Flags.enableCameraCompatForDesktopWindowing() && isChangeEnabled(mActivityRecord,
+ OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT);
}
boolean isOverrideOrientationOnlyForCameraEnabled() {
@@ -202,22 +200,10 @@
&& !mActivityRecord.shouldCreateAppCompatDisplayInsets();
}
- @FreeformCameraCompatMode
- int getFreeformCameraCompatMode() {
- return mAppCompatCameraOverridesState.mFreeformCameraCompatMode;
- }
-
- void setFreeformCameraCompatMode(@FreeformCameraCompatMode int freeformCameraCompatMode) {
- mAppCompatCameraOverridesState.mFreeformCameraCompatMode = freeformCameraCompatMode;
- }
-
static class AppCompatCameraOverridesState {
// Whether activity "refresh" was requested but not finished in
// ActivityRecord#activityResumedLocked following the camera compat force rotation in
// DisplayRotationCompatPolicy.
private boolean mIsRefreshRequested;
-
- @FreeformCameraCompatMode
- private int mFreeformCameraCompatMode = CAMERA_COMPAT_FREEFORM_NONE;
}
}
diff --git a/services/core/java/com/android/server/wm/CameraCompatFreeformPolicy.java b/services/core/java/com/android/server/wm/CameraCompatFreeformPolicy.java
index 2a0252a..506477f 100644
--- a/services/core/java/com/android/server/wm/CameraCompatFreeformPolicy.java
+++ b/services/core/java/com/android/server/wm/CameraCompatFreeformPolicy.java
@@ -38,7 +38,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.CameraCompatTaskInfo;
-import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.view.DisplayInfo;
import android.view.Surface;
@@ -46,7 +45,6 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.ProtoLog;
import com.android.internal.protolog.WmProtoLogGroups;
-import com.android.window.flags.Flags;
/**
* Policy for camera compatibility freeform treatment.
@@ -124,26 +122,6 @@
return appBoundsChanged || displayRotationChanged;
}
- /**
- * Whether activity is eligible for camera compatibility free-form treatment.
- *
- * <p>The treatment is applied to a fixed-orientation camera activity in free-form windowing
- * mode. The treatment letterboxes or pillarboxes the activity to the expected orientation and
- * provides changes to the camera and display orientation signals to match those expected on a
- * portrait device in that orientation (for example, on a standard phone).
- *
- * <p>The treatment is enabled when the following conditions are met:
- * <ul>
- * <li>Property gating the camera compatibility free-form treatment is enabled.
- * <li>Activity isn't opted out by the device manufacturer with override.
- * </ul>
- */
- @VisibleForTesting
- boolean isCameraCompatForFreeformEnabledForActivity(@NonNull ActivityRecord activity) {
- return Flags.enableCameraCompatForDesktopWindowing() && !activity.info.isChangeEnabled(
- ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_FREEFORM_WINDOWING_TREATMENT);
- }
-
@Override
public void onCameraOpened(@NonNull ActivityRecord cameraActivity,
@NonNull String cameraId) {
@@ -159,16 +137,10 @@
}
cameraActivity.recomputeConfiguration();
- updateCameraCompatMode(cameraActivity);
cameraActivity.getTask().dispatchTaskInfoChangedIfNeeded(/* force= */ true);
cameraActivity.ensureActivityConfiguration(/* ignoreVisibility= */ false);
}
- private void updateCameraCompatMode(@NonNull ActivityRecord cameraActivity) {
- cameraActivity.mAppCompatController.getAppCompatCameraOverrides()
- .setFreeformCameraCompatMode(getCameraCompatMode(cameraActivity));
- }
-
@Override
public boolean onCameraClosed(@NonNull String cameraId) {
// Top activity in the same task as the camera activity, or `null` if the task is
@@ -277,7 +249,8 @@
boolean isTreatmentEnabledForActivity(@NonNull ActivityRecord activity,
boolean checkOrientation) {
int orientation = activity.getRequestedConfigurationOrientation();
- return isCameraCompatForFreeformEnabledForActivity(activity)
+ return activity.mAppCompatController.getAppCompatCameraOverrides()
+ .shouldApplyFreeformTreatmentForCameraCompat()
&& mCameraStateMonitor.isCameraRunningForActivity(activity)
&& (!checkOrientation || orientation != ORIENTATION_UNDEFINED)
&& activity.inFreeformWindowingMode()
diff --git a/services/core/java/com/android/server/wm/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java
index 3b24798..f40d636 100644
--- a/services/core/java/com/android/server/wm/DisplayArea.java
+++ b/services/core/java/com/android/server/wm/DisplayArea.java
@@ -101,8 +101,6 @@
DisplayArea(WindowManagerService wms, Type type, String name, int featureId) {
super(wms);
- // TODO(display-area): move this up to ConfigurationContainer
- setOverrideOrientation(SCREEN_ORIENTATION_UNSET);
mType = type;
mName = name;
mFeatureId = featureId;
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
index 4b2d454..cf145f9 100644
--- a/services/core/java/com/android/server/wm/InsetsStateController.java
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -228,13 +228,11 @@
changed |= provider.updateClientVisibility(caller,
isImeProvider ? statsToken : null);
}
- if (!android.view.inputmethod.Flags.refactorInsetsController()) {
- if (changed) {
- notifyInsetsChanged();
- mDisplayContent.updateSystemGestureExclusion();
+ if (changed) {
+ notifyInsetsChanged();
+ mDisplayContent.updateSystemGestureExclusion();
- mDisplayContent.getDisplayPolicy().updateSystemBarAttributes();
- }
+ mDisplayContent.getDisplayPolicy().updateSystemBarAttributes();
}
}
diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java
index 9de96f14..f0e12fe 100644
--- a/services/core/java/com/android/server/wm/RecentTasks.java
+++ b/services/core/java/com/android/server/wm/RecentTasks.java
@@ -326,9 +326,12 @@
ProtoLog.i(WM_DEBUG_TASKS, "Setting frozen recents task list");
// Always update the reordering time when this is called to ensure that the timeout
- // is reset
+ // is reset. Extend this duration when running in tests.
+ final long timeout = ActivityManager.isRunningInUserTestHarness()
+ ? mFreezeTaskListTimeoutMs * 10
+ : mFreezeTaskListTimeoutMs;
mService.mH.removeCallbacks(mResetFreezeTaskListOnTimeoutRunnable);
- mService.mH.postDelayed(mResetFreezeTaskListOnTimeoutRunnable, mFreezeTaskListTimeoutMs);
+ mService.mH.postDelayed(mResetFreezeTaskListOnTimeoutRunnable, timeout);
}
/**
@@ -2022,22 +2025,9 @@
// Fill in some deprecated values.
rti.id = rti.isRunning ? rti.taskId : INVALID_TASK_ID;
rti.persistentId = rti.taskId;
- rti.lastSnapshotData.set(tr.mLastTaskSnapshotData);
if (!getTasksAllowed) {
Task.trimIneffectiveInfo(tr, rti);
}
-
- // Fill in organized child task info for the task created by organizer.
- if (tr.mCreatedByOrganizer) {
- for (int i = tr.getChildCount() - 1; i >= 0; i--) {
- final Task childTask = tr.getChildAt(i).asTask();
- if (childTask != null && childTask.isOrganized()) {
- final ActivityManager.RecentTaskInfo cti = new ActivityManager.RecentTaskInfo();
- childTask.fillTaskInfo(cti, true /* stripExtras */, tda);
- rti.childrenTaskInfos.add(cti);
- }
- }
- }
return rti;
}
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 6707a27..c89feb4 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -108,6 +108,7 @@
import android.graphics.Region;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManagerInternal;
+import android.hardware.display.DisplayManagerInternal.DisplayBrightnessOverrideRequest;
import android.hardware.power.Mode;
import android.net.Uri;
import android.os.Binder;
@@ -185,8 +186,9 @@
private static final long SLEEP_TRANSITION_WAIT_MILLIS = 1000L;
private Object mLastWindowFreezeSource = null;
- private float mScreenBrightnessOverride = PowerManager.BRIGHTNESS_INVALID_FLOAT;
- private CharSequence mScreenBrightnessOverrideTag;
+ // Per-display WindowManager overrides that are passed on.
+ private final SparseArray<DisplayBrightnessOverrideRequest> mDisplayBrightnessOverrides =
+ new SparseArray<>();
private long mUserActivityTimeout = -1;
private boolean mUpdateRotation = false;
// Only set while traversing the default display based on its content.
@@ -775,8 +777,7 @@
UPDATE_FOCUS_WILL_PLACE_SURFACES, false /*updateInputWindows*/);
}
- mScreenBrightnessOverride = PowerManager.BRIGHTNESS_INVALID_FLOAT;
- mScreenBrightnessOverrideTag = null;
+ mDisplayBrightnessOverrides.clear();
mUserActivityTimeout = -1;
mObscureApplicationContentOnSecondaryDisplays = false;
mSustainedPerformanceModeCurrent = false;
@@ -879,18 +880,10 @@
}
if (!mWmService.mDisplayFrozen) {
- final float brightnessOverride = mScreenBrightnessOverride < PowerManager.BRIGHTNESS_MIN
- || mScreenBrightnessOverride > PowerManager.BRIGHTNESS_MAX
- ? PowerManager.BRIGHTNESS_INVALID_FLOAT : mScreenBrightnessOverride;
- CharSequence overrideTag = null;
- if (brightnessOverride != PowerManager.BRIGHTNESS_INVALID_FLOAT) {
- overrideTag = mScreenBrightnessOverrideTag;
- }
- int brightnessFloatAsIntBits = Float.floatToIntBits(brightnessOverride);
// Post these on a handler such that we don't call into power manager service while
// holding the window manager lock to avoid lock contention with power manager lock.
- mHandler.obtainMessage(SET_SCREEN_BRIGHTNESS_OVERRIDE, brightnessFloatAsIntBits,
- 0, overrideTag).sendToTarget();
+ mHandler.obtainMessage(SET_SCREEN_BRIGHTNESS_OVERRIDE, mDisplayBrightnessOverrides)
+ .sendToTarget();
mHandler.obtainMessage(SET_USER_ACTIVITY_TIMEOUT, mUserActivityTimeout).sendToTarget();
}
@@ -1043,10 +1036,13 @@
}
if (w.isDrawn() || (w.mActivityRecord != null && w.mActivityRecord.firstWindowDrawn
&& w.mActivityRecord.isVisibleRequested())) {
- if (!syswin && w.mAttrs.screenBrightness >= 0
- && Float.isNaN(mScreenBrightnessOverride)) {
- mScreenBrightnessOverride = w.mAttrs.screenBrightness;
- mScreenBrightnessOverrideTag = w.getWindowTag();
+ if (!syswin && w.mAttrs.screenBrightness >= PowerManager.BRIGHTNESS_MIN
+ && w.mAttrs.screenBrightness <= PowerManager.BRIGHTNESS_MAX
+ && !mDisplayBrightnessOverrides.contains(w.getDisplayId())) {
+ var brightnessOverride = new DisplayBrightnessOverrideRequest();
+ brightnessOverride.brightness = w.mAttrs.screenBrightness;
+ brightnessOverride.tag = w.getWindowTag();
+ mDisplayBrightnessOverrides.put(w.getDisplayId(), brightnessOverride);
}
// This function assumes that the contents of the default display are processed first
@@ -1118,8 +1114,10 @@
public void handleMessage(Message msg) {
switch (msg.what) {
case SET_SCREEN_BRIGHTNESS_OVERRIDE:
- mWmService.mPowerManagerInternal.setScreenBrightnessOverrideFromWindowManager(
- Float.intBitsToFloat(msg.arg1), (CharSequence) msg.obj);
+ var brightnessOverrides =
+ (SparseArray<DisplayBrightnessOverrideRequest>) msg.obj;
+ mWmService.mDisplayManagerInternal.setScreenBrightnessOverrideFromWindowManager(
+ brightnessOverrides);
break;
case SET_USER_ACTIVITY_TIMEOUT:
mWmService.mPowerManagerInternal.
@@ -2856,7 +2854,10 @@
void prepareForShutdown() {
for (int i = 0; i < getChildCount(); i++) {
- createSleepToken("shutdown", getChildAt(i).mDisplayId);
+ final int displayId = getChildAt(i).mDisplayId;
+ mWindowManager.mSnapshotController.mTaskSnapshotController
+ .snapshotForShutdown(displayId);
+ createSleepToken("shutdown", displayId);
}
}
diff --git a/services/core/java/com/android/server/wm/SnapshotPersistQueue.java b/services/core/java/com/android/server/wm/SnapshotPersistQueue.java
index 1c8c245..bd8e8f4 100644
--- a/services/core/java/com/android/server/wm/SnapshotPersistQueue.java
+++ b/services/core/java/com/android/server/wm/SnapshotPersistQueue.java
@@ -64,6 +64,7 @@
private boolean mStarted;
private final Object mLock = new Object();
private final UserManagerInternal mUserManagerInternal;
+ private boolean mShutdown;
SnapshotPersistQueue() {
mUserManagerInternal = LocalServices.getService(UserManagerInternal.class);
@@ -101,6 +102,16 @@
}
}
+ /**
+ * Write out everything in the queue because of shutdown.
+ */
+ void shutdown() {
+ synchronized (mLock) {
+ mShutdown = true;
+ mLock.notifyAll();
+ }
+ }
+
@VisibleForTesting
void waitForQueueEmpty() {
while (true) {
@@ -193,7 +204,9 @@
if (isReadyToWrite) {
next.write();
}
- SystemClock.sleep(DELAY_MS);
+ if (!mShutdown) {
+ SystemClock.sleep(DELAY_MS);
+ }
}
synchronized (mLock) {
final boolean writeQueueEmpty = mWriteQueue.isEmpty();
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index dbc3b76c2..9504fc7 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -128,7 +128,6 @@
import android.annotation.UserIdInt;
import android.app.Activity;
import android.app.ActivityManager;
-import android.app.ActivityManager.RecentTaskInfo.PersistedTaskSnapshotData;
import android.app.ActivityManager.TaskDescription;
import android.app.ActivityOptions;
import android.app.ActivityTaskManager;
@@ -256,9 +255,6 @@
private static final String ATTR_MIN_HEIGHT = "min_height";
private static final String ATTR_PERSIST_TASK_VERSION = "persist_task_version";
private static final String ATTR_WINDOW_LAYOUT_AFFINITY = "window_layout_affinity";
- private static final String ATTR_LAST_SNAPSHOT_TASK_SIZE = "last_snapshot_task_size";
- private static final String ATTR_LAST_SNAPSHOT_CONTENT_INSETS = "last_snapshot_content_insets";
- private static final String ATTR_LAST_SNAPSHOT_BUFFER_SIZE = "last_snapshot_buffer_size";
// How long to wait for all background Activities to redraw following a call to
// convertToTranslucent().
@@ -472,10 +468,6 @@
// NOTE: This value needs to be persisted with each task
private TaskDescription mTaskDescription;
- // Information about the last snapshot that should be persisted with the task to allow SystemUI
- // to layout without loading all the task snapshots
- final PersistedTaskSnapshotData mLastTaskSnapshotData;
-
/** @see #setCanAffectSystemUiFlags */
private boolean mCanAffectSystemUiFlags = true;
@@ -635,14 +627,13 @@
ComponentName _realActivity, ComponentName _origActivity, boolean _rootWasReset,
boolean _autoRemoveRecents, int _userId, int _effectiveUid,
String _lastDescription, long lastTimeMoved, boolean neverRelinquishIdentity,
- TaskDescription _lastTaskDescription, PersistedTaskSnapshotData _lastSnapshotData,
- int taskAffiliation, int prevTaskId, int nextTaskId, int callingUid,
- String callingPackage, @Nullable String callingFeatureId, int resizeMode,
- boolean supportsPictureInPicture, boolean _realActivitySuspended,
- boolean userSetupComplete, int minWidth, int minHeight, ActivityInfo info,
- IVoiceInteractionSession _voiceSession, IVoiceInteractor _voiceInteractor,
- boolean _createdByOrganizer, IBinder _launchCookie, boolean _deferTaskAppear,
- boolean _removeWithTaskOrganizer) {
+ TaskDescription _lastTaskDescription, int taskAffiliation, int prevTaskId,
+ int nextTaskId, int callingUid, String callingPackage,
+ @Nullable String callingFeatureId, int resizeMode, boolean supportsPictureInPicture,
+ boolean _realActivitySuspended, boolean userSetupComplete, int minWidth, int minHeight,
+ ActivityInfo info, IVoiceInteractionSession _voiceSession,
+ IVoiceInteractor _voiceInteractor, boolean _createdByOrganizer, IBinder _launchCookie,
+ boolean _deferTaskAppear, boolean _removeWithTaskOrganizer) {
super(atmService, null /* fragmentToken */, _createdByOrganizer, false /* isEmbedded */);
mTaskId = _taskId;
@@ -652,9 +643,6 @@
mTaskDescription = _lastTaskDescription != null
? _lastTaskDescription
: new TaskDescription();
- mLastTaskSnapshotData = _lastSnapshotData != null
- ? _lastSnapshotData
- : new PersistedTaskSnapshotData();
affinityIntent = _affinityIntent;
affinity = _affinity;
rootAffinity = _rootAffinity;
@@ -3111,7 +3099,6 @@
}
void onSnapshotChanged(TaskSnapshot snapshot) {
- mLastTaskSnapshotData.set(snapshot);
mAtmService.getTaskChangeNotificationController().notifyTaskSnapshotChanged(
mTaskId, snapshot);
}
@@ -3423,8 +3410,8 @@
? top.getLastParentBeforePip().mTaskId : INVALID_TASK_ID;
info.shouldDockBigOverlays = top != null && top.shouldDockBigOverlays;
info.mTopActivityLocusId = top != null ? top.getLocusId() : null;
- info.isTopActivityLimitSystemEducationDialogs = top != null
- ? top.mShouldLimitSystemEducationDialogs : false;
+ info.topActivityRequestOpenInBrowserEducationTimestamp = top != null
+ ? top.mRequestOpenInBrowserEducationTimestamp : 0;
final Task parentTask = getParent() != null ? getParent().asTask() : null;
info.parentTaskId = parentTask != null && parentTask.mCreatedByOrganizer
? parentTask.mTaskId
@@ -3523,6 +3510,7 @@
info.capturedLink = null;
info.capturedLinkTimestamp = 0;
+ info.topActivityRequestOpenInBrowserEducationTimestamp = 0;
}
@Nullable PictureInPictureParams getPictureInPictureParams() {
@@ -3989,19 +3977,6 @@
out.attributeInt(null, ATTR_MIN_HEIGHT, mMinHeight);
out.attributeInt(null, ATTR_PERSIST_TASK_VERSION, PERSIST_TASK_VERSION);
- if (mLastTaskSnapshotData.taskSize != null) {
- out.attribute(null, ATTR_LAST_SNAPSHOT_TASK_SIZE,
- mLastTaskSnapshotData.taskSize.flattenToString());
- }
- if (mLastTaskSnapshotData.contentInsets != null) {
- out.attribute(null, ATTR_LAST_SNAPSHOT_CONTENT_INSETS,
- mLastTaskSnapshotData.contentInsets.flattenToString());
- }
- if (mLastTaskSnapshotData.bufferSize != null) {
- out.attribute(null, ATTR_LAST_SNAPSHOT_BUFFER_SIZE,
- mLastTaskSnapshotData.bufferSize.flattenToString());
- }
-
if (affinityIntent != null) {
out.startTag(null, TAG_AFFINITYINTENT);
affinityIntent.saveToXml(out);
@@ -4068,7 +4043,6 @@
int taskId = INVALID_TASK_ID;
final int outerDepth = in.getDepth();
TaskDescription taskDescription = new TaskDescription();
- PersistedTaskSnapshotData lastSnapshotData = new PersistedTaskSnapshotData();
int taskAffiliation = INVALID_TASK_ID;
int prevTaskId = INVALID_TASK_ID;
int nextTaskId = INVALID_TASK_ID;
@@ -4175,15 +4149,6 @@
case ATTR_PERSIST_TASK_VERSION:
persistTaskVersion = Integer.parseInt(attrValue);
break;
- case ATTR_LAST_SNAPSHOT_TASK_SIZE:
- lastSnapshotData.taskSize = Point.unflattenFromString(attrValue);
- break;
- case ATTR_LAST_SNAPSHOT_CONTENT_INSETS:
- lastSnapshotData.contentInsets = Rect.unflattenFromString(attrValue);
- break;
- case ATTR_LAST_SNAPSHOT_BUFFER_SIZE:
- lastSnapshotData.bufferSize = Point.unflattenFromString(attrValue);
- break;
default:
if (!attrName.startsWith(TaskDescription.ATTR_TASKDESCRIPTION_PREFIX)) {
Slog.w(TAG, "Task: Unknown attribute=" + attrName);
@@ -4277,7 +4242,6 @@
.setLastTimeMoved(lastTimeOnTop)
.setNeverRelinquishIdentity(neverRelinquishIdentity)
.setLastTaskDescription(taskDescription)
- .setLastSnapshotData(lastSnapshotData)
.setTaskAffiliation(taskAffiliation)
.setPrevAffiliateTaskId(prevTaskId)
.setNextAffiliateTaskId(nextTaskId)
@@ -6443,7 +6407,6 @@
private long mLastTimeMoved;
private boolean mNeverRelinquishIdentity;
private TaskDescription mLastTaskDescription;
- private PersistedTaskSnapshotData mLastSnapshotData;
private int mTaskAffiliation;
private int mPrevAffiliateTaskId = INVALID_TASK_ID;
private int mNextAffiliateTaskId = INVALID_TASK_ID;
@@ -6671,11 +6634,6 @@
return this;
}
- private Builder setLastSnapshotData(PersistedTaskSnapshotData lastSnapshotData) {
- mLastSnapshotData = lastSnapshotData;
- return this;
- }
-
private Builder setOrigActivity(ComponentName origActivity) {
mOrigActivity = origActivity;
return this;
@@ -6824,7 +6782,7 @@
return new Task(mAtmService, mTaskId, mIntent, mAffinityIntent, mAffinity,
mRootAffinity, mRealActivity, mOrigActivity, mRootWasReset, mAutoRemoveRecents,
mUserId, mEffectiveUid, mLastDescription, mLastTimeMoved,
- mNeverRelinquishIdentity, mLastTaskDescription, mLastSnapshotData,
+ mNeverRelinquishIdentity, mLastTaskDescription,
mTaskAffiliation, mPrevAffiliateTaskId, mNextAffiliateTaskId, mCallingUid,
mCallingPackage, mCallingFeatureId, mResizeMode, mSupportsPictureInPicture,
mRealActivitySuspended, mUserSetupComplete, mMinWidth, mMinHeight,
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index 1f82cdb..9fe3f756 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -307,6 +307,28 @@
}
/**
+ * Record task snapshots before shutdown.
+ */
+ void snapshotForShutdown(int displayId) {
+ if (!com.android.window.flags.Flags.recordTaskSnapshotsBeforeShutdown()) {
+ return;
+ }
+ final DisplayContent displayContent = mService.mRoot.getDisplayContent(displayId);
+ if (displayContent == null) {
+ return;
+ }
+ displayContent.forAllLeafTasks(task -> {
+ if (task.isVisible() && !task.isActivityTypeHome()) {
+ final TaskSnapshot snapshot = captureSnapshot(task);
+ if (snapshot != null) {
+ mPersister.persistSnapshot(task.mTaskId, task.mUserId, snapshot);
+ }
+ }
+ }, true /* traverseTopToBottom */);
+ mPersister.mSnapshotPersistQueue.shutdown();
+ }
+
+ /**
* Called when screen is being turned off.
*/
void screenTurningOff(int displayId, ScreenOffListener listener) {
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index 143d1b7..8562bb2 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -658,8 +658,8 @@
}
// Always allow WindowState to assign layers since it won't affect transition.
return wc.asWindowState() != null || (!isPlaying()
- // Don't assign task while collecting.
- && !(wc.asTask() != null && isCollecting()));
+ // Don't assign task or display area layers while collecting.
+ && !((wc.asTask() != null || wc.asDisplayArea() != null) && isCollecting()));
}
@WindowConfiguration.WindowingMode
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 5f92bb6..2397e03 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -1676,30 +1676,36 @@
* @param orientation the specified orientation. Needs to be one of {@link ScreenOrientation}.
* @param requestingContainer the container which orientation request has changed. Mostly used
* to ensure it gets correct configuration.
+ * @return the resolved override orientation of this window container.
*/
- void setOrientation(@ScreenOrientation int orientation,
+ @ScreenOrientation
+ int setOrientation(@ScreenOrientation int orientation,
@Nullable WindowContainer requestingContainer) {
if (getOverrideOrientation() == orientation) {
- return;
+ return orientation;
}
-
setOverrideOrientation(orientation);
final WindowContainer parent = getParent();
- if (parent != null) {
- if (getConfiguration().orientation != getRequestedConfigurationOrientation()
- // Update configuration directly only if the change won't be dispatched from
- // ancestor. This prevents from computing intermediate configuration when the
- // parent also needs to be updated from the ancestor. E.g. the app requests
- // portrait but the task is still in landscape. While updating from display,
- // the task can be updated to portrait first so the configuration can be
- // computed in a consistent environment.
- && (inMultiWindowMode()
- || !handlesOrientationChangeFromDescendant(orientation))) {
- // Resolve the requested orientation.
- onConfigurationChanged(parent.getConfiguration());
- }
- onDescendantOrientationChanged(requestingContainer);
+ if (parent == null) {
+ return orientation;
}
+ // The derived class can return a result that is different from the given orientation.
+ final int resolvedOrientation = getOverrideOrientation();
+ if (getConfiguration().orientation != getRequestedConfigurationOrientation(
+ false /* forDisplay */, resolvedOrientation)
+ // Update configuration directly only if the change won't be dispatched from
+ // ancestor. This prevents from computing intermediate configuration when the
+ // parent also needs to be updated from the ancestor. E.g. the app requests
+ // portrait but the task is still in landscape. While updating from display,
+ // the task can be updated to portrait first so the configuration can be
+ // computed in a consistent environment.
+ && (inMultiWindowMode()
+ || !handlesOrientationChangeFromDescendant(orientation))) {
+ // Resolve the requested orientation.
+ onConfigurationChanged(parent.getConfiguration());
+ }
+ onDescendantOrientationChanged(requestingContainer);
+ return resolvedOrientation;
}
@ScreenOrientation
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 88b2d22..8268cae 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -1022,12 +1022,12 @@
return;
}
- final boolean disableSecureWindows;
+ boolean disableSecureWindows;
try {
disableSecureWindows = Settings.Secure.getIntForUser(mContext.getContentResolver(),
Settings.Secure.DISABLE_SECURE_WINDOWS, 0) != 0;
} catch (Settings.SettingNotFoundException e) {
- return;
+ disableSecureWindows = false;
}
if (mDisableSecureWindows == disableSecureWindows) {
return;
@@ -10233,6 +10233,17 @@
}
}
+ /**
+ * Resets the spatial ordering of recents for testing purposes.
+ */
+ void resetFreezeRecentTaskListReordering() {
+ if (!checkCallingPermission(permission.MANAGE_ACTIVITY_TASKS,
+ "resetFreezeRecentTaskListReordering()")) {
+ throw new SecurityException("Requires MANAGE_ACTIVITY_TASKS permission");
+ }
+ mAtmService.getRecentTasks().resetFreezeTaskListReorderingOnTimeout();
+ }
+
@Override
public void registerTrustedPresentationListener(IBinder window,
ITrustedPresentationListener listener,
diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
index 21ed8d7..fe2bcc7 100644
--- a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
+++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
@@ -161,6 +161,8 @@
return runReset(pw);
case "disable-blur":
return runSetBlurDisabled(pw);
+ case "reset-freeze-recent-tasks":
+ return runResetFreezeRecentTaskListReordering(pw);
case "set-display-windowing-mode":
return runSetDisplayWindowingMode(pw);
case "get-display-windowing-mode":
@@ -275,6 +277,11 @@
return 0;
}
+ private int runResetFreezeRecentTaskListReordering(PrintWriter pw) throws RemoteException {
+ mInternal.resetFreezeRecentTaskListReordering();
+ return 0;
+ }
+
private void printInitialDisplayDensity(PrintWriter pw , int displayId) {
try {
final int initialDensity = mInterface.getInitialDisplayDensity(displayId);
@@ -1592,6 +1599,8 @@
printLetterboxHelp(pw);
printMultiWindowConfigHelp(pw);
+ pw.println(" reset-freeze-recent-tasks");
+ pw.println(" Resets the spatial ordering of the recent tasks list");
pw.println(" set-display-windowing-mode [-d DISPLAY_ID] [mode_id]");
pw.println(" As mode_id, use " + WINDOWING_MODE_UNDEFINED + " for undefined, "
+ WINDOWING_MODE_FREEFORM + " for freeform, " + WINDOWING_MODE_FULLSCREEN + " for"
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index ead1282..c42aa37 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -814,10 +814,6 @@
mService.mTaskSupervisor.setDeferRootVisibilityUpdate(false /* deferUpdate */);
if (deferResume) {
mService.mTaskSupervisor.endDeferResume();
- // Transient launching the Recents via HIERARCHY_OP_TYPE_PENDING_INTENT directly
- // resume the Recents activity with no TRANSACT_EFFECTS_LIFECYCLE. Explicitly
- // checks if the top resumed activity should be updated after defer-resume ended.
- mService.mTaskSupervisor.updateTopResumedActivityIfNeeded("endWCT");
}
mService.continueWindowLayout();
}
@@ -886,7 +882,8 @@
if (windowingMode > -1) {
if (mService.isInLockTaskMode()
- && WindowConfiguration.inMultiWindowMode(windowingMode)) {
+ && WindowConfiguration.inMultiWindowMode(windowingMode)
+ && !container.isEmbedded()) {
Slog.w(TAG, "Dropping unsupported request to set multi-window windowing mode"
+ " during locked task mode.");
return effects;
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 81af78e..6009848 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -182,7 +182,6 @@
import static com.android.server.wm.WindowStateProto.VIEW_VISIBILITY;
import static com.android.server.wm.WindowStateProto.WINDOW_CONTAINER;
import static com.android.server.wm.WindowStateProto.WINDOW_FRAMES;
-import static com.android.window.flags.Flags.secureWindowState;
import static com.android.window.flags.Flags.surfaceTrustedOverlay;
import android.annotation.CallSuper;
@@ -1187,9 +1186,7 @@
if (surfaceTrustedOverlay() && isWindowTrustedOverlay()) {
getPendingTransaction().setTrustedOverlay(mSurfaceControl, true);
}
- if (secureWindowState()) {
- getPendingTransaction().setSecure(mSurfaceControl, isSecureLocked());
- }
+ getPendingTransaction().setSecure(mSurfaceControl, isSecureLocked());
// All apps should be considered as occluding when computing TrustedPresentation Thresholds.
final boolean canOccludePresentation = !mSession.mCanAddInternalSystemWindow;
getPendingTransaction().setCanOccludePresentation(mSurfaceControl, canOccludePresentation);
@@ -6174,18 +6171,10 @@
void setSecureLocked(boolean isSecure) {
ProtoLog.i(WM_SHOW_TRANSACTIONS, "SURFACE isSecure=%b: %s", isSecure, getName());
- if (secureWindowState()) {
- if (mSurfaceControl == null) {
- return;
- }
- getPendingTransaction().setSecure(mSurfaceControl, isSecure);
- } else {
- if (mWinAnimator.mSurfaceControl == null) {
- return;
- }
- getPendingTransaction().setSecure(mWinAnimator.mSurfaceControl,
- isSecure);
+ if (mSurfaceControl == null) {
+ return;
}
+ getPendingTransaction().setSecure(mSurfaceControl, isSecure);
if (mDisplayContent != null) {
mDisplayContent.refreshImeSecureFlag(getSyncTransaction());
}
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index a934eea..0154d95 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -49,7 +49,6 @@
import static com.android.server.wm.WindowStateAnimatorProto.SURFACE;
import static com.android.server.wm.WindowStateAnimatorProto.SYSTEM_DECOR_RECT;
import static com.android.server.wm.WindowSurfaceControllerProto.SHOWN;
-import static com.android.window.flags.Flags.secureWindowState;
import static com.android.window.flags.Flags.setScPropertiesInClient;
import android.content.Context;
@@ -310,12 +309,6 @@
int flags = SurfaceControl.HIDDEN;
final WindowManager.LayoutParams attrs = w.mAttrs;
- if (!secureWindowState()) {
- if (w.isSecureLocked()) {
- flags |= SurfaceControl.SECURE;
- }
- }
-
if ((mWin.mAttrs.privateFlags & PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY) != 0) {
flags |= SurfaceControl.SKIP_SCREENSHOT;
}
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index e383375..dece612 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -2330,6 +2330,12 @@
im->getInputManager()->getReader().toggleCapsLockState(deviceId);
}
+static void resetLockedModifierState(JNIEnv* env, jobject nativeImplObj) {
+ NativeInputManager* im = getNativeInputManager(env, nativeImplObj);
+
+ im->getInputManager()->getReader().resetLockedModifierState();
+}
+
static void nativeDisplayRemoved(JNIEnv* env, jobject nativeImplObj, jint displayId) {
NativeInputManager* im = getNativeInputManager(env, nativeImplObj);
@@ -3134,6 +3140,7 @@
{"verifyInputEvent", "(Landroid/view/InputEvent;)Landroid/view/VerifiedInputEvent;",
(void*)nativeVerifyInputEvent},
{"toggleCapsLock", "(I)V", (void*)nativeToggleCapsLock},
+ {"resetLockedModifierState", "()V", (void*)resetLockedModifierState},
{"displayRemoved", "(I)V", (void*)nativeDisplayRemoved},
{"setFocusedApplication", "(ILandroid/view/InputApplicationHandle;)V",
(void*)nativeSetFocusedApplication},
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
index b982098..76d16e1 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
@@ -371,6 +371,9 @@
}
ActiveAdmin(int userId, boolean permissionBased) {
+ if (Flags.activeAdminCleanup()) {
+ throw new UnsupportedOperationException("permission based admin no longer supported");
+ }
if (permissionBased == false) {
throw new IllegalArgumentException("Can only pass true for permissionBased admin");
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java
index 395ea91..c937e10 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java
@@ -21,6 +21,7 @@
import android.annotation.UserIdInt;
import android.app.admin.DeviceAdminInfo;
import android.app.admin.DevicePolicyManager;
+import android.app.admin.flags.Flags;
import android.content.ComponentName;
import android.os.FileUtils;
import android.os.PersistableBundle;
@@ -124,17 +125,18 @@
final ArrayList<ActiveAdmin> mAdminList = new ArrayList<>();
final ArrayList<ComponentName> mRemovingAdmins = new ArrayList<>();
- // Some DevicePolicyManager APIs can be called by (1) a DPC or (2) an app with permissions that
- // isn't a DPC. For the latter, the caller won't have to provide a ComponentName and won't be
- // mapped to an ActiveAdmin. This permission-based admin should be used to persist policies
- // set by the permission-based caller. This admin should not be added to mAdminMap or mAdminList
- // since a lot of methods in DPMS assume the ActiveAdmins here have a valid ComponentName.
- // Instead, use variants of DPMS active admin getters to include the permission-based admin.
+ /**
+ * @deprecated Do not use. Policies set by permission holders must go into DevicePolicyEngine.
+ */
+ @Deprecated
ActiveAdmin mPermissionBasedAdmin;
// Create or get the permission-based admin. The permission-based admin will not have a
// DeviceAdminInfo or ComponentName.
ActiveAdmin createOrGetPermissionBasedAdmin(int userId) {
+ if (Flags.activeAdminCleanup()) {
+ throw new UnsupportedOperationException("permission based admin no longer supported");
+ }
if (mPermissionBasedAdmin == null) {
mPermissionBasedAdmin = new ActiveAdmin(userId, /* permissionBased= */ true);
}
@@ -147,7 +149,7 @@
// This is the list of component allowed to start lock task mode.
List<String> mLockTaskPackages = new ArrayList<>();
- /** @deprecated moved to {@link ActiveAdmin#protectedPackages}. */
+ /** @deprecated moved to DevicePolicyEngine. */
@Deprecated
@Nullable
List<String> mUserControlDisabledPackages;
@@ -280,7 +282,7 @@
}
}
- if (policyData.mPermissionBasedAdmin != null) {
+ if (!Flags.activeAdminCleanup() && policyData.mPermissionBasedAdmin != null) {
out.startTag(null, "permission-based-admin");
policyData.mPermissionBasedAdmin.writeToXml(out);
out.endTag(null, "permission-based-admin");
@@ -521,7 +523,8 @@
} catch (RuntimeException e) {
Slogf.w(TAG, e, "Failed loading admin %s", name);
}
- } else if ("permission-based-admin".equals(tag)) {
+ } else if (!Flags.activeAdminCleanup() && "permission-based-admin".equals(tag)) {
+
ActiveAdmin ap = new ActiveAdmin(policy.mUserId, /* permissionBased= */ true);
ap.readFromXml(parser, /* overwritePolicies= */ false);
policy.mPermissionBasedAdmin = ap;
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
index c19c58e..cb333f0 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
@@ -1236,6 +1236,8 @@
}
}
for (EnforcingAdmin admin : admins) {
+ // No need to make changes to system enforcing admins.
+ if (admin.isSystemAuthority()) break;
if (updatedPackage == null || updatedPackage.equals(admin.getPackageName())) {
if (!isPackageInstalled(admin.getPackageName(), userId)) {
Slogf.i(TAG, String.format(
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 8ad8786..6292cbf 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -376,6 +376,7 @@
import android.app.compat.CompatChanges;
import android.app.role.OnRoleHoldersChangedListener;
import android.app.role.RoleManager;
+import android.app.supervision.SupervisionManagerInternal;
import android.app.trust.TrustManager;
import android.app.usage.UsageStatsManagerInternal;
import android.compat.annotation.ChangeId;
@@ -504,6 +505,7 @@
import com.android.internal.app.LocalePicker;
import com.android.internal.infra.AndroidFuture;
import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto;
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.internal.net.NetworkUtilsInternal;
import com.android.internal.notification.SystemNotificationChannels;
@@ -715,24 +717,24 @@
SECURE_SETTINGS_DEVICEOWNER_ALLOWLIST.add(Settings.Secure.LOCATION_MODE);
GLOBAL_SETTINGS_ALLOWLIST = new ArraySet<>();
- GLOBAL_SETTINGS_ALLOWLIST.add(Settings.Global.ADB_ENABLED);
- GLOBAL_SETTINGS_ALLOWLIST.add(Settings.Global.ADB_WIFI_ENABLED);
- GLOBAL_SETTINGS_ALLOWLIST.add(Settings.Global.AUTO_TIME);
- GLOBAL_SETTINGS_ALLOWLIST.add(Settings.Global.AUTO_TIME_ZONE);
- GLOBAL_SETTINGS_ALLOWLIST.add(Settings.Global.DATA_ROAMING);
- GLOBAL_SETTINGS_ALLOWLIST.add(Settings.Global.USB_MASS_STORAGE_ENABLED);
- GLOBAL_SETTINGS_ALLOWLIST.add(Settings.Global.WIFI_SLEEP_POLICY);
- GLOBAL_SETTINGS_ALLOWLIST.add(Settings.Global.STAY_ON_WHILE_PLUGGED_IN);
- GLOBAL_SETTINGS_ALLOWLIST.add(Settings.Global.WIFI_DEVICE_OWNER_CONFIGS_LOCKDOWN);
- GLOBAL_SETTINGS_ALLOWLIST.add(Settings.Global.PRIVATE_DNS_MODE);
- GLOBAL_SETTINGS_ALLOWLIST.add(Settings.Global.PRIVATE_DNS_SPECIFIER);
+ GLOBAL_SETTINGS_ALLOWLIST.add(Global.ADB_ENABLED);
+ GLOBAL_SETTINGS_ALLOWLIST.add(Global.ADB_WIFI_ENABLED);
+ GLOBAL_SETTINGS_ALLOWLIST.add(Global.AUTO_TIME);
+ GLOBAL_SETTINGS_ALLOWLIST.add(Global.AUTO_TIME_ZONE);
+ GLOBAL_SETTINGS_ALLOWLIST.add(Global.DATA_ROAMING);
+ GLOBAL_SETTINGS_ALLOWLIST.add(Global.USB_MASS_STORAGE_ENABLED);
+ GLOBAL_SETTINGS_ALLOWLIST.add(Global.WIFI_SLEEP_POLICY);
+ GLOBAL_SETTINGS_ALLOWLIST.add(Global.STAY_ON_WHILE_PLUGGED_IN);
+ GLOBAL_SETTINGS_ALLOWLIST.add(Global.WIFI_DEVICE_OWNER_CONFIGS_LOCKDOWN);
+ GLOBAL_SETTINGS_ALLOWLIST.add(Global.PRIVATE_DNS_MODE);
+ GLOBAL_SETTINGS_ALLOWLIST.add(PRIVATE_DNS_SPECIFIER);
GLOBAL_SETTINGS_DEPRECATED = new ArraySet<>();
- GLOBAL_SETTINGS_DEPRECATED.add(Settings.Global.BLUETOOTH_ON);
- GLOBAL_SETTINGS_DEPRECATED.add(Settings.Global.DEVELOPMENT_SETTINGS_ENABLED);
- GLOBAL_SETTINGS_DEPRECATED.add(Settings.Global.MODE_RINGER);
- GLOBAL_SETTINGS_DEPRECATED.add(Settings.Global.NETWORK_PREFERENCE);
- GLOBAL_SETTINGS_DEPRECATED.add(Settings.Global.WIFI_ON);
+ GLOBAL_SETTINGS_DEPRECATED.add(Global.BLUETOOTH_ON);
+ GLOBAL_SETTINGS_DEPRECATED.add(Global.DEVELOPMENT_SETTINGS_ENABLED);
+ GLOBAL_SETTINGS_DEPRECATED.add(Global.MODE_RINGER);
+ GLOBAL_SETTINGS_DEPRECATED.add(Global.NETWORK_PREFERENCE);
+ GLOBAL_SETTINGS_DEPRECATED.add(Global.WIFI_ON);
SYSTEM_SETTINGS_ALLOWLIST = new ArraySet<>();
SYSTEM_SETTINGS_ALLOWLIST.add(Settings.System.SCREEN_BRIGHTNESS);
@@ -775,7 +777,7 @@
/**
* Strings logged with {@link
- * com.android.internal.logging.nano.MetricsProto.MetricsEvent#PROVISIONING_ENTRY_POINT_ADB},
+ * MetricsProto.MetricsEvent#PROVISIONING_ENTRY_POINT_ADB},
* {@link DevicePolicyEnums#PROVISIONING_ENTRY_POINT_ADB},
* {@link DevicePolicyEnums#SET_NETWORK_LOGGING_ENABLED} and
* {@link DevicePolicyEnums#RETRIEVE_NETWORK_LOGS}.
@@ -786,11 +788,11 @@
/**
* For admin apps targeting R+, throw when the app sets password requirement
* that is not taken into account at given quality. For example when quality is set
- * to {@link android.app.admin.DevicePolicyManager#PASSWORD_QUALITY_UNSPECIFIED}, it doesn't
+ * to {@link DevicePolicyManager#PASSWORD_QUALITY_UNSPECIFIED}, it doesn't
* make sense to require certain password length. If the intent is to require a password of
* certain length having at least NUMERIC quality, the admin should first call
- * {@link android.app.admin.DevicePolicyManager#setPasswordQuality} and only then call
- * {@link android.app.admin.DevicePolicyManager#setPasswordMinimumLength}.
+ * {@link DevicePolicyManager#setPasswordQuality} and only then call
+ * {@link DevicePolicyManager#setPasswordMinimumLength}.
*
* <p>Conversely when an admin app targeting R+ lowers password quality, those
* requirements that stop making sense are reset to default values.
@@ -801,9 +803,9 @@
/**
* Admin apps targeting Android R+ may not use
- * {@link android.app.admin.DevicePolicyManager#setSecureSetting} to change the deprecated
- * {@link android.provider.Settings.Secure#LOCATION_MODE} setting. Instead they should use
- * {@link android.app.admin.DevicePolicyManager#setLocationEnabled}.
+ * {@link DevicePolicyManager#setSecureSetting} to change the deprecated
+ * {@link Settings.Secure#LOCATION_MODE} setting. Instead they should use
+ * {@link DevicePolicyManager#setLocationEnabled}.
*/
@ChangeId
@EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q)
@@ -849,7 +851,7 @@
private @interface CopyAccountStatus {}
/**
- * Mapping of {@link android.app.admin.DevicePolicyManager.ApplicationExemptionConstants} to
+ * Mapping of {@link DevicePolicyManager.ApplicationExemptionConstants} to
* corresponding app-ops.
*/
private static final Map<Integer, String> APPLICATION_EXEMPTION_CONSTANTS_TO_APP_OPS =
@@ -881,11 +883,11 @@
/**
* Admin apps targeting Android S+ may not use
- * {@link android.app.admin.DevicePolicyManager#setPasswordQuality} to set password quality
+ * {@link DevicePolicyManager#setPasswordQuality} to set password quality
* on the {@code DevicePolicyManager} instance obtained by calling
- * {@link android.app.admin.DevicePolicyManager#getParentProfileInstance}.
+ * {@link DevicePolicyManager#getParentProfileInstance}.
* Instead, they should use
- * {@link android.app.admin.DevicePolicyManager#setRequiredPasswordComplexity} to set
+ * {@link DevicePolicyManager#setRequiredPasswordComplexity} to set
* coarse-grained password requirements device-wide.
*/
@ChangeId
@@ -894,7 +896,7 @@
/**
* For Admin Apps targeting U+
- * If {@link android.security.IKeyChainService#setGrant} is called with an alias with no
+ * If {@link IKeyChainService#setGrant} is called with an alias with no
* existing key, throw IllegalArgumentException.
*/
@ChangeId
@@ -926,6 +928,8 @@
final UsageStatsManagerInternal mUsageStatsManagerInternal;
final TelephonyManager mTelephonyManager;
final RoleManager mRoleManager;
+ final SupervisionManagerInternal mSupervisionManagerInternal;
+
private final LockPatternUtils mLockPatternUtils;
private final LockSettingsInternal mLockSettingsInternal;
private final DeviceAdminServiceController mDeviceAdminServiceController;
@@ -1474,8 +1478,8 @@
if (packageName == null || packageName.equals(adminPackage)) {
if (mIPackageManager.getPackageInfo(adminPackage, 0, userHandle) == null
|| mIPackageManager.getReceiverInfo(aa.info.getComponent(),
- PackageManager.MATCH_DIRECT_BOOT_AWARE
- | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
+ MATCH_DIRECT_BOOT_AWARE
+ | MATCH_DIRECT_BOOT_UNAWARE,
userHandle) == null) {
Slogf.e(LOG_TAG, String.format(
"Admin package %s not found for user %d, removing active admin",
@@ -1693,7 +1697,7 @@
return getPackageManager().hasSystemFeature(PackageManager.FEATURE_DEVICE_ADMIN);
}
- Context createContextAsUser(UserHandle user) throws PackageManager.NameNotFoundException {
+ Context createContextAsUser(UserHandle user) throws NameNotFoundException {
final String packageName = mContext.getPackageName();
return mContext.createPackageContextAsUser(packageName, 0, user);
}
@@ -2005,25 +2009,25 @@
}
void settingsGlobalPutStringForUser(String name, String value, int userHandle) {
- Settings.Global.putStringForUser(mContext.getContentResolver(),
+ Global.putStringForUser(mContext.getContentResolver(),
name, value, userHandle);
}
int settingsGlobalGetInt(String name, int def) {
- return Settings.Global.getInt(mContext.getContentResolver(), name, def);
+ return Global.getInt(mContext.getContentResolver(), name, def);
}
@Nullable
String settingsGlobalGetString(String name) {
- return Settings.Global.getString(mContext.getContentResolver(), name);
+ return Global.getString(mContext.getContentResolver(), name);
}
void settingsGlobalPutInt(String name, int value) {
- Settings.Global.putInt(mContext.getContentResolver(), name, value);
+ Global.putInt(mContext.getContentResolver(), name, value);
}
void settingsGlobalPutString(String name, String value) {
- Settings.Global.putString(mContext.getContentResolver(), name, value);
+ Global.putString(mContext.getContentResolver(), name, value);
}
void settingsSystemPutStringForUser(String name, String value, int userId) {
@@ -2082,6 +2086,11 @@
boolean isAdminInstalledCaCertAutoApproved() {
return false;
}
+
+ @Nullable
+ SupervisionManagerInternal getSupervisionManager() {
+ return LocalServices.getService(SupervisionManagerInternal.class);
+ }
}
/**
@@ -2113,6 +2122,11 @@
mIPermissionManager = Objects.requireNonNull(injector.getIPermissionManager());
mTelephonyManager = Objects.requireNonNull(injector.getTelephonyManager());
mRoleManager = Objects.requireNonNull(injector.getRoleManager());
+ if (Flags.secondaryLockscreenApiEnabled()) {
+ mSupervisionManagerInternal = injector.getSupervisionManager();
+ } else {
+ mSupervisionManagerInternal = null;
+ }
mLocalService = new LocalService();
mLockPatternUtils = injector.newLockPatternUtils();
@@ -2234,7 +2248,6 @@
return Collections.unmodifiableSet(packageNames);
}
-
private @Nullable String getDefaultRoleHolderPackageName(int resId) {
String packageNameAndSignature = mContext.getString(resId);
@@ -3194,8 +3207,8 @@
return mIPackageManager.getReceiverInfo(adminName,
GET_META_DATA
| PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS
- | PackageManager.MATCH_DIRECT_BOOT_AWARE
- | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userHandle);
+ | MATCH_DIRECT_BOOT_AWARE
+ | MATCH_DIRECT_BOOT_UNAWARE, userHandle);
} catch (RemoteException e) {
// shouldn't happen.
Slogf.wtf(LOG_TAG, "Error getting receiver info", e);
@@ -3206,9 +3219,9 @@
throw new IllegalArgumentException("Unknown admin: " + adminName);
}
- if (!permission.BIND_DEVICE_ADMIN.equals(ai.permission)) {
+ if (!BIND_DEVICE_ADMIN.equals(ai.permission)) {
final String message = "DeviceAdminReceiver " + adminName + " must be protected with "
- + permission.BIND_DEVICE_ADMIN;
+ + BIND_DEVICE_ADMIN;
Slogf.w(LOG_TAG, message);
if (throwForMissingPermission &&
ai.applicationInfo.targetSdkVersion > Build.VERSION_CODES.M) {
@@ -3966,7 +3979,8 @@
final int N = admins.size();
for (int i = 0; i < N; i++) {
ActiveAdmin admin = admins.get(i);
- if ((admin.isPermissionBased || admin.info.usesPolicy(DeviceAdminInfo.USES_POLICY_EXPIRE_PASSWORD))
+ if (((!Flags.activeAdminCleanup() && admin.isPermissionBased)
+ || admin.info.usesPolicy(DeviceAdminInfo.USES_POLICY_EXPIRE_PASSWORD))
&& admin.passwordExpirationTimeout > 0L
&& now >= admin.passwordExpirationDate - EXPIRATION_GRACE_PERIOD_MS
&& admin.passwordExpirationDate > 0L) {
@@ -4399,8 +4413,8 @@
final ApplicationInfo ai;
try {
ai = mInjector.getIPackageManager().getApplicationInfo(packageName,
- (PackageManager.MATCH_DIRECT_BOOT_AWARE
- | PackageManager.MATCH_DIRECT_BOOT_UNAWARE), userHandle);
+ (MATCH_DIRECT_BOOT_AWARE
+ | MATCH_DIRECT_BOOT_UNAWARE), userHandle);
} catch (RemoteException e) {
throw new IllegalStateException(e);
}
@@ -5563,13 +5577,25 @@
caller.getUserId());
Preconditions.checkArgument(!calledOnParent || isProfileOwner(caller));
- ActiveAdmin activeAdmin = admin.getActiveAdmin();
+ final ActiveAdmin activeAdmin;
+ if (Flags.activeAdminCleanup()) {
+ if (admin.hasAuthority(EnforcingAdmin.DPC_AUTHORITY)) {
+ synchronized (getLockObject()) {
+ activeAdmin = getActiveAdminUncheckedLocked(
+ admin.getComponentName(), admin.getUserId());
+ }
+ } else {
+ activeAdmin = null;
+ }
+ } else {
+ activeAdmin = admin.getActiveAdmin();
+ }
// We require the caller to explicitly clear any password quality requirements set
// on the parent DPM instance, to avoid the case where password requirements are
// specified in the form of quality on the parent but complexity on the profile
// itself.
- if (!calledOnParent) {
+ if (activeAdmin != null && !calledOnParent) {
final boolean hasQualityRequirementsOnParent = activeAdmin.hasParentActiveAdmin()
&& activeAdmin.getParentActiveAdmin().mPasswordPolicy.quality
!= PASSWORD_QUALITY_UNSPECIFIED;
@@ -5593,20 +5619,22 @@
}
mInjector.binderWithCleanCallingIdentity(() -> {
- // Reset the password policy.
- if (calledOnParent) {
- activeAdmin.getParentActiveAdmin().mPasswordPolicy = new PasswordPolicy();
- } else {
- activeAdmin.mPasswordPolicy = new PasswordPolicy();
+ if (activeAdmin != null) {
+ // Reset the password policy.
+ if (calledOnParent) {
+ activeAdmin.getParentActiveAdmin().mPasswordPolicy = new PasswordPolicy();
+ } else {
+ activeAdmin.mPasswordPolicy = new PasswordPolicy();
+ }
+ updatePasswordQualityCacheForUserGroup(caller.getUserId());
}
+
synchronized (getLockObject()) {
updatePasswordValidityCheckpointLocked(caller.getUserId(), calledOnParent);
}
- updatePasswordQualityCacheForUserGroup(caller.getUserId());
saveSettingsLocked(caller.getUserId());
});
-
DevicePolicyEventLogger
.createEvent(DevicePolicyEnums.SET_PASSWORD_COMPLEXITY)
.setAdmin(caller.getPackageName())
@@ -5965,7 +5993,7 @@
Preconditions.checkCallAuthorization(admin != null,
"Unauthorized caller cannot call resetPassword.");
if (getTargetSdk(admin.info.getPackageName(),
- userHandle) <= android.os.Build.VERSION_CODES.M) {
+ userHandle) <= Build.VERSION_CODES.M) {
Slogf.e(LOG_TAG, "Device admin can no longer call resetPassword()");
return false;
}
@@ -6115,7 +6143,7 @@
if (policy.mLastMaximumTimeToLock != Long.MAX_VALUE) {
// Make sure KEEP_SCREEN_ON is disabled, since that
// would allow bypassing of the maximum time to lock.
- mInjector.settingsGlobalPutInt(Settings.Global.STAY_ON_WHILE_PLUGGED_IN, 0);
+ mInjector.settingsGlobalPutInt(Global.STAY_ON_WHILE_PLUGGED_IN, 0);
}
getPowerManagerInternal().setMaximumScreenOffTimeoutFromDeviceAdmin(parentId, timeMs);
});
@@ -6287,28 +6315,33 @@
final int callingUserId = caller.getUserId();
ComponentName adminComponent = null;
synchronized (getLockObject()) {
- ActiveAdmin admin;
// Make sure the caller has any active admin with the right policy or
// the required permission.
if (Flags.lockNowCoexistence()) {
- admin = enforcePermissionsAndGetEnforcingAdmin(
+ EnforcingAdmin enforcingAdmin = enforcePermissionsAndGetEnforcingAdmin(
/* admin= */ null,
/* permissions= */ new String[]{MANAGE_DEVICE_POLICY_LOCK, LOCK_DEVICE},
/* deviceAdminPolicy= */ USES_POLICY_FORCE_LOCK,
caller.getPackageName(),
getAffectedUser(parent)
- ).getActiveAdmin();
+ );
+ if (Flags.activeAdminCleanup()) {
+ adminComponent = enforcingAdmin.getComponentName();
+ } else {
+ ActiveAdmin admin = enforcingAdmin.getActiveAdmin();
+ adminComponent = admin == null ? null : admin.info.getComponent();
+ }
} else {
- admin = getActiveAdminOrCheckPermissionForCallerLocked(
+ ActiveAdmin admin = getActiveAdminOrCheckPermissionForCallerLocked(
null,
- DeviceAdminInfo.USES_POLICY_FORCE_LOCK,
+ USES_POLICY_FORCE_LOCK,
parent,
LOCK_DEVICE);
+ adminComponent = admin == null ? null : admin.info.getComponent();
}
checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_LOCK_NOW);
final long ident = mInjector.binderClearCallingIdentity();
try {
- adminComponent = admin == null ? null : admin.info.getComponent();
if (adminComponent != null) {
// For Profile Owners only, callers with only permission not allowed.
if ((flags & DevicePolicyManager.FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY) != 0) {
@@ -7443,7 +7476,7 @@
* privileged APIs.
* <p>
* This is done by checking that the calling package is authorized to perform the app operation
- * {@link android.app.AppOpsManager#OP_MANAGE_CREDENTIALS}.
+ * {@link AppOpsManager#OP_MANAGE_CREDENTIALS}.
*
* @param caller the calling identity
* @return {@code true} if the calling process is the credential management app.
@@ -7453,7 +7486,7 @@
AppOpsManager appOpsManager = mInjector.getAppOpsManager();
if (appOpsManager == null) return false;
return appOpsManager.noteOpNoThrow(AppOpsManager.OP_MANAGE_CREDENTIALS, caller.getUid(),
- caller.getPackageName(), null, null) == AppOpsManager.MODE_ALLOWED;
+ caller.getPackageName(), null, null) == MODE_ALLOWED;
});
}
@@ -7764,7 +7797,7 @@
public void wipeDataWithReason(String callerPackageName, int flags,
@NonNull String wipeReasonForUser, boolean calledOnParentInstance,
boolean factoryReset) {
- if (!mHasFeature && !hasCallingOrSelfPermission(permission.MASTER_CLEAR)) {
+ if (!mHasFeature && !hasCallingOrSelfPermission(MASTER_CLEAR)) {
return;
}
CallerIdentity caller = getCallerIdentity(callerPackageName);
@@ -7777,7 +7810,6 @@
USES_POLICY_WIPE_DATA,
caller.getPackageName(),
factoryReset ? UserHandle.USER_ALL : getAffectedUser(calledOnParentInstance));
- ActiveAdmin admin = enforcingAdmin.getActiveAdmin();
checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_WIPE_DATA);
@@ -7786,10 +7818,20 @@
calledByProfileOwnerOnOrgOwnedDevice, calledOnParentInstance);
}
- int userId = admin != null ? admin.getUserHandle().getIdentifier()
- : caller.getUserId();
- Slogf.i(LOG_TAG, "wipeDataWithReason(%s): admin=%s, user=%d", wipeReasonForUser, admin,
- userId);
+ int userId;
+ ActiveAdmin admin = null;
+ if (Flags.activeAdminCleanup()) {
+ userId = enforcingAdmin.getUserId();
+ Slogf.i(LOG_TAG, "wipeDataWithReason(%s): admin=%s, user=%d", wipeReasonForUser,
+ enforcingAdmin, userId);
+ } else {
+ admin = enforcingAdmin.getActiveAdmin();
+ userId = admin != null ? admin.getUserHandle().getIdentifier()
+ : caller.getUserId();
+ Slogf.i(LOG_TAG, "wipeDataWithReason(%s): admin=%s, user=%d", wipeReasonForUser, admin,
+ userId);
+ }
+
if (calledByProfileOwnerOnOrgOwnedDevice) {
// When wipeData is called on the parent instance, it implies wiping the entire device.
if (calledOnParentInstance) {
@@ -7810,25 +7852,36 @@
final String adminName;
final ComponentName adminComp;
- if (admin != null) {
- if (admin.isPermissionBased) {
- adminComp = null;
- adminName = caller.getPackageName();
- event.setAdmin(adminName);
- } else {
- adminComp = admin.info.getComponent();
- adminName = adminComp.flattenToShortString();
- event.setAdmin(adminComp);
- }
+ if (Flags.activeAdminCleanup()) {
+ adminComp = enforcingAdmin.getComponentName();
+ adminName = adminComp != null
+ ? adminComp.flattenToShortString()
+ : enforcingAdmin.getPackageName();
+ event.setAdmin(enforcingAdmin.getPackageName());
+ // Not including any HSUM handling here because the "else" branch in the "flag off"
+ // case below is unreachable under normal circumstances and for permission-based
+ // callers admin won't be null.
} else {
- adminComp = null;
- adminName = mInjector.getPackageManager().getPackagesForUid(caller.getUid())[0];
- Slogf.i(LOG_TAG, "Logging wipeData() event admin as " + adminName);
- event.setAdmin(adminName);
- if (mInjector.userManagerIsHeadlessSystemUserMode()) {
- // On headless system user mode, the call is meant to factory reset the whole
- // device, otherwise the caller could simply remove the current user.
- userId = UserHandle.USER_SYSTEM;
+ if (admin != null) {
+ if (admin.isPermissionBased) {
+ adminComp = null;
+ adminName = caller.getPackageName();
+ event.setAdmin(adminName);
+ } else {
+ adminComp = admin.info.getComponent();
+ adminName = adminComp.flattenToShortString();
+ event.setAdmin(adminComp);
+ }
+ } else {
+ adminComp = null;
+ adminName = mInjector.getPackageManager().getPackagesForUid(caller.getUid())[0];
+ Slogf.i(LOG_TAG, "Logging wipeData() event admin as " + adminName);
+ event.setAdmin(adminName);
+ if (mInjector.userManagerIsHeadlessSystemUserMode()) {
+ // On headless system user mode, the call is meant to factory reset the whole
+ // device, otherwise the caller could simply remove the current user.
+ userId = UserHandle.USER_SYSTEM;
+ }
}
}
event.write();
@@ -8142,7 +8195,7 @@
synchronized (getLockObject()) {
if (who == null) {
Preconditions.checkCallAuthorization(frpManagementAgentUid == caller.getUid()
- || hasCallingPermission(permission.MASTER_CLEAR)
+ || hasCallingPermission(MASTER_CLEAR)
|| hasCallingPermission(MANAGE_DEVICE_POLICY_FACTORY_RESET),
"Must be called by the FRP management agent on device");
admin = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked();
@@ -8316,7 +8369,8 @@
List<ActiveAdmin> admins = getActiveAdminsForLockscreenPoliciesLocked(userHandle);
for (int i = 0; i < admins.size(); i++) {
ActiveAdmin admin = admins.get(i);
- if (admin.isPermissionBased || admin.info.usesPolicy(DeviceAdminInfo.USES_POLICY_EXPIRE_PASSWORD)) {
+ if ((!Flags.activeAdminCleanup() && admin.isPermissionBased)
+ || admin.info.usesPolicy(DeviceAdminInfo.USES_POLICY_EXPIRE_PASSWORD)) {
affectedUserIds.add(admin.getUserHandle().getIdentifier());
long timeout = admin.passwordExpirationTimeout;
admin.passwordExpirationDate =
@@ -8410,7 +8464,7 @@
*/
private int getUserIdToWipeForFailedPasswords(ActiveAdmin admin) {
final int userId = admin.getUserHandle().getIdentifier();
- if (admin.isPermissionBased) {
+ if (!Flags.activeAdminCleanup() && admin.isPermissionBased) {
return userId;
}
final ComponentName component = admin.info.getComponent();
@@ -8628,9 +8682,9 @@
Slogf.e(LOG_TAG, "Invalid proxy properties, ignoring: " + proxyProperties.toString());
return;
}
- mInjector.settingsGlobalPutString(Settings.Global.GLOBAL_HTTP_PROXY_HOST, data[0]);
- mInjector.settingsGlobalPutInt(Settings.Global.GLOBAL_HTTP_PROXY_PORT, proxyPort);
- mInjector.settingsGlobalPutString(Settings.Global.GLOBAL_HTTP_PROXY_EXCLUSION_LIST,
+ mInjector.settingsGlobalPutString(Global.GLOBAL_HTTP_PROXY_HOST, data[0]);
+ mInjector.settingsGlobalPutInt(Global.GLOBAL_HTTP_PROXY_PORT, proxyPort);
+ mInjector.settingsGlobalPutString(Global.GLOBAL_HTTP_PROXY_EXCLUSION_LIST,
exclusionList);
}
@@ -8751,7 +8805,7 @@
}
final int rawStatus = getEncryptionStatus();
- if ((rawStatus == DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE_PER_USER) && legacyApp) {
+ if ((rawStatus == ENCRYPTION_STATUS_ACTIVE_PER_USER) && legacyApp) {
return DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE;
}
return rawStatus;
@@ -8775,7 +8829,7 @@
*/
private int getEncryptionStatus() {
if (mInjector.storageManagerIsFileBasedEncryptionEnabled()) {
- return DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE_PER_USER;
+ return ENCRYPTION_STATUS_ACTIVE_PER_USER;
} else {
return DevicePolicyManager.ENCRYPTION_STATUS_UNSUPPORTED;
}
@@ -8970,7 +9024,7 @@
// Turn AUTO_TIME on in settings if it is required
if (required) {
mInjector.binderWithCleanCallingIdentity(
- () -> mInjector.settingsGlobalPutInt(Settings.Global.AUTO_TIME,
+ () -> mInjector.settingsGlobalPutInt(Global.AUTO_TIME,
1 /* AUTO_TIME on */));
}
DevicePolicyEventLogger
@@ -10402,7 +10456,7 @@
policy.mDelegationMap.clear();
policy.mStatusBarDisabled = false;
policy.mSecondaryLockscreenEnabled = false;
- policy.mUserProvisioningState = DevicePolicyManager.STATE_USER_UNMANAGED;
+ policy.mUserProvisioningState = STATE_USER_UNMANAGED;
policy.mAffiliationIds.clear();
resetAffiliationCacheLocked();
policy.mLockTaskPackages.clear();
@@ -10437,7 +10491,7 @@
@Override
public int getUserProvisioningState(int userHandle) {
if (!mHasFeature) {
- return DevicePolicyManager.STATE_USER_UNMANAGED;
+ return STATE_USER_UNMANAGED;
}
final CallerIdentity caller = getCallerIdentity();
Preconditions.checkCallAuthorization(canManageUsers(caller)
@@ -10492,7 +10546,7 @@
// ADB shell can only move directly from un-managed to finalized as part of
// directly setting profile-owner or device-owner.
if (getUserProvisioningState(userId)
- != DevicePolicyManager.STATE_USER_UNMANAGED
+ != STATE_USER_UNMANAGED
|| newState != STATE_USER_SETUP_FINALIZED) {
throw new IllegalStateException("Not allowed to change provisioning state "
+ "unless current provisioning state is unmanaged, and new state"
@@ -10530,9 +10584,9 @@
}
// Valid transitions for normal use-cases.
switch (currentState) {
- case DevicePolicyManager.STATE_USER_UNMANAGED:
+ case STATE_USER_UNMANAGED:
// Can move to any state from unmanaged (except itself as an edge case)..
- if (newState != DevicePolicyManager.STATE_USER_UNMANAGED) {
+ if (newState != STATE_USER_UNMANAGED) {
return;
}
break;
@@ -10556,7 +10610,7 @@
break;
case DevicePolicyManager.STATE_USER_PROFILE_FINALIZED:
// Should only move to an unmanaged state after removing the work profile.
- if (newState == DevicePolicyManager.STATE_USER_UNMANAGED) {
+ if (newState == STATE_USER_UNMANAGED) {
return;
}
break;
@@ -10928,7 +10982,7 @@
UserHandle userHandle = UserHandle.of(userId);
userContext = mContext.createPackageContextAsUser(packageName, /* flags= */ 0,
userHandle);
- } catch (PackageManager.NameNotFoundException nnfe) {
+ } catch (NameNotFoundException nnfe) {
Slogf.w(LOG_TAG, nnfe, "%s is not installed for user %d", packageName, userId);
return null;
}
@@ -11148,20 +11202,20 @@
}
private boolean canQueryAdminPolicy(CallerIdentity caller) {
- return hasCallingOrSelfPermission(permission.QUERY_ADMIN_POLICY);
+ return hasCallingOrSelfPermission(QUERY_ADMIN_POLICY);
}
private boolean hasPermission(String permission, int pid, int uid) {
- return mContext.checkPermission(permission, pid, uid) == PackageManager.PERMISSION_GRANTED;
+ return mContext.checkPermission(permission, pid, uid) == PERMISSION_GRANTED;
}
private boolean hasCallingPermission(String permission) {
- return mContext.checkCallingPermission(permission) == PackageManager.PERMISSION_GRANTED;
+ return mContext.checkCallingPermission(permission) == PERMISSION_GRANTED;
}
private boolean hasCallingOrSelfPermission(String permission) {
return mContext.checkCallingOrSelfPermission(permission)
- == PackageManager.PERMISSION_GRANTED;
+ == PERMISSION_GRANTED;
}
private boolean hasPermissionForPreflight(CallerIdentity caller, String permission) {
@@ -11467,7 +11521,7 @@
private String getEncryptionStatusName(int encryptionStatus) {
switch (encryptionStatus) {
- case DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE_PER_USER:
+ case ENCRYPTION_STATUS_ACTIVE_PER_USER:
return "per-user";
case DevicePolicyManager.ENCRYPTION_STATUS_UNSUPPORTED:
return "unsupported";
@@ -12549,7 +12603,7 @@
if ((flags & DevicePolicyManager.SKIP_SETUP_WIZARD) != 0) {
Settings.Secure.putIntForUser(mContext.getContentResolver(),
- Settings.Secure.USER_SETUP_COMPLETE, 1, userHandle);
+ USER_SETUP_COMPLETE, 1, userHandle);
}
sendProvisioningCompletedBroadcast(
@@ -13362,10 +13416,13 @@
Objects.requireNonNull(systemEntity);
CallerIdentity caller = getCallerIdentity();
- if (caller.getUid() != Process.SYSTEM_UID) {
+ if (!isSystemUid(caller)) {
throw new SecurityException("Only system services can call setUserRestrictionForUser"
+ " on a target user: " + targetUser);
}
+ if (!UserRestrictionsUtils.isValidRestriction(key)) {
+ throw new IllegalArgumentException("Invalid restriction key: " + key);
+ }
if (VERBOSE_LOG) {
Slogf.v(LOG_TAG, "Creating SystemEnforcingAdmin %s for calling package %s",
systemEntity, caller.getPackageName());
@@ -13498,6 +13555,31 @@
logUserRestrictionCall(key, /* enabled= */ true, /* parent= */ false, caller,
UserHandle.USER_ALL);
}
+
+ @Override
+ public void setUserRestrictionGloballyFromSystem(@NonNull String systemEntity, String key,
+ boolean enabled) {
+ Objects.requireNonNull(systemEntity);
+
+ CallerIdentity caller = getCallerIdentity();
+ if (!isSystemUid(caller)) {
+ throw new SecurityException("Only system services can call"
+ + " setUserRestrictionGloballyFromSystem");
+ }
+ if (!UserRestrictionsUtils.isValidRestriction(key)) {
+ throw new IllegalArgumentException("Invalid restriction key: " + key);
+ }
+ if (VERBOSE_LOG) {
+ Slogf.v(LOG_TAG, "Creating SystemEnforcingAdmin %s for calling package %s",
+ systemEntity, caller.getPackageName());
+ }
+ EnforcingAdmin admin = EnforcingAdmin.createSystemEnforcingAdmin(systemEntity);
+
+ setGlobalUserRestrictionInternal(admin, key, enabled);
+
+ logUserRestrictionCall(key, enabled, /* parent= */ false, caller, UserHandle.USER_ALL);
+ }
+
private void setLocalUserRestrictionInternal(
EnforcingAdmin admin, String key, boolean enabled, int userId) {
PolicyDefinition<Boolean> policyDefinition =
@@ -13515,6 +13597,7 @@
userId);
}
}
+
private void setGlobalUserRestrictionInternal(
EnforcingAdmin admin, String key, boolean enabled) {
PolicyDefinition<Boolean> policyDefinition =
@@ -13936,8 +14019,8 @@
List<ResolveInfo> activitiesToEnable = mIPackageManager
.queryIntentActivities(intent,
intent.resolveTypeIfNeeded(mContext.getContentResolver()),
- PackageManager.MATCH_DIRECT_BOOT_AWARE
- | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
+ MATCH_DIRECT_BOOT_AWARE
+ | MATCH_DIRECT_BOOT_UNAWARE,
parentUserId)
.getList();
@@ -14556,34 +14639,76 @@
}
}
+ private boolean hasActiveSupervisionTestAdminLocked(@UserIdInt int userId) {
+ ensureLocked();
+ if (mConstants.USE_TEST_ADMIN_AS_SUPERVISION_COMPONENT) {
+ final DevicePolicyData policy = getUserData(userId);
+ for (ActiveAdmin admin : policy.mAdminMap.values()) {
+ if (admin != null && admin.testOnlyAdmin) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
@Override
public void setSecondaryLockscreenEnabled(ComponentName who, boolean enabled,
PersistableBundle options) {
- Objects.requireNonNull(who, "ComponentName is null");
-
- // Check can set secondary lockscreen enabled
- final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(
- isDefaultDeviceOwner(caller) || isProfileOwner(caller));
- Preconditions.checkCallAuthorization(!isManagedProfile(caller.getUserId()),
- "User %d is not allowed to call setSecondaryLockscreenEnabled",
+ if (Flags.secondaryLockscreenApiEnabled()) {
+ final CallerIdentity caller = getCallerIdentity();
+ final boolean isRoleHolder = isCallerSystemSupervisionRoleHolder(caller);
+ synchronized (getLockObject()) {
+ // TODO(b/378102594): Remove access for test admins.
+ final boolean isTestAdmin = hasActiveSupervisionTestAdminLocked(caller.getUserId());
+ Preconditions.checkCallAuthorization(isRoleHolder || isTestAdmin,
+ "Caller (%d) is not the SYSTEM_SUPERVISION role holder",
caller.getUserId());
+ }
- synchronized (getLockObject()) {
- // Allow testOnly admins to bypass supervision config requirement.
- Preconditions.checkCallAuthorization(isAdminTestOnlyLocked(who, caller.getUserId())
- || isSupervisionComponentLocked(caller.getComponentName()), "Admin %s is not "
- + "the default supervision component", caller.getComponentName());
- DevicePolicyData policy = getUserData(caller.getUserId());
- policy.mSecondaryLockscreenEnabled = enabled;
- saveSettingsLocked(caller.getUserId());
+ if (mSupervisionManagerInternal != null) {
+ mSupervisionManagerInternal.setSupervisionLockscreenEnabledForUser(
+ caller.getUserId(), enabled, options);
+ } else {
+ synchronized (getLockObject()) {
+ DevicePolicyData policy = getUserData(caller.getUserId());
+ policy.mSecondaryLockscreenEnabled = enabled;
+ saveSettingsLocked(caller.getUserId());
+ }
+ }
+ } else {
+ Objects.requireNonNull(who, "ComponentName is null");
+
+ // Check can set secondary lockscreen enabled
+ final CallerIdentity caller = getCallerIdentity(who);
+ Preconditions.checkCallAuthorization(
+ isDefaultDeviceOwner(caller) || isProfileOwner(caller));
+ Preconditions.checkCallAuthorization(!isManagedProfile(caller.getUserId()),
+ "User %d is not allowed to call setSecondaryLockscreenEnabled",
+ caller.getUserId());
+
+ synchronized (getLockObject()) {
+ // Allow testOnly admins to bypass supervision config requirement.
+ Preconditions.checkCallAuthorization(isAdminTestOnlyLocked(who, caller.getUserId())
+ || isSupervisionComponentLocked(caller.getComponentName()),
+ "Admin %s is not the default supervision component",
+ caller.getComponentName());
+ DevicePolicyData policy = getUserData(caller.getUserId());
+ policy.mSecondaryLockscreenEnabled = enabled;
+ saveSettingsLocked(caller.getUserId());
+ }
}
}
@Override
public boolean isSecondaryLockscreenEnabled(@NonNull UserHandle userHandle) {
- synchronized (getLockObject()) {
- return getUserData(userHandle.getIdentifier()).mSecondaryLockscreenEnabled;
+ if (Flags.secondaryLockscreenApiEnabled() && mSupervisionManagerInternal != null) {
+ return mSupervisionManagerInternal.isSupervisionLockscreenEnabledForUser(
+ userHandle.getIdentifier());
+ } else {
+ synchronized (getLockObject()) {
+ return getUserData(userHandle.getIdentifier()).mSecondaryLockscreenEnabled;
+ }
}
}
@@ -14782,7 +14907,7 @@
if (policy == null) {
// We default on the power button menu, in order to be consistent with pre-P
// behaviour.
- return DevicePolicyManager.LOCK_TASK_FEATURE_GLOBAL_ACTIONS;
+ return LOCK_TASK_FEATURE_GLOBAL_ACTIONS;
}
return policy.getFlags();
}
@@ -14911,7 +15036,7 @@
"Permission denial: device owners cannot update %1$s", setting));
}
- if (Settings.Global.STAY_ON_WHILE_PLUGGED_IN.equals(setting)) {
+ if (Global.STAY_ON_WHILE_PLUGGED_IN.equals(setting)) {
// ignore if it contradicts an existing policy
long timeMs = getMaximumTimeToLock(
who, mInjector.userHandleGetCallingUserId(), /* parent */ false);
@@ -15416,7 +15541,7 @@
final int N = users.size();
for (int i = 0; i < N; i++) {
int userHandle = users.get(i).id;
- if (mInjector.settingsSecureGetIntForUser(Settings.Secure.USER_SETUP_COMPLETE, 0,
+ if (mInjector.settingsSecureGetIntForUser(USER_SETUP_COMPLETE, 0,
userHandle) != 0) {
DevicePolicyData policy = getUserData(userHandle);
if (!policy.mUserSetupComplete) {
@@ -15444,7 +15569,7 @@
private class SetupContentObserver extends ContentObserver {
private final Uri mUserSetupComplete = Settings.Secure.getUriFor(
- Settings.Secure.USER_SETUP_COMPLETE);
+ USER_SETUP_COMPLETE);
private final Uri mPaired = Settings.Secure.getUriFor(Settings.Secure.DEVICE_PAIRED);
private final Uri mDefaultImeChanged = Settings.Secure.getUriFor(
Settings.Secure.DEFAULT_INPUT_METHOD);
@@ -15492,7 +15617,7 @@
private class DevicePolicyConstantsObserver extends ContentObserver {
final Uri mConstantsUri =
- Settings.Global.getUriFor(Settings.Global.DEVICE_POLICY_CONSTANTS);
+ Global.getUriFor(Global.DEVICE_POLICY_CONSTANTS);
DevicePolicyConstantsObserver(Handler handler) {
super(handler);
@@ -15785,9 +15910,9 @@
final int uid = Objects.requireNonNull(
mInjector.getPackageManager().getApplicationInfoAsUser(
Objects.requireNonNull(packageName), /* flags= */ 0, userId)).uid;
- return PackageManager.PERMISSION_GRANTED
+ return PERMISSION_GRANTED
== ActivityManager.checkComponentPermission(
- android.Manifest.permission.MODIFY_QUIET_MODE, uid, /* owningUid= */
+ permission.MODIFY_QUIET_MODE, uid, /* owningUid= */
-1, /* exported= */ true);
} catch (NameNotFoundException ex) {
Slogf.w(LOG_TAG, "Cannot find the package %s to check for permissions.",
@@ -15924,7 +16049,7 @@
private @Mode int findInteractAcrossProfilesResetMode(String packageName) {
return getDefaultCrossProfilePackages().contains(packageName)
- ? AppOpsManager.MODE_ALLOWED
+ ? MODE_ALLOWED
: AppOpsManager.opToDefaultMode(AppOpsManager.OP_INTERACT_ACROSS_PROFILES);
}
@@ -16243,7 +16368,8 @@
if (admin.mPasswordPolicy.quality < minPasswordQuality) {
return false;
}
- return admin.isPermissionBased || admin.info.usesPolicy(DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD);
+ return (!Flags.activeAdminCleanup() && admin.isPermissionBased)
+ || admin.info.usesPolicy(DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD);
}
@Override
@@ -16649,13 +16775,13 @@
synchronized (getLockObject()) {
long ident = mInjector.binderClearCallingIdentity();
boolean isPostQAdmin = getTargetSdk(caller.getPackageName(), caller.getUserId())
- >= android.os.Build.VERSION_CODES.Q;
+ >= Build.VERSION_CODES.Q;
try {
if (!isPostQAdmin) {
// Legacy admins assume that they cannot control pre-M apps
if (getTargetSdk(packageName, caller.getUserId())
- < android.os.Build.VERSION_CODES.M) {
+ < Build.VERSION_CODES.M) {
callback.sendResult(null);
return;
}
@@ -16666,7 +16792,7 @@
}
if (grantState == PERMISSION_GRANT_STATE_GRANTED
|| grantState == DevicePolicyManager.PERMISSION_GRANT_STATE_DENIED
- || grantState == DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT) {
+ || grantState == PERMISSION_GRANT_STATE_DEFAULT) {
AdminPermissionControlParams permissionParams =
new AdminPermissionControlParams(packageName, permission,
grantState,
@@ -16701,26 +16827,26 @@
private static final List<String> SENSOR_PERMISSIONS = new ArrayList<>();
{
- SENSOR_PERMISSIONS.add(Manifest.permission.ACCESS_FINE_LOCATION);
- SENSOR_PERMISSIONS.add(Manifest.permission.ACCESS_BACKGROUND_LOCATION);
- SENSOR_PERMISSIONS.add(Manifest.permission.ACCESS_COARSE_LOCATION);
- SENSOR_PERMISSIONS.add(Manifest.permission.CAMERA);
- SENSOR_PERMISSIONS.add(Manifest.permission.RECORD_AUDIO);
- SENSOR_PERMISSIONS.add(Manifest.permission.ACTIVITY_RECOGNITION);
- SENSOR_PERMISSIONS.add(Manifest.permission.BODY_SENSORS);
- SENSOR_PERMISSIONS.add(Manifest.permission.BACKGROUND_CAMERA);
- SENSOR_PERMISSIONS.add(Manifest.permission.RECORD_BACKGROUND_AUDIO);
- SENSOR_PERMISSIONS.add(Manifest.permission.BODY_SENSORS_BACKGROUND);
+ SENSOR_PERMISSIONS.add(permission.ACCESS_FINE_LOCATION);
+ SENSOR_PERMISSIONS.add(permission.ACCESS_BACKGROUND_LOCATION);
+ SENSOR_PERMISSIONS.add(permission.ACCESS_COARSE_LOCATION);
+ SENSOR_PERMISSIONS.add(permission.CAMERA);
+ SENSOR_PERMISSIONS.add(permission.RECORD_AUDIO);
+ SENSOR_PERMISSIONS.add(permission.ACTIVITY_RECOGNITION);
+ SENSOR_PERMISSIONS.add(permission.BODY_SENSORS);
+ SENSOR_PERMISSIONS.add(permission.BACKGROUND_CAMERA);
+ SENSOR_PERMISSIONS.add(permission.RECORD_BACKGROUND_AUDIO);
+ SENSOR_PERMISSIONS.add(permission.BODY_SENSORS_BACKGROUND);
}
private boolean canGrantPermission(CallerIdentity caller, String permission,
String targetPackageName) {
boolean isPostQAdmin = getTargetSdk(caller.getPackageName(), caller.getUserId())
- >= android.os.Build.VERSION_CODES.Q;
+ >= Build.VERSION_CODES.Q;
if (!isPostQAdmin) {
// Legacy admins assume that they cannot control pre-M apps
if (getTargetSdk(targetPackageName, caller.getUserId())
- < android.os.Build.VERSION_CODES.M) {
+ < Build.VERSION_CODES.M) {
return false;
}
}
@@ -16767,7 +16893,7 @@
throws RemoteException {
int granted;
if (getTargetSdk(caller.getPackageName(), caller.getUserId())
- < android.os.Build.VERSION_CODES.Q) {
+ < Build.VERSION_CODES.Q) {
// The per-Q behavior was to not check the app-ops state.
granted = mIPackageManager.checkPermission(permission, packageName, userId);
} else {
@@ -16776,11 +16902,11 @@
if (packageState == null) {
Slog.w(LOG_TAG, "Can't get permission state for missing package "
+ packageName);
- return DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT;
+ return PERMISSION_GRANT_STATE_DEFAULT;
} else if (!packageState.getUserStateOrDefault(userId).isInstalled()) {
Slog.w(LOG_TAG, "Can't get permission state for uninstalled package "
+ packageName);
- return DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT;
+ return PERMISSION_GRANT_STATE_DEFAULT;
} else {
if (PermissionChecker.checkPermissionForPreflight(mContext, permission,
PermissionChecker.PID_UNKNOWN,
@@ -16788,7 +16914,7 @@
!= PermissionChecker.PERMISSION_GRANTED) {
granted = PackageManager.PERMISSION_DENIED;
} else {
- granted = PackageManager.PERMISSION_GRANTED;
+ granted = PERMISSION_GRANTED;
}
}
@@ -16799,11 +16925,11 @@
if ((permFlags & PackageManager.FLAG_PERMISSION_POLICY_FIXED)
!= PackageManager.FLAG_PERMISSION_POLICY_FIXED) {
// Not controlled by policy
- return DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT;
+ return PERMISSION_GRANT_STATE_DEFAULT;
} else {
// Policy controlled so return result based on permission grant state
- return granted == PackageManager.PERMISSION_GRANTED
- ? DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED
+ return granted == PERMISSION_GRANTED
+ ? PERMISSION_GRANT_STATE_GRANTED
: DevicePolicyManager.PERMISSION_GRANT_STATE_DENIED;
}
}
@@ -16923,9 +17049,9 @@
}
if (action != null) {
switch (action) {
- case DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE:
+ case ACTION_PROVISION_MANAGED_PROFILE:
return checkManagedProfileProvisioningPreCondition(packageName, userId);
- case DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE:
+ case ACTION_PROVISION_MANAGED_DEVICE:
case DevicePolicyManager.ACTION_PROVISION_FINANCED_DEVICE:
return checkDeviceOwnerProvisioningPreCondition(componentName, userId);
}
@@ -18272,7 +18398,7 @@
hasCallingOrSelfPermission(MANAGE_PROFILE_AND_DEVICE_OWNERS));
boolean isUserCompleted = mInjector.settingsSecureGetIntForUser(
- Settings.Secure.USER_SETUP_COMPLETE, 0, userId) != 0;
+ USER_SETUP_COMPLETE, 0, userId) != 0;
DevicePolicyData policy = getUserData(userId);
policy.mUserSetupComplete = isUserCompleted;
mStateCache.setDeviceProvisioned(isUserCompleted);
@@ -19119,6 +19245,14 @@
}
}
+ private boolean isAnyResetPasswordTokenActiveForUser(int userId) {
+ return mDevicePolicyEngine
+ .getLocalPoliciesSetByAdmins(PolicyDefinition.RESET_PASSWORD_TOKEN, userId)
+ .values()
+ .stream()
+ .anyMatch((p) -> isResetPasswordTokenActiveForUserLocked(p.getValue(), userId));
+ }
+
private boolean isResetPasswordTokenActiveForUserLocked(
long passwordTokenHandle, int userHandle) {
if (passwordTokenHandle != 0) {
@@ -19895,7 +20029,7 @@
}
private boolean isDeviceAB() {
- return "true".equalsIgnoreCase(android.os.SystemProperties
+ return "true".equalsIgnoreCase(SystemProperties
.get(AB_DEVICE_KEY, ""));
}
@@ -20162,7 +20296,7 @@
return mOwners.hasDeviceOwner()
&& mInjector.getIActivityManager().getLockTaskModeState()
== ActivityManager.LOCK_TASK_MODE_LOCKED
- && !isLockTaskFeatureEnabled(DevicePolicyManager.LOCK_TASK_FEATURE_SYSTEM_INFO)
+ && !isLockTaskFeatureEnabled(LOCK_TASK_FEATURE_SYSTEM_INFO)
&& !deviceHasKeyguard()
&& !inEphemeralUserSession();
}
@@ -20173,7 +20307,7 @@
int lockTaskFeatures = policy == null
// We default on the power button menu, in order to be consistent with pre-P
// behaviour.
- ? DevicePolicyManager.LOCK_TASK_FEATURE_GLOBAL_ACTIONS
+ ? LOCK_TASK_FEATURE_GLOBAL_ACTIONS
: policy.getFlags();
return (lockTaskFeatures & lockTaskFeature) == lockTaskFeature;
}
@@ -20919,7 +21053,7 @@
private boolean canHandleCheckPolicyComplianceIntent(CallerIdentity caller) {
mInjector.binderWithCleanCallingIdentity(() -> {
- final Intent intent = new Intent(DevicePolicyManager.ACTION_CHECK_POLICY_COMPLIANCE);
+ final Intent intent = new Intent(ACTION_CHECK_POLICY_COMPLIANCE);
intent.setPackage(caller.getPackageName());
final List<ResolveInfo> handlers =
mInjector.getPackageManager().queryIntentActivitiesAsUser(intent, /* flags= */
@@ -20974,6 +21108,9 @@
Preconditions.checkCallAuthorization(isSystemUid(getCallerIdentity()),
String.format(NOT_SYSTEM_CALLER_MSG,
"call canProfileOwnerResetPasswordWhenLocked"));
+ if (Flags.resetPasswordWithTokenCoexistence()) {
+ return isAnyResetPasswordTokenActiveForUser(userId);
+ }
synchronized (getLockObject()) {
final ActiveAdmin poAdmin = getProfileOwnerAdminLocked(userId);
DevicePolicyData policy = getUserData(userId);
@@ -21125,6 +21262,17 @@
Preconditions.checkCallAuthorization(
hasCallingOrSelfPermission(MANAGE_PROFILE_AND_DEVICE_OWNERS));
+ if (Flags.splitCreateManagedProfileEnabled()) {
+ return mInjector.binderWithCleanCallingIdentity(() -> {
+ UserHandle managedProfileUser =
+ createManagedProfileInternal(provisioningParams, caller);
+ maybeMigrateAccount(managedProfileUser.getIdentifier(), caller.getUserId(),
+ provisioningParams.getAccountToMigrate(),
+ provisioningParams.isKeepingAccountOnMigration(), callerPackage);
+ finalizeCreateManagedProfileInternal(provisioningParams, managedProfileUser);
+ return managedProfileUser;
+ });
+ }
provisioningParams.logParams(callerPackage);
UserInfo userInfo = null;
@@ -21218,6 +21366,130 @@
}
@Override
+ public UserHandle createManagedProfile(
+ @NonNull ManagedProfileProvisioningParams provisioningParams,
+ @NonNull String callerPackage) {
+ Objects.requireNonNull(provisioningParams, "provisioningParams is null");
+ Objects.requireNonNull(callerPackage, "callerPackage is null");
+ Objects.requireNonNull(provisioningParams.getProfileAdminComponentName(), "admin is null");
+ Preconditions.checkCallAuthorization(
+ hasCallingOrSelfPermission(MANAGE_PROFILE_AND_DEVICE_OWNERS));
+ CallerIdentity caller = getCallerIdentity(callerPackage);
+
+ return mInjector.binderWithCleanCallingIdentity(() ->
+ createManagedProfileInternal(provisioningParams, caller));
+ }
+
+ private UserHandle createManagedProfileInternal(
+ @NonNull ManagedProfileProvisioningParams provisioningParams,
+ @NonNull CallerIdentity caller) {
+ provisioningParams.logParams(caller.getPackageName());
+ final ComponentName admin = provisioningParams.getProfileAdminComponentName();
+ final int callingUserId = caller.getUserId();
+ UserInfo userInfo = null;
+ try {
+ final int result = checkProvisioningPreconditionSkipPermission(
+ ACTION_PROVISION_MANAGED_PROFILE, admin, callingUserId);
+ if (result != STATUS_OK) {
+ throw new ServiceSpecificException(
+ ERROR_PRE_CONDITION_FAILED,
+ "Provisioning preconditions failed with result: " + result);
+ }
+
+ final long startTime = SystemClock.elapsedRealtime();
+
+ onCreateAndProvisionManagedProfileStarted(provisioningParams);
+
+ userInfo = createProfileForUser(provisioningParams, callingUserId);
+ if (userInfo == null) {
+ throw new ServiceSpecificException(
+ ERROR_PROFILE_CREATION_FAILED,
+ "Error creating profile, createProfileForUserEvenWhenDisallowed "
+ + "returned null.");
+ }
+ resetInteractAcrossProfilesAppOps(caller.getUserId());
+ logEventDuration(
+ DevicePolicyEnums.PLATFORM_PROVISIONING_CREATE_PROFILE_MS,
+ startTime,
+ caller.getPackageName());
+
+ maybeInstallDevicePolicyManagementRoleHolderInUser(userInfo.id);
+ installExistingAdminPackage(userInfo.id, admin.getPackageName());
+
+ if (!enableAdminAndSetProfileOwner(userInfo.id, caller.getUserId(), admin)) {
+ throw new ServiceSpecificException(
+ ERROR_SETTING_PROFILE_OWNER_FAILED,
+ "Error setting profile owner.");
+ }
+ setUserSetupComplete(userInfo.id);
+ startProfileForSetup(userInfo.id, caller.getPackageName());
+
+ if (provisioningParams.isOrganizationOwnedProvisioning()) {
+ synchronized (getLockObject()) {
+ setProfileOwnerOnOrganizationOwnedDeviceUncheckedLocked(admin, userInfo.id,
+ true);
+ }
+ }
+ return userInfo.getUserHandle();
+ } catch (Exception e) {
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.PLATFORM_PROVISIONING_ERROR)
+ .setStrings(caller.getPackageName())
+ .write();
+ // In case of any errors during provisioning, remove the newly created profile.
+ if (userInfo != null) {
+ mUserManager.removeUserEvenWhenDisallowed(userInfo.id);
+ }
+ throw e;
+ }
+ }
+
+ private UserInfo createProfileForUser(ManagedProfileProvisioningParams params, int userId) {
+ final Set<String> nonRequiredApps = params.isLeaveAllSystemAppsEnabled()
+ ? Collections.emptySet()
+ : mOverlayPackagesProvider.getNonRequiredApps(params.getProfileAdminComponentName(),
+ userId, ACTION_PROVISION_MANAGED_PROFILE);
+ if (nonRequiredApps.isEmpty()) {
+ Slogf.i(LOG_TAG, "No disallowed packages for the managed profile.");
+ } else {
+ for (String packageName : nonRequiredApps) {
+ Slogf.i(LOG_TAG, "Disallowed package [" + packageName + "]");
+ }
+ }
+ return mUserManager.createProfileForUserEvenWhenDisallowed(
+ params.getProfileName(),
+ UserManager.USER_TYPE_PROFILE_MANAGED,
+ UserInfo.FLAG_DISABLED,
+ userId,
+ nonRequiredApps.toArray(new String[nonRequiredApps.size()]));
+ }
+
+ @Override
+ public void finalizeCreateManagedProfile(
+ @NonNull ManagedProfileProvisioningParams provisioningParams,
+ @NonNull UserHandle managedProfileUser) {
+ Objects.requireNonNull(provisioningParams, "provisioningParams is null");
+ Objects.requireNonNull(managedProfileUser, "managedProfileUser is null");
+ Preconditions.checkCallAuthorization(
+ hasCallingOrSelfPermission(MANAGE_PROFILE_AND_DEVICE_OWNERS));
+
+ mInjector.binderWithCleanCallingIdentity(() -> {
+ finalizeCreateManagedProfileInternal(provisioningParams, managedProfileUser);
+ });
+ }
+
+ private void finalizeCreateManagedProfileInternal(
+ @NonNull ManagedProfileProvisioningParams provisioningParams,
+ @NonNull UserHandle managedProfileUser
+ ) {
+ onCreateAndProvisionManagedProfileCompleted(provisioningParams);
+ sendProvisioningCompletedBroadcast(
+ managedProfileUser.getIdentifier(),
+ ACTION_PROVISION_MANAGED_PROFILE,
+ provisioningParams.isLeaveAllSystemAppsEnabled());
+ }
+
+ @Override
public void finalizeWorkProfileProvisioning(UserHandle managedProfileUser,
Account migratedAccount) {
Preconditions.checkCallAuthorization(
@@ -21387,7 +21659,7 @@
private void pregrantDefaultInteractAcrossProfilesAppOps(@UserIdInt int userId) {
final String op =
- AppOpsManager.permissionToOp(Manifest.permission.INTERACT_ACROSS_PROFILES);
+ AppOpsManager.permissionToOp(permission.INTERACT_ACROSS_PROFILES);
for (String packageName : getConfigurableDefaultCrossProfilePackages(userId)) {
if (!appOpIsDefaultOrAllowed(userId, op, packageName)) {
continue;
@@ -21590,7 +21862,8 @@
Slogf.i(LOG_TAG, "Account removed from the primary user.");
} else {
// TODO(174768447): Revisit start activity logic.
- final Intent removeIntent = result.getParcelable(AccountManager.KEY_INTENT, android.content.Intent.class);
+ final Intent removeIntent =
+ result.getParcelable(AccountManager.KEY_INTENT, Intent.class);
removeIntent.addFlags(FLAG_ACTIVITY_NEW_TASK);
if (removeIntent != null) {
Slogf.i(LOG_TAG, "Starting activity to remove account");
@@ -21886,7 +22159,7 @@
}
synchronized (getLockObject()) {
mInjector.settingsGlobalPutStringForUser(
- Settings.Global.DEVICE_DEMO_MODE, Integer.toString(/* value= */ 1), userId);
+ Global.DEVICE_DEMO_MODE, Integer.toString(/* value= */ 1), userId);
}
setUserProvisioningState(STATE_USER_SETUP_FINALIZED, userId);
@@ -22149,7 +22422,7 @@
@Override
public boolean isDevicePotentiallyStolen(String callerPackageName) {
final CallerIdentity caller = getCallerIdentity(callerPackageName);
- if (!android.app.admin.flags.Flags.deviceTheftImplEnabled()) {
+ if (!Flags.deviceTheftImplEnabled()) {
return false;
}
enforcePermission(QUERY_DEVICE_STOLEN_STATE, caller.getPackageName(),
@@ -22185,7 +22458,7 @@
@Override
public void setDrawables(@NonNull List<DevicePolicyDrawableResource> drawables) {
Preconditions.checkCallAuthorization(hasCallingOrSelfPermission(
- android.Manifest.permission.UPDATE_DEVICE_MANAGEMENT_RESOURCES));
+ permission.UPDATE_DEVICE_MANAGEMENT_RESOURCES));
Objects.requireNonNull(drawables, "drawables must be provided.");
@@ -22201,7 +22474,7 @@
@Override
public void resetDrawables(@NonNull List<String> drawableIds) {
Preconditions.checkCallAuthorization(hasCallingOrSelfPermission(
- android.Manifest.permission.UPDATE_DEVICE_MANAGEMENT_RESOURCES));
+ permission.UPDATE_DEVICE_MANAGEMENT_RESOURCES));
Objects.requireNonNull(drawableIds, "drawableIds must be provided.");
@@ -22227,7 +22500,7 @@
@Override
public void setStrings(@NonNull List<DevicePolicyStringResource> strings) {
Preconditions.checkCallAuthorization(hasCallingOrSelfPermission(
- android.Manifest.permission.UPDATE_DEVICE_MANAGEMENT_RESOURCES));
+ permission.UPDATE_DEVICE_MANAGEMENT_RESOURCES));
Objects.requireNonNull(strings, "strings must be provided.");
@@ -22242,7 +22515,7 @@
@Override
public void resetStrings(@NonNull List<String> stringIds) {
Preconditions.checkCallAuthorization(hasCallingOrSelfPermission(
- android.Manifest.permission.UPDATE_DEVICE_MANAGEMENT_RESOURCES));
+ permission.UPDATE_DEVICE_MANAGEMENT_RESOURCES));
mInjector.binderWithCleanCallingIdentity(() -> {
if (mDeviceManagementResourcesProvider.removeStrings(stringIds)) {
@@ -22312,7 +22585,7 @@
@Override
public void resetShouldAllowBypassingDevicePolicyManagementRoleQualificationState() {
Preconditions.checkCallAuthorization(hasCallingOrSelfPermission(
- android.Manifest.permission.MANAGE_ROLE_HOLDERS));
+ permission.MANAGE_ROLE_HOLDERS));
setBypassDevicePolicyManagementRoleQualificationStateInternal(
/* currentRoleHolder= */ null, /* allowBypass= */ false);
}
@@ -22320,7 +22593,7 @@
@Override
public boolean shouldAllowBypassingDevicePolicyManagementRoleQualification() {
Preconditions.checkCallAuthorization(hasCallingOrSelfPermission(
- android.Manifest.permission.MANAGE_ROLE_HOLDERS));
+ permission.MANAGE_ROLE_HOLDERS));
return mInjector.binderWithCleanCallingIdentity(() -> {
if (getUserData(
UserHandle.USER_SYSTEM).mBypassDevicePolicyManagementRoleQualifications) {
@@ -23316,7 +23589,8 @@
return EnforcingAdmin.createDeviceAdminEnforcingAdmin(admin.info.getComponent(), userId,
admin);
}
- admin = getUserData(userId).createOrGetPermissionBasedAdmin(userId);
+ admin = Flags.activeAdminCleanup()
+ ? null : getUserData(userId).createOrGetPermissionBasedAdmin(userId);
return EnforcingAdmin.createEnforcingAdmin(caller.getPackageName(), userId, admin);
}
@@ -23339,8 +23613,8 @@
}
}
}
-
- admin = getUserData(userId).createOrGetPermissionBasedAdmin(userId);
+ admin = Flags.activeAdminCleanup()
+ ? null : getUserData(userId).createOrGetPermissionBasedAdmin(userId);
return EnforcingAdmin.createEnforcingAdmin(packageName, userId, admin);
}
@@ -23912,7 +24186,7 @@
if (!isRuntimePermission(permission)) {
continue;
}
- int grantState = DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT;
+ int grantState = PERMISSION_GRANT_STATE_DEFAULT;
try {
grantState = getPermissionGrantStateForUser(
packageInfo.packageName, permission,
@@ -23925,7 +24199,7 @@
Slogf.e(LOG_TAG, e, "Error retrieving permission grant state for %s "
+ "and %s", packageInfo.packageName, permission);
}
- if (grantState == DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT) {
+ if (grantState == PERMISSION_GRANT_STATE_DEFAULT) {
// Not Controlled by a policy
continue;
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/EnforcingAdmin.java b/services/devicepolicy/java/com/android/server/devicepolicy/EnforcingAdmin.java
index 634f1bc..1fd628a 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/EnforcingAdmin.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/EnforcingAdmin.java
@@ -23,6 +23,7 @@
import android.app.admin.DpcAuthority;
import android.app.admin.RoleAuthority;
import android.app.admin.UnknownAuthority;
+import android.app.admin.flags.Flags;
import android.content.ComponentName;
import android.os.UserHandle;
@@ -280,6 +281,10 @@
return getAuthorities().contains(authority);
}
+ boolean isSystemAuthority() {
+ return mIsSystemAuthority;
+ }
+
@NonNull
String getPackageName() {
return mPackageName;
@@ -291,9 +296,17 @@
@Nullable
public ActiveAdmin getActiveAdmin() {
+ if (Flags.activeAdminCleanup()) {
+ throw new UnsupportedOperationException("getActiveAdmin() no longer supported");
+ }
return mActiveAdmin;
}
+ @Nullable
+ ComponentName getComponentName() {
+ return mComponentName;
+ }
+
@NonNull
android.app.admin.EnforcingAdmin getParcelableAdmin() {
Authority authority;
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 19b0343..dc10dd9 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -249,8 +249,10 @@
import com.android.server.security.FileIntegrityService;
import com.android.server.security.KeyAttestationApplicationIdProviderService;
import com.android.server.security.KeyChainSystemService;
-import com.android.server.security.adaptiveauthentication.AdaptiveAuthenticationService;
import com.android.server.security.advancedprotection.AdvancedProtectionService;
+import com.android.server.security.authenticationpolicy.AuthenticationPolicyService;
+import com.android.server.security.authenticationpolicy.SecureLockDeviceService;
+import com.android.server.security.forensic.ForensicService;
import com.android.server.security.rkp.RemoteProvisioningService;
import com.android.server.selinux.SelinuxAuditLogsService;
import com.android.server.sensorprivacy.SensorPrivacyService;
@@ -1760,6 +1762,13 @@
mSystemServiceManager.startService(LogcatManagerService.class);
t.traceEnd();
+ if (!isWatch && !isTv && !isAutomotive
+ && android.security.Flags.aflApi()) {
+ t.traceBegin("StartForensicService");
+ mSystemServiceManager.startService(ForensicService.class);
+ t.traceEnd();
+ }
+
if (AppFunctionManagerConfiguration.isSupported(context)) {
t.traceBegin("StartAppFunctionManager");
mSystemServiceManager.startService(AppFunctionManagerService.class);
@@ -2651,9 +2660,15 @@
mSystemServiceManager.startService(AuthService.class);
t.traceEnd();
+ if (android.security.Flags.secureLockdown()) {
+ t.traceBegin("StartSecureLockDeviceService.Lifecycle");
+ mSystemServiceManager.startService(SecureLockDeviceService.Lifecycle.class);
+ t.traceEnd();
+ }
+
if (android.adaptiveauth.Flags.enableAdaptiveAuth()) {
- t.traceBegin("StartAdaptiveAuthenticationService");
- mSystemServiceManager.startService(AdaptiveAuthenticationService.class);
+ t.traceBegin("StartAuthenticationPolicyService");
+ mSystemServiceManager.startService(AuthenticationPolicyService.class);
t.traceEnd();
}
@@ -2761,8 +2776,9 @@
mSystemServiceManager.startService(WEAR_MODE_SERVICE_CLASS);
t.traceEnd();
- boolean enableWristOrientationService = SystemProperties.getBoolean(
- "config.enable_wristorientation", false);
+ boolean enableWristOrientationService =
+ !android.server.Flags.migrateWristOrientation()
+ && SystemProperties.getBoolean("config.enable_wristorientation", false);
if (enableWristOrientationService) {
t.traceBegin("StartWristOrientationService");
mSystemServiceManager.startService(WRIST_ORIENTATION_SERVICE_CLASS);
@@ -3053,7 +3069,10 @@
if (com.android.ranging.flags.Flags.rangingStackEnabled()) {
if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_UWB)
|| context.getPackageManager().hasSystemFeature(
- PackageManager.FEATURE_WIFI_RTT)) {
+ PackageManager.FEATURE_WIFI_RTT)
+ || (com.android.ranging.flags.Flags.rangingCsEnabled()
+ && context.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_BLUETOOTH_LE_CHANNEL_SOUNDING))) {
t.traceBegin("RangingService");
// TODO: b/375264320 - Remove after RELEASE_RANGING_STACK is ramped to next.
try {
diff --git a/services/java/com/android/server/flags.aconfig b/services/java/com/android/server/flags.aconfig
index e2ac22d..4412968 100644
--- a/services/java/com/android/server/flags.aconfig
+++ b/services/java/com/android/server/flags.aconfig
@@ -39,6 +39,14 @@
}
flag {
+ name: "migrate_wrist_orientation"
+ namespace: "wear_frameworks"
+ description: "Migrate wrist orientation service functionality to wear settings service"
+ bug: "352725980"
+ is_fixed_read_only: true
+}
+
+flag {
name: "allow_network_time_update_service"
namespace: "wear_systems"
description: "Allow NetworkTimeUpdateService on Wear"
diff --git a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
index 4e86888..e307e52 100644
--- a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
+++ b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
@@ -144,6 +144,10 @@
public void onBootPhase(int phase) {
if (phase == PHASE_SYSTEM_SERVICES_READY) {
UsbManager usbManager = getContext().getSystemService(UsbManager.class);
+ if (usbManager == null) {
+ mAdbActive = false;
+ return;
+ }
mAdbActive = ((usbManager.getCurrentFunctions() & UsbManager.FUNCTION_ADB) == 1);
Log.d(LOG_TAG, "ADB is " + mAdbActive + " on system startup");
}
diff --git a/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceRoboTest.java b/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceRoboTest.java
index a547d0f..4e9fff2 100644
--- a/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceRoboTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceRoboTest.java
@@ -31,6 +31,7 @@
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import static org.robolectric.Shadows.shadowOf;
import static org.testng.Assert.expectThrows;
@@ -96,6 +97,7 @@
@UserIdInt private int mUserTwoId;
@Mock private UserBackupManagerService mUserSystemService;
@Mock private UserBackupManagerService mUserOneService;
+ @Mock private BackupAgentConnectionManager mUserOneBackupAgentConnectionManager;
@Mock private UserBackupManagerService mUserTwoService;
/** Setup */
@@ -116,6 +118,9 @@
mShadowContext.grantPermissions(BACKUP);
mShadowContext.grantPermissions(INTERACT_ACROSS_USERS_FULL);
+ when(mUserOneService.getBackupAgentConnectionManager()).thenReturn(
+ mUserOneBackupAgentConnectionManager);
+
ShadowBinder.setCallingUid(Process.SYSTEM_UID);
}
@@ -226,7 +231,7 @@
backupManagerService.agentConnected(mUserOneId, TEST_PACKAGE, agentBinder);
- verify(mUserOneService).agentConnected(TEST_PACKAGE, agentBinder);
+ verify(mUserOneBackupAgentConnectionManager).agentConnected(TEST_PACKAGE, agentBinder);
}
/** Test that the backup service does not route methods for non-registered users. */
@@ -239,7 +244,8 @@
backupManagerService.agentConnected(mUserTwoId, TEST_PACKAGE, agentBinder);
- verify(mUserOneService, never()).agentConnected(TEST_PACKAGE, agentBinder);
+ verify(mUserOneBackupAgentConnectionManager, never()).agentConnected(TEST_PACKAGE,
+ agentBinder);
}
/** Test that the backup service routes methods correctly to the user that requests it. */
diff --git a/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java b/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
index 7349c14..aeb1ba9 100644
--- a/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
@@ -107,6 +107,7 @@
import com.android.internal.infra.AndroidFuture;
import com.android.server.EventLogTags;
import com.android.server.LocalServices;
+import com.android.server.backup.BackupAgentConnectionManager;
import com.android.server.backup.BackupRestoreTask;
import com.android.server.backup.DataChangedJournal;
import com.android.server.backup.KeyValueBackupJob;
@@ -167,7 +168,6 @@
import java.util.concurrent.TimeoutException;
import java.util.stream.Stream;
-// TODO: Test agents timing out
@RunWith(RobolectricTestRunner.class)
@Config(
shadows = {
@@ -195,6 +195,7 @@
@Mock private IBackupManagerMonitor mMonitor;
@Mock private OnTaskFinishedListener mListener;
@Mock private PackageManagerInternal mPackageManagerInternal;
+ @Mock private BackupAgentConnectionManager mBackupAgentConnectionManager;
private UserBackupManagerService mBackupManagerService;
private TransportData mTransport;
@@ -257,6 +258,8 @@
when(mBackupManagerService.getBaseStateDir()).thenReturn(mBaseStateDir);
when(mBackupManagerService.getDataDir()).thenReturn(mDataDir);
when(mBackupManagerService.getBackupManagerBinder()).thenReturn(mBackupManager);
+ when(mBackupManagerService.getBackupAgentConnectionManager()).thenReturn(
+ mBackupAgentConnectionManager);
mBackupHandler = mBackupManagerService.getBackupHandler();
mShadowBackupLooper = shadowOf(mBackupHandler.getLooper());
@@ -749,7 +752,8 @@
/**
* Agent unavailable means {@link
- * UserBackupManagerService#bindToAgentSynchronous(ApplicationInfo, int)} returns {@code null}.
+ * BackupAgentConnectionManager#bindToAgentSynchronous(ApplicationInfo, int, int)} returns
+ * {@code null}.
*
* @see #setUpAgent(PackageData)
*/
@@ -805,7 +809,7 @@
TransportMock transportMock = setUpInitializedTransport(mTransport);
setUpAgent(PACKAGE_1);
doThrow(SecurityException.class)
- .when(mBackupManagerService)
+ .when(mBackupAgentConnectionManager)
.bindToAgentSynchronous(argThat(applicationInfo(PACKAGE_1)), anyInt(), anyInt());
KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1);
@@ -823,7 +827,7 @@
TransportMock transportMock = setUpInitializedTransport(mTransport);
setUpAgent(PACKAGE_1);
doThrow(SecurityException.class)
- .when(mBackupManagerService)
+ .when(mBackupAgentConnectionManager)
.bindToAgentSynchronous(argThat(applicationInfo(PACKAGE_1)), anyInt(), anyInt());
KeyValueBackupTask task = createKeyValueBackupTask(transportMock, true, PACKAGE_1);
@@ -861,7 +865,7 @@
runTask(task);
verify(mBackupManagerService).setWorkSource(null);
- verify(mBackupManagerService).unbindAgent(argThat(applicationInfo(PACKAGE_1)));
+ verify(mBackupAgentConnectionManager).unbindAgent(argThat(applicationInfo(PACKAGE_1)));
}
@Test
@@ -1097,7 +1101,7 @@
runTask(task);
verify(agentMock.agentBinder).fail(any());
- verify(mBackupManagerService).unbindAgent(argThat(applicationInfo(PACKAGE_1)));
+ verify(mBackupAgentConnectionManager).unbindAgent(argThat(applicationInfo(PACKAGE_1)));
}
@Test
@@ -1418,7 +1422,8 @@
.isEqualTo("newState".getBytes());
assertCleansUpFiles(mTransport, PM_PACKAGE);
// We don't unbind PM
- verify(mBackupManagerService, never()).unbindAgent(argThat(applicationInfo(PM_PACKAGE)));
+ verify(mBackupAgentConnectionManager, never()).unbindAgent(
+ argThat(applicationInfo(PM_PACKAGE)));
}
@Test
@@ -1439,7 +1444,8 @@
runTask(task);
- verify(mBackupManagerService, never()).unbindAgent(argThat(applicationInfo(PM_PACKAGE)));
+ verify(mBackupAgentConnectionManager, never()).unbindAgent(
+ argThat(applicationInfo(PM_PACKAGE)));
}
@Test
@@ -1642,9 +1648,10 @@
runTask(task);
- InOrder inOrder = inOrder(agentMock.agent, mBackupManagerService);
+ InOrder inOrder = inOrder(agentMock.agent, mBackupAgentConnectionManager);
inOrder.verify(agentMock.agent).onQuotaExceeded(anyLong(), eq(1234L));
- inOrder.verify(mBackupManagerService).unbindAgent(argThat(applicationInfo(PACKAGE_1)));
+ inOrder.verify(mBackupAgentConnectionManager).unbindAgent(
+ argThat(applicationInfo(PACKAGE_1)));
}
@Test
@@ -2634,12 +2641,12 @@
doNothing().when(backupAgentBinder).fail(any());
if (packageData.available) {
doReturn(backupAgentBinder)
- .when(mBackupManagerService)
+ .when(mBackupAgentConnectionManager)
.bindToAgentSynchronous(argThat(applicationInfo(packageData)), anyInt(),
anyInt());
} else {
doReturn(null)
- .when(mBackupManagerService)
+ .when(mBackupAgentConnectionManager)
.bindToAgentSynchronous(argThat(applicationInfo(packageData)), anyInt(),
anyInt());
}
@@ -2976,7 +2983,7 @@
private void assertCleansUpFilesAndAgent(TransportData transport, PackageData packageData) {
assertCleansUpFiles(transport, packageData);
- verify(mBackupManagerService).unbindAgent(argThat(applicationInfo(packageData)));
+ verify(mBackupAgentConnectionManager).unbindAgent(argThat(applicationInfo(packageData)));
}
private void assertCleansUpFiles(TransportData transport, PackageData packageData) {
diff --git a/services/supervision/java/com/android/server/supervision/SupervisionService.java b/services/supervision/java/com/android/server/supervision/SupervisionService.java
index 67e2547..53a25dd 100644
--- a/services/supervision/java/com/android/server/supervision/SupervisionService.java
+++ b/services/supervision/java/com/android/server/supervision/SupervisionService.java
@@ -21,10 +21,11 @@
import android.annotation.UserIdInt;
import android.app.admin.DevicePolicyManagerInternal;
import android.app.supervision.ISupervisionManager;
+import android.app.supervision.SupervisionManagerInternal;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.UserInfo;
-import android.os.Bundle;
+import android.os.PersistableBundle;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ShellCallback;
@@ -179,8 +180,15 @@
}
@Override
+ public boolean isSupervisionLockscreenEnabledForUser(@UserIdInt int userId) {
+ synchronized (getLockObject()) {
+ return getUserDataLocked(userId).supervisionLockScreenEnabled;
+ }
+ }
+
+ @Override
public void setSupervisionLockscreenEnabledForUser(
- @UserIdInt int userId, boolean enabled, @Nullable Bundle options) {
+ @UserIdInt int userId, boolean enabled, @Nullable PersistableBundle options) {
synchronized (getLockObject()) {
SupervisionUserData data = getUserDataLocked(userId);
data.supervisionLockScreenEnabled = enabled;
diff --git a/services/supervision/java/com/android/server/supervision/SupervisionUserData.java b/services/supervision/java/com/android/server/supervision/SupervisionUserData.java
index 5616237..1dd48f5 100644
--- a/services/supervision/java/com/android/server/supervision/SupervisionUserData.java
+++ b/services/supervision/java/com/android/server/supervision/SupervisionUserData.java
@@ -19,7 +19,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
-import android.os.Bundle;
+import android.os.PersistableBundle;
import android.util.IndentingPrintWriter;
/** User specific data, used internally by the {@link SupervisionService}. */
@@ -27,7 +27,7 @@
public final @UserIdInt int userId;
public boolean supervisionEnabled;
public boolean supervisionLockScreenEnabled;
- @Nullable public Bundle supervisionLockScreenOptions;
+ @Nullable public PersistableBundle supervisionLockScreenOptions;
public SupervisionUserData(@UserIdInt int userId) {
this.userId = userId;
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/InputMethodServiceTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/InputMethodServiceTest.java
index 5bb6b19..d087155 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/InputMethodServiceTest.java
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/InputMethodServiceTest.java
@@ -194,7 +194,13 @@
() -> assertThat(mActivity.hideImeWithWindowInsetsController()).isTrue(),
true /* expected */,
false /* inputViewStarted */);
- assertThat(mInputMethodService.isInputViewShown()).isFalse();
+ if (mFlagsValueProvider.getBoolean(Flags.FLAG_REFACTOR_INSETS_CONTROLLER)) {
+ // The IME visibility is only sent at the end of the animation. Therefore, we have to
+ // wait until the visibility was sent to the server and the IME window hidden.
+ eventually(() -> assertThat(mInputMethodService.isInputViewShown()).isFalse());
+ } else {
+ assertThat(mInputMethodService.isInputViewShown()).isFalse();
+ }
}
/**
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/BroadcastHelperTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/BroadcastHelperTest.java
index 1be5cef..acd34e3 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/BroadcastHelperTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/BroadcastHelperTest.java
@@ -28,6 +28,7 @@
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -60,6 +61,7 @@
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
+import java.util.List;
@AppModeFull
@AppModeNonSdkSandbox
@@ -124,7 +126,8 @@
@Test
public void changeNonExportedComponent_sendPackageChangedBroadcastToSystem_withPermission()
throws Exception {
- changeComponentAndSendPackageChangedBroadcast(false /* changeExportedComponent */);
+ changeComponentAndSendPackageChangedBroadcast(false /* changeExportedComponent */,
+ new String[0] /* sharedPackages */);
ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
verify(mMockActivityManagerInternal).broadcastIntentWithCallback(
@@ -140,7 +143,8 @@
@Test
public void changeNonExportedComponent_sendPackageChangedBroadcastToApplicationItself()
throws Exception {
- changeComponentAndSendPackageChangedBroadcast(false /* changeExportedComponent */);
+ changeComponentAndSendPackageChangedBroadcast(false /* changeExportedComponent */,
+ new String[0] /* sharedPackages */);
ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
verify(mMockActivityManagerInternal).broadcastIntentWithCallback(captor.capture(), eq(null),
@@ -150,9 +154,45 @@
assertThat(intent.getPackage()).isEqualTo(PACKAGE_CHANGED_TEST_PACKAGE_NAME);
}
+ @RequiresFlagsEnabled(FLAG_REDUCE_BROADCASTS_FOR_COMPONENT_STATE_CHANGES)
+ @Test
+ public void changeNonExportedComponent_sendPackageChangedBroadcastToSharedUserIdApplications()
+ throws Exception {
+ changeComponentAndSendPackageChangedBroadcast(false /* changeExportedComponent */,
+ new String[]{"shared.package"} /* sharedPackages */);
+
+ ArgumentCaptor<Intent> captorIntent = ArgumentCaptor.forClass(Intent.class);
+ ArgumentCaptor<String[]> captorRequiredPermissions = ArgumentCaptor.forClass(
+ String[].class);
+ verify(mMockActivityManagerInternal, times(3)).broadcastIntentWithCallback(
+ captorIntent.capture(), eq(null), captorRequiredPermissions.capture(), anyInt(),
+ eq(null), eq(null), eq(null));
+ List<Intent> intents = captorIntent.getAllValues();
+ List<String[]> requiredPermissions = captorRequiredPermissions.getAllValues();
+ assertNotNull(intents);
+ assertThat(intents.size()).isEqualTo(3);
+
+ final Intent intent1 = intents.get(0);
+ final String[] requiredPermission1 = requiredPermissions.get(0);
+ assertThat(intent1.getPackage()).isEqualTo("android");
+ assertThat(requiredPermission1).isEqualTo(
+ new String[]{PERMISSION_PACKAGE_CHANGED_BROADCAST_ON_COMPONENT_STATE_CHANGED});
+
+ final Intent intent2 = intents.get(1);
+ final String[] requiredPermission2 = requiredPermissions.get(1);
+ assertThat(intent2.getPackage()).isEqualTo(PACKAGE_CHANGED_TEST_PACKAGE_NAME);
+ assertThat(requiredPermission2).isNull();
+
+ final Intent intent3 = intents.get(2);
+ final String[] requiredPermission3 = requiredPermissions.get(2);
+ assertThat(intent3.getPackage()).isEqualTo("shared.package");
+ assertThat(requiredPermission3).isNull();
+ }
+
@Test
public void changeExportedComponent_sendPackageChangedBroadcastToAll() throws Exception {
- changeComponentAndSendPackageChangedBroadcast(true /* changeExportedComponent */);
+ changeComponentAndSendPackageChangedBroadcast(true /* changeExportedComponent */,
+ new String[0] /* sharedPackages */);
ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
verify(mMockActivityManagerInternal).broadcastIntentWithCallback(captor.capture(), eq(null),
@@ -162,11 +202,14 @@
assertNull(intent.getPackage());
}
- private void changeComponentAndSendPackageChangedBroadcast(boolean changeExportedComponent) {
+ private void changeComponentAndSendPackageChangedBroadcast(boolean changeExportedComponent,
+ String[] sharedPackages) {
when(mMockSnapshot.getPackageStateInternal(eq(PACKAGE_CHANGED_TEST_PACKAGE_NAME),
anyInt())).thenReturn(mMockPackageStateInternal);
when(mMockSnapshot.isInstantAppInternal(any(), anyInt(), anyInt())).thenReturn(false);
when(mMockSnapshot.getVisibilityAllowLists(any(), any())).thenReturn(null);
+ when(mMockSnapshot.getSharedUserPackagesForPackage(eq(PACKAGE_CHANGED_TEST_PACKAGE_NAME),
+ anyInt())).thenReturn(sharedPackages);
when(mMockPackageStateInternal.getPkg()).thenReturn(mMockAndroidPackageInternal);
when(mMockParsedActivity.getClassName()).thenReturn(
diff --git a/services/tests/VpnTests/java/com/android/server/connectivity/VpnTest.java b/services/tests/VpnTests/java/com/android/server/connectivity/VpnTest.java
index 08155c7..9772ef9 100644
--- a/services/tests/VpnTests/java/com/android/server/connectivity/VpnTest.java
+++ b/services/tests/VpnTests/java/com/android/server/connectivity/VpnTest.java
@@ -2380,10 +2380,14 @@
@Test
public void doTestMigrateIkeSession_Vcn() throws Exception {
final int expectedKeepalive = 2097; // Any unlikely number will do
- final NetworkCapabilities vcnNc = new NetworkCapabilities.Builder()
- .addTransportType(TRANSPORT_CELLULAR)
- .setTransportInfo(new VcnTransportInfo(TEST_SUB_ID, expectedKeepalive))
- .build();
+ final NetworkCapabilities vcnNc =
+ new NetworkCapabilities.Builder()
+ .addTransportType(TRANSPORT_CELLULAR)
+ .setTransportInfo(
+ new VcnTransportInfo.Builder()
+ .setMinUdpPort4500NatTimeoutSeconds(expectedKeepalive)
+ .build())
+ .build();
final Ikev2VpnProfile ikev2VpnProfile = makeIkeV2VpnProfile(
true /* isAutomaticIpVersionSelectionEnabled */,
true /* isAutomaticNattKeepaliveTimerEnabled */,
diff --git a/services/tests/apexsystemservices/services/Android.bp b/services/tests/apexsystemservices/services/Android.bp
index 477ea4c..70d84dc 100644
--- a/services/tests/apexsystemservices/services/Android.bp
+++ b/services/tests/apexsystemservices/services/Android.bp
@@ -17,4 +17,5 @@
],
visibility: ["//frameworks/base/services/tests/apexsystemservices:__subpackages__"],
apex_available: ["//apex_available:anyapex"],
+ compile_dex: true,
}
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 80e5ee3..759976f 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -226,6 +226,9 @@
"EVENT_DISPLAY_HDR_SDR_RATIO_CHANGED";
private static final String EVENT_DISPLAY_CONNECTED = "EVENT_DISPLAY_CONNECTED";
private static final String EVENT_DISPLAY_DISCONNECTED = "EVENT_DISPLAY_DISCONNECTED";
+ private static final String EVENT_DISPLAY_REFRESH_RATE_CHANGED =
+ "EVENT_DISPLAY_REFRESH_RATE_CHANGED";
+ private static final String EVENT_DISPLAY_STATE_CHANGED = "EVENT_DISPLAY_STATE_CHANGED";
private static final String DISPLAY_GROUP_EVENT_ADDED = "DISPLAY_GROUP_EVENT_ADDED";
private static final String DISPLAY_GROUP_EVENT_REMOVED = "DISPLAY_GROUP_EVENT_REMOVED";
private static final String DISPLAY_GROUP_EVENT_CHANGED = "DISPLAY_GROUP_EVENT_CHANGED";
@@ -4234,6 +4237,10 @@
return EVENT_DISPLAY_CONNECTED;
case DisplayManagerGlobal.EVENT_DISPLAY_DISCONNECTED:
return EVENT_DISPLAY_DISCONNECTED;
+ case DisplayManagerGlobal.EVENT_DISPLAY_REFRESH_RATE_CHANGED:
+ return EVENT_DISPLAY_REFRESH_RATE_CHANGED;
+ case DisplayManagerGlobal.EVENT_DISPLAY_STATE_CHANGED:
+ return EVENT_DISPLAY_STATE_CHANGED;
default:
return "UNKNOWN: " + eventType;
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
index e64d985..c996339 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
@@ -121,8 +121,8 @@
private static final float PROX_SENSOR_MAX_RANGE = 5;
private static final float DOZE_SCALE_FACTOR = 0.34f;
private static final float DEFAULT_DOZE_BRIGHTNESS = 0.121f;
+ private static final float OVERRIDE_BRIGHTNESS = 0.567f;
- private static final float BRIGHTNESS_RAMP_RATE_MINIMUM = 0.0f;
private static final float BRIGHTNESS_RAMP_RATE_FAST_DECREASE = 0.3f;
private static final float BRIGHTNESS_RAMP_RATE_FAST_INCREASE = 0.4f;
private static final float BRIGHTNESS_RAMP_RATE_SLOW_DECREASE = 0.1f;
@@ -2438,6 +2438,36 @@
any(BrightnessEvent.class));
}
+ @Test
+ public void brightnessOverrideInPowerRequest_enablesOverrideBrightnessStrategy() {
+ when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON);
+ DisplayPowerRequest dpr = new DisplayPowerRequest();
+ dpr.screenBrightnessOverride = OVERRIDE_BRIGHTNESS;
+ mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+ advanceTime(1); // Run updatePowerState
+
+ verify(mHolder.animator).animateTo(eq(OVERRIDE_BRIGHTNESS), anyFloat(), anyFloat(),
+ eq(false));
+ }
+
+ @Test
+ public void brightnessOverrideRequest_enablesOverrideBrightnessStrategy() {
+ mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID);
+ when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON);
+
+ DisplayPowerRequest dpr = new DisplayPowerRequest();
+ mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+ advanceTime(1); // Run updatePowerState
+
+ var dbor = new DisplayManagerInternal.DisplayBrightnessOverrideRequest();
+ dbor.brightness = OVERRIDE_BRIGHTNESS;
+ mHolder.dpc.setBrightnessOverrideRequest(dbor);
+ advanceTime(1); // Process the WM brightness override request
+
+ verify(mHolder.animator).animateTo(eq(OVERRIDE_BRIGHTNESS), anyFloat(), anyFloat(),
+ eq(false));
+ }
+
/**
* Creates a mock and registers it to {@link LocalServices}.
*/
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 b6da3ae..ff652a2 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayMapperTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayMapperTest.java
@@ -35,7 +35,9 @@
import static com.android.server.display.LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_ADDED;
import static com.android.server.display.LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_CONNECTED;
import static com.android.server.display.LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_DISCONNECTED;
+import static com.android.server.display.LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_REFRESH_RATE_CHANGED;
import static com.android.server.display.LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_REMOVED;
+import static com.android.server.display.LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_STATE_CHANGED;
import static com.android.server.display.layout.Layout.Display.POSITION_REAR;
import static com.android.server.display.layout.Layout.Display.POSITION_UNKNOWN;
import static com.android.server.utils.FoldSettingProvider.SETTING_VALUE_SELECTIVE_STAY_AWAKE;
@@ -1158,6 +1160,29 @@
mLogicalDisplayMapper.getDisplayLocked(device2).getDevicePositionLocked());
}
+ @Test
+ public void updateAndGetMaskForDisplayPropertyChanges_getsPropertyChangedFlags() {
+ // Change the display state
+ DisplayInfo newDisplayInfo = new DisplayInfo();
+ newDisplayInfo.state = STATE_OFF;
+ assertEquals(LOGICAL_DISPLAY_EVENT_STATE_CHANGED,
+ mLogicalDisplayMapper.updateAndGetMaskForDisplayPropertyChanges(newDisplayInfo));
+
+ // Change the refresh rate override
+ newDisplayInfo = new DisplayInfo();
+ newDisplayInfo.refreshRateOverride = 30;
+ assertEquals(LOGICAL_DISPLAY_EVENT_REFRESH_RATE_CHANGED,
+ mLogicalDisplayMapper.updateAndGetMaskForDisplayPropertyChanges(newDisplayInfo));
+
+ // Change multiple properties
+ newDisplayInfo = new DisplayInfo();
+ newDisplayInfo.refreshRateOverride = 30;
+ newDisplayInfo.state = STATE_OFF;
+ assertEquals(LOGICAL_DISPLAY_EVENT_REFRESH_RATE_CHANGED
+ | LOGICAL_DISPLAY_EVENT_STATE_CHANGED,
+ mLogicalDisplayMapper.updateAndGetMaskForDisplayPropertyChanges(newDisplayInfo));
+ }
+
/////////////////
// Helper Methods
/////////////////
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java
index 4f875c3..49de801 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java
@@ -52,6 +52,7 @@
import com.android.server.display.brightness.strategy.AutomaticBrightnessStrategy;
import com.android.server.display.brightness.strategy.DisplayBrightnessStrategy;
import com.android.server.display.brightness.strategy.OffloadBrightnessStrategy;
+import com.android.server.display.brightness.strategy.OverrideBrightnessStrategy;
import com.android.server.display.brightness.strategy.TemporaryBrightnessStrategy;
import com.android.server.display.feature.DisplayManagerFlags;
@@ -155,6 +156,27 @@
}
@Test
+ public void updateWindowManagerBrightnessOverride() {
+ var request = new DisplayManagerInternal.DisplayBrightnessOverrideRequest();
+ request.brightness = 0.4f;
+ request.tag = "cts";
+ OverrideBrightnessStrategy overrideBrightnessStrategy = mock(
+ OverrideBrightnessStrategy.class);
+ when(mDisplayBrightnessStrategySelector.getOverrideBrightnessStrategy()).thenReturn(
+ overrideBrightnessStrategy);
+
+ when(overrideBrightnessStrategy.updateWindowManagerBrightnessOverride(any()))
+ .thenReturn(false);
+ assertFalse(mDisplayBrightnessController.updateWindowManagerBrightnessOverride(request));
+ verify(overrideBrightnessStrategy).updateWindowManagerBrightnessOverride(request);
+
+ when(overrideBrightnessStrategy.updateWindowManagerBrightnessOverride(any()))
+ .thenReturn(true);
+ assertTrue(mDisplayBrightnessController.updateWindowManagerBrightnessOverride(request));
+ verify(overrideBrightnessStrategy, times(2)).updateWindowManagerBrightnessOverride(request);
+ }
+
+ @Test
public void setCurrentScreenBrightness() {
// Current Screen brightness is set as expected when a different value than the current
// is set
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java
index b99a18c..a647691 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java
@@ -183,6 +183,8 @@
when(mContext.getContentResolver()).thenReturn(contentResolver);
when(mContext.getResources()).thenReturn(mResources);
when(mInvalidBrightnessStrategy.getName()).thenReturn("InvalidBrightnessStrategy");
+ when(mOverrideBrightnessStrategy.getWindowManagerBrightnessOverride())
+ .thenReturn(PowerManager.BRIGHTNESS_INVALID_FLOAT);
mDisplayBrightnessStrategySelector = new DisplayBrightnessStrategySelector(mContext,
mInjector, DISPLAY_ID, mDisplayManagerFlags);
@@ -284,6 +286,20 @@
}
@Test
+ public void selectStrategySelectsOverrideStrategyWhenWindowManagerOverrideIsValid() {
+ DisplayManagerInternal.DisplayPowerRequest displayPowerRequest = mock(
+ DisplayManagerInternal.DisplayPowerRequest.class);
+ displayPowerRequest.screenBrightnessOverride = Float.NaN;
+ when(mFollowerBrightnessStrategy.getBrightnessToFollow()).thenReturn(Float.NaN);
+ when(mOverrideBrightnessStrategy.getWindowManagerBrightnessOverride()).thenReturn(0.4f);
+ assertEquals(mDisplayBrightnessStrategySelector.selectStrategy(
+ new StrategySelectionRequest(displayPowerRequest, Display.STATE_ON,
+ 0.1f, false, mDisplayOffloadSession,
+ STYLUS_IS_NOT_BEING_USED, /* isBedtimeModeWearEnabled= */ false)),
+ mOverrideBrightnessStrategy);
+ }
+
+ @Test
public void selectStrategySelectsTemporaryStrategyWhenValid() {
DisplayManagerInternal.DisplayPowerRequest displayPowerRequest = mock(
DisplayManagerInternal.DisplayPowerRequest.class);
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/OverrideBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/OverrideBrightnessStrategyTest.java
index cc21af1..414f274a 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/OverrideBrightnessStrategyTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/OverrideBrightnessStrategyTest.java
@@ -16,8 +16,9 @@
package com.android.server.display.brightness.strategy;
+import static android.os.PowerManager.BRIGHTNESS_INVALID_FLOAT;
-import static org.junit.Assert.assertEquals;
+import static com.google.common.truth.Truth.assertThat;
import android.hardware.display.DisplayManagerInternal;
@@ -34,7 +35,6 @@
@SmallTest
@RunWith(AndroidJUnit4.class)
-
public class OverrideBrightnessStrategyTest {
private OverrideBrightnessStrategy mOverrideBrightnessStrategy;
@@ -62,7 +62,56 @@
new StrategyExecutionRequest(displayPowerRequest, 0.2f,
/* userSetBrightnessChanged= */ false,
/* isStylusBeingUsed */ false));
- assertEquals(updatedDisplayBrightnessState, expectedDisplayBrightnessState);
+ assertThat(updatedDisplayBrightnessState).isEqualTo(expectedDisplayBrightnessState);
}
+ @Test
+ public void testUpdateBrightnessWhenWindowManagerOverrideIsRequested() {
+ var overrideRequest = new DisplayManagerInternal.DisplayBrightnessOverrideRequest();
+ overrideRequest.brightness = 0.2f;
+ mOverrideBrightnessStrategy.updateWindowManagerBrightnessOverride(overrideRequest);
+ DisplayManagerInternal.DisplayPowerRequest
+ displayPowerRequest = new DisplayManagerInternal.DisplayPowerRequest();
+ displayPowerRequest.screenBrightnessOverride = BRIGHTNESS_INVALID_FLOAT;
+ BrightnessReason brightnessReason = new BrightnessReason();
+ brightnessReason.setReason(BrightnessReason.REASON_OVERRIDE);
+ DisplayBrightnessState expectedDisplayBrightnessState =
+ new DisplayBrightnessState.Builder()
+ .setBrightness(overrideRequest.brightness)
+ .setBrightnessReason(brightnessReason)
+ .setDisplayBrightnessStrategyName(mOverrideBrightnessStrategy.getName())
+ .build();
+ DisplayBrightnessState updatedDisplayBrightnessState =
+ mOverrideBrightnessStrategy.updateBrightness(
+ new StrategyExecutionRequest(displayPowerRequest, 0.2f,
+ /* userSetBrightnessChanged= */ false,
+ /* isStylusBeingUsed */ false));
+ assertThat(updatedDisplayBrightnessState).isEqualTo(expectedDisplayBrightnessState);
+ }
+
+ @Test
+ public void testUpdateWindowManagerBrightnessOverride() {
+ var request = new DisplayManagerInternal.DisplayBrightnessOverrideRequest();
+ assertThat(mOverrideBrightnessStrategy.updateWindowManagerBrightnessOverride(request))
+ .isFalse();
+ assertThat(mOverrideBrightnessStrategy.getWindowManagerBrightnessOverride())
+ .isEqualTo(BRIGHTNESS_INVALID_FLOAT);
+
+ request.brightness = 0.2f;
+ assertThat(mOverrideBrightnessStrategy.updateWindowManagerBrightnessOverride(request))
+ .isTrue();
+ assertThat(mOverrideBrightnessStrategy.getWindowManagerBrightnessOverride())
+ .isEqualTo(0.2f);
+
+ // Passing the same request doesn't result in an update.
+ assertThat(mOverrideBrightnessStrategy.updateWindowManagerBrightnessOverride(request))
+ .isFalse();
+ assertThat(mOverrideBrightnessStrategy.getWindowManagerBrightnessOverride())
+ .isEqualTo(0.2f);
+
+ assertThat(mOverrideBrightnessStrategy.updateWindowManagerBrightnessOverride(null))
+ .isTrue();
+ assertThat(mOverrideBrightnessStrategy.getWindowManagerBrightnessOverride())
+ .isEqualTo(BRIGHTNESS_INVALID_FLOAT);
+ }
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/SystemRequestObserverTest.kt b/services/tests/displayservicetests/src/com/android/server/display/mode/SystemRequestObserverTest.kt
index 9ea7ea7..56e4048 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/SystemRequestObserverTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/SystemRequestObserverTest.kt
@@ -27,6 +27,7 @@
import org.mockito.junit.MockitoJUnit
import org.mockito.kotlin.any
import org.mockito.kotlin.argumentCaptor
+import org.mockito.kotlin.clearInvocations
import org.mockito.kotlin.doThrow
import org.mockito.kotlin.eq
import org.mockito.kotlin.mock
@@ -149,6 +150,29 @@
}
@Test
+ fun testTokenUnlinkToDeath_noVotes() {
+ val systemRequestObserver = SystemRequestObserver(storage)
+
+ systemRequestObserver.requestDisplayModes(mockToken, DISPLAY_ID, null)
+
+ verify(mockToken, never()).unlinkToDeath(any(), eq(0))
+ }
+
+ @Test
+ fun testTokenUnlinkToDeath_removedVotes() {
+ val systemRequestObserver = SystemRequestObserver(storage)
+ val requestedModes = intArrayOf(1, 2, 3)
+
+ systemRequestObserver.requestDisplayModes(mockToken, DISPLAY_ID, requestedModes)
+ systemRequestObserver.requestDisplayModes(mockToken, DISPLAY_ID, null)
+ clearInvocations(mockToken)
+
+ systemRequestObserver.requestDisplayModes(mockToken, DISPLAY_ID, null)
+
+ verify(mockToken, never()).unlinkToDeath(any(), eq(0))
+ }
+
+ @Test
fun testTokenUnlinkToDeathNotCalled_votesForOtherDisplayInStorage() {
val systemRequestObserver = SystemRequestObserver(storage)
val requestedModes = intArrayOf(1, 2, 3)
diff --git a/services/tests/mockingservicestests/Android.bp b/services/tests/mockingservicestests/Android.bp
index 95acd75..993569f 100644
--- a/services/tests/mockingservicestests/Android.bp
+++ b/services/tests/mockingservicestests/Android.bp
@@ -78,7 +78,7 @@
"am_flags_lib",
"device_policy_aconfig_flags_lib",
] + select(soong_config_variable("ANDROID", "release_crashrecovery_module"), {
- "true": ["service-crashrecovery.impl"],
+ "true": ["service-crashrecovery-pre-jarjar"],
default: [],
}),
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 30de0e8..8dc8c14 100644
--- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
@@ -67,7 +67,6 @@
import static com.android.server.alarm.AlarmManagerService.AlarmHandler.CHECK_EXACT_ALARM_PERMISSION_ON_UPDATE;
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;
import static com.android.server.alarm.AlarmManagerService.AlarmHandler.REMOVE_FOR_CANCELED;
import static com.android.server.alarm.AlarmManagerService.AlarmHandler.TEMPORARY_QUOTA_CHANGED;
import static com.android.server.alarm.AlarmManagerService.Constants.KEY_ALLOW_WHILE_IDLE_COMPAT_QUOTA;
@@ -152,7 +151,6 @@
import android.os.UserHandle;
import android.os.UserManager;
import android.platform.test.annotations.DisableFlags;
-import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.Presubmit;
import android.platform.test.flag.junit.SetFlagsRule;
import android.platform.test.flag.util.FlagSetException;
@@ -436,8 +434,7 @@
*/
private void disableFlagsNotSetByAnnotation() {
try {
- mSetFlagsRule.disableFlags(Flags.FLAG_USE_FROZEN_STATE_TO_DROP_LISTENER_ALARMS,
- Flags.FLAG_START_USER_BEFORE_SCHEDULED_ALARMS);
+ mSetFlagsRule.disableFlags(Flags.FLAG_START_USER_BEFORE_SCHEDULED_ALARMS);
} catch (FlagSetException fse) {
// Expected if the test about to be run requires this enabled.
}
@@ -523,13 +520,11 @@
mService.onStart();
- if (Flags.useFrozenStateToDropListenerAlarms()) {
- final ArgumentCaptor<ActivityManager.UidFrozenStateChangedCallback> frozenCaptor =
- ArgumentCaptor.forClass(ActivityManager.UidFrozenStateChangedCallback.class);
- verify(mActivityManager).registerUidFrozenStateChangedCallback(
- any(HandlerExecutor.class), frozenCaptor.capture());
- mUidFrozenStateCallback = frozenCaptor.getValue();
- }
+ final ArgumentCaptor<ActivityManager.UidFrozenStateChangedCallback> frozenCaptor =
+ ArgumentCaptor.forClass(ActivityManager.UidFrozenStateChangedCallback.class);
+ verify(mActivityManager).registerUidFrozenStateChangedCallback(
+ any(HandlerExecutor.class), frozenCaptor.capture());
+ mUidFrozenStateCallback = frozenCaptor.getValue();
// Unable to mock mMockContext to return a mock stats manager.
// So just mocking the whole MetricsHelper instance.
@@ -3744,79 +3739,11 @@
testTemporaryQuota_bumpedBeforeDeferral(STANDBY_BUCKET_RARE);
}
- @Test
- public void exactListenerAlarmsRemovedOnCached() {
- mockChangeEnabled(EXACT_LISTENER_ALARMS_DROPPED_ON_CACHED, true);
-
- setTestAlarmWithListener(ELAPSED_REALTIME, 31, getNewListener(() -> {}), WINDOW_EXACT,
- TEST_CALLING_UID);
- setTestAlarmWithListener(RTC, 42, getNewListener(() -> {}), 56, TEST_CALLING_UID);
- setTestAlarm(ELAPSED_REALTIME, 54, WINDOW_EXACT, getNewMockPendingIntent(), 0, 0,
- TEST_CALLING_UID, null);
- setTestAlarm(RTC, 49, 154, getNewMockPendingIntent(), 0, 0, TEST_CALLING_UID, null);
-
- setTestAlarmWithListener(ELAPSED_REALTIME, 21, getNewListener(() -> {}), WINDOW_EXACT,
- TEST_CALLING_UID_2);
- setTestAlarmWithListener(RTC, 412, getNewListener(() -> {}), 561, TEST_CALLING_UID_2);
- setTestAlarm(ELAPSED_REALTIME, 26, WINDOW_EXACT, getNewMockPendingIntent(), 0, 0,
- TEST_CALLING_UID_2, null);
- setTestAlarm(RTC, 549, 234, getNewMockPendingIntent(), 0, 0, TEST_CALLING_UID_2, null);
-
- assertEquals(8, mService.mAlarmStore.size());
-
- mListener.handleUidCachedChanged(TEST_CALLING_UID, true);
- assertAndHandleMessageSync(REMOVE_EXACT_LISTENER_ALARMS_ON_CACHED);
- assertEquals(7, mService.mAlarmStore.size());
-
- mListener.handleUidCachedChanged(TEST_CALLING_UID_2, true);
- assertAndHandleMessageSync(REMOVE_EXACT_LISTENER_ALARMS_ON_CACHED);
- assertEquals(6, mService.mAlarmStore.size());
- }
-
- @Test
- public void alarmCountOnListenerCached() {
- mockChangeEnabled(EXACT_LISTENER_ALARMS_DROPPED_ON_CACHED, true);
-
- // Set some alarms for TEST_CALLING_UID.
- final int numExactListenerUid1 = 14;
- for (int i = 0; i < numExactListenerUid1; i++) {
- setTestAlarmWithListener(ALARM_TYPES[i % 4], mNowElapsedTest + i,
- getNewListener(() -> {}));
- }
- setTestAlarmWithListener(RTC, 42, getNewListener(() -> {}), 56, TEST_CALLING_UID);
- setTestAlarm(ELAPSED_REALTIME, 54, getNewMockPendingIntent());
- setTestAlarm(RTC, 49, 154, getNewMockPendingIntent(), 0, 0, TEST_CALLING_UID, null);
-
- // Set some alarms for TEST_CALLING_UID_2.
- final int numExactListenerUid2 = 9;
- for (int i = 0; i < numExactListenerUid2; i++) {
- setTestAlarmWithListener(ALARM_TYPES[i % 4], mNowElapsedTest + i,
- getNewListener(() -> {}), WINDOW_EXACT, TEST_CALLING_UID_2);
- }
- setTestAlarmWithListener(RTC, 412, getNewListener(() -> {}), 561, TEST_CALLING_UID_2);
- setTestAlarm(RTC_WAKEUP, 26, WINDOW_EXACT, getNewMockPendingIntent(), 0, 0,
- TEST_CALLING_UID_2, null);
-
- assertEquals(numExactListenerUid1 + 3, mService.mAlarmsPerUid.get(TEST_CALLING_UID));
- assertEquals(numExactListenerUid2 + 2, mService.mAlarmsPerUid.get(TEST_CALLING_UID_2));
-
- mListener.handleUidCachedChanged(TEST_CALLING_UID, true);
- assertAndHandleMessageSync(REMOVE_EXACT_LISTENER_ALARMS_ON_CACHED);
- assertEquals(3, mService.mAlarmsPerUid.get(TEST_CALLING_UID));
- assertEquals(numExactListenerUid2 + 2, mService.mAlarmsPerUid.get(TEST_CALLING_UID_2));
-
- mListener.handleUidCachedChanged(TEST_CALLING_UID_2, true);
- assertAndHandleMessageSync(REMOVE_EXACT_LISTENER_ALARMS_ON_CACHED);
- assertEquals(3, mService.mAlarmsPerUid.get(TEST_CALLING_UID));
- assertEquals(2, mService.mAlarmsPerUid.get(TEST_CALLING_UID_2));
- }
-
private void executeUidFrozenStateCallback(int[] uids, int[] frozenStates) {
assertNotNull(mUidFrozenStateCallback);
mUidFrozenStateCallback.onUidFrozenStateChanged(uids, frozenStates);
}
- @EnableFlags(Flags.FLAG_USE_FROZEN_STATE_TO_DROP_LISTENER_ALARMS)
@DisableFlags(Flags.FLAG_START_USER_BEFORE_SCHEDULED_ALARMS)
@Test
public void exactListenerAlarmsRemovedOnFrozen() {
@@ -3848,7 +3775,6 @@
assertEquals(6, mService.mAlarmStore.size());
}
- @EnableFlags(Flags.FLAG_USE_FROZEN_STATE_TO_DROP_LISTENER_ALARMS)
@DisableFlags(Flags.FLAG_START_USER_BEFORE_SCHEDULED_ALARMS)
@Test
public void alarmCountOnListenerFrozen() {
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java
index dcbc234..5a872ea 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java
@@ -47,10 +47,8 @@
import static com.android.server.am.ProcessList.NETWORK_STATE_BLOCK;
import static com.android.server.am.ProcessList.NETWORK_STATE_NO_CHANGE;
import static com.android.server.am.ProcessList.NETWORK_STATE_UNBLOCK;
-
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
-
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
@@ -80,6 +78,7 @@
import android.app.ActivityManager;
import android.app.AppGlobals;
import android.app.AppOpsManager;
+import android.app.ApplicationThreadConstants;
import android.app.BackgroundStartPrivileges;
import android.app.BroadcastOptions;
import android.app.ForegroundServiceDelegationOptions;
@@ -87,6 +86,7 @@
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.SyncNotedAppOp;
+import android.app.backup.BackupAnnotations;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
@@ -111,6 +111,7 @@
import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;
+import android.os.UserHandle;
import android.permission.IPermissionManager;
import android.platform.test.annotations.Presubmit;
import android.platform.test.annotations.RequiresFlagsEnabled;
@@ -133,6 +134,7 @@
import com.android.server.am.ProcessList.IsolatedUidRangeAllocator;
import com.android.server.am.UidObserverController.ChangeRecord;
import com.android.server.appop.AppOpsService;
+import com.android.server.job.JobSchedulerInternal;
import com.android.server.notification.NotificationManagerInternal;
import com.android.server.wm.ActivityTaskManagerInternal;
import com.android.server.wm.ActivityTaskManagerService;
@@ -228,6 +230,7 @@
@Mock private PackageManagerInternal mPackageManagerInternal;
@Mock private ActivityTaskManagerInternal mActivityTaskManagerInternal;
@Mock private NotificationManagerInternal mNotificationManagerInternal;
+ @Mock private JobSchedulerInternal mJobSchedulerInternal;
@Mock private ContentResolver mContentResolver;
private TestInjector mInjector;
@@ -249,6 +252,7 @@
LocalServices.addService(PackageManagerInternal.class, mPackageManagerInternal);
LocalServices.addService(ActivityTaskManagerInternal.class, mActivityTaskManagerInternal);
LocalServices.addService(NotificationManagerInternal.class, mNotificationManagerInternal);
+ LocalServices.addService(JobSchedulerInternal.class, mJobSchedulerInternal);
doReturn(new ComponentName("", "")).when(mPackageManagerInternal)
.getSystemUiServiceComponent();
@@ -308,6 +312,7 @@
LocalServices.removeServiceForTest(PackageManagerInternal.class);
LocalServices.removeServiceForTest(ActivityTaskManagerInternal.class);
LocalServices.removeServiceForTest(NotificationManagerInternal.class);
+ LocalServices.removeServiceForTest(JobSchedulerInternal.class);
if (mMockingSession != null) {
mMockingSession.finishMocking();
@@ -1548,6 +1553,50 @@
eq(notificationId), anyInt());
}
+ @SuppressWarnings("GuardedBy")
+ @Test
+ public void bindBackupAgent_fullBackup_shouldUseRestrictedMode_setsInFullBackup()
+ throws Exception {
+ ActivityManagerService spyAms = spy(mAms);
+ ApplicationInfo applicationInfo = new ApplicationInfo();
+ applicationInfo.packageName = TEST_PACKAGE;
+ applicationInfo.processName = TEST_PACKAGE;
+ applicationInfo.uid = TEST_UID;
+ doReturn(applicationInfo).when(mPackageManager).getApplicationInfo(eq(TEST_PACKAGE),
+ anyLong(), anyInt());
+ ProcessRecord appRec = new ProcessRecord(mAms, applicationInfo, TAG, TEST_UID);
+ doReturn(appRec).when(spyAms).getProcessRecordLocked(eq(TEST_PACKAGE), eq(TEST_UID));
+
+ spyAms.bindBackupAgent(TEST_PACKAGE, ApplicationThreadConstants.BACKUP_MODE_FULL,
+ UserHandle.USER_SYSTEM,
+ BackupAnnotations.BackupDestination.CLOUD, /* shouldUseRestrictedMode= */
+ true);
+
+ assertThat(appRec.isInFullBackup()).isTrue();
+ }
+
+ @SuppressWarnings("GuardedBy")
+ @Test
+ public void bindBackupAgent_fullBackup_shouldNotUseRestrictedMode_doesNotSetInFullBackup()
+ throws Exception {
+ ActivityManagerService spyAms = spy(mAms);
+ ApplicationInfo applicationInfo = new ApplicationInfo();
+ applicationInfo.packageName = TEST_PACKAGE;
+ applicationInfo.processName = TEST_PACKAGE;
+ applicationInfo.uid = TEST_UID;
+ doReturn(applicationInfo).when(mPackageManager).getApplicationInfo(eq(TEST_PACKAGE),
+ anyLong(), anyInt());
+ ProcessRecord appRec = new ProcessRecord(mAms, applicationInfo, TAG, TEST_UID);
+ doReturn(appRec).when(spyAms).getProcessRecordLocked(eq(TEST_PACKAGE), eq(TEST_UID));
+
+ spyAms.bindBackupAgent(TEST_PACKAGE, ApplicationThreadConstants.BACKUP_MODE_FULL,
+ UserHandle.USER_SYSTEM,
+ BackupAnnotations.BackupDestination.CLOUD, /* shouldUseRestrictedMode= */
+ false);
+
+ assertThat(appRec.isInFullBackup()).isFalse();
+ }
+
private static class TestHandler extends Handler {
private static final long WAIT_FOR_MSG_TIMEOUT_MS = 4000; // 4 sec
private static final long WAIT_FOR_MSG_INTERVAL_MS = 400; // 0.4 sec
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BaseBroadcastQueueTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BaseBroadcastQueueTest.java
index a1a8b0e..5eb23a2 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BaseBroadcastQueueTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BaseBroadcastQueueTest.java
@@ -58,6 +58,7 @@
import com.android.server.wm.ActivityTaskManagerService;
import org.junit.Rule;
+import org.mockito.ArgumentMatcher;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -185,10 +186,10 @@
doReturn(mAppStartInfoTracker).when(mProcessList).getAppStartInfoTracker();
- doReturn(true).when(mPlatformCompat).isChangeEnabledByUidInternalNoLogging(
- eq(BroadcastFilter.CHANGE_RESTRICT_PRIORITY_VALUES), anyInt());
- doReturn(true).when(mPlatformCompat).isChangeEnabledByUidInternalNoLogging(
- eq(BroadcastRecord.CHANGE_LIMIT_PRIORITY_SCOPE), anyInt());
+ doReturn(true).when(mPlatformCompat).isChangeEnabledInternalNoLogging(
+ eq(BroadcastFilter.RESTRICT_PRIORITY_VALUES), any(ApplicationInfo.class));
+ doReturn(true).when(mPlatformCompat).isChangeEnabledInternalNoLogging(
+ eq(BroadcastRecord.LIMIT_PRIORITY_SCOPE), any(ApplicationInfo.class));
}
public void tearDown() throws Exception {
@@ -298,7 +299,7 @@
filter.setPriority(priority);
final BroadcastFilter res = new BroadcastFilter(filter, receiverList,
receiverList.app.info.packageName, null, null, null, receiverList.uid,
- receiverList.userId, false, false, true,
+ receiverList.userId, false, false, true, receiverList.app.info,
mock(PlatformCompat.class));
receiverList.add(res);
return res;
@@ -308,4 +309,8 @@
app.mOptRecord.setPendingFreeze(pendingFreeze);
app.mOptRecord.setFrozen(frozen);
}
+
+ ArgumentMatcher<ApplicationInfo> appInfoEquals(int uid) {
+ return test -> (test.uid == uid);
+ }
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastFilterTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastFilterTest.java
index e977a7d..5d106ac 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastFilterTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastFilterTest.java
@@ -20,10 +20,13 @@
import static com.google.common.truth.Truth.assertWithMessage;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import android.content.pm.ApplicationInfo;
import android.os.Process;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
@@ -53,14 +56,14 @@
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
+
+ doReturn(true).when(mPlatformCompat).isChangeEnabledInternalNoLogging(
+ eq(BroadcastFilter.RESTRICT_PRIORITY_VALUES), any(ApplicationInfo.class));
}
@Test
@EnableFlags(Flags.FLAG_RESTRICT_PRIORITY_VALUES)
public void testCalculateAdjustedPriority() {
- doReturn(true).when(mPlatformCompat).isChangeEnabledByUidInternalNoLogging(
- eq(BroadcastFilter.CHANGE_RESTRICT_PRIORITY_VALUES), anyInt());
-
{
// Pairs of {initial-priority, expected-adjusted-priority}
final Pair<Integer, Integer>[] priorities = new Pair[] {
@@ -95,8 +98,8 @@
@Test
@EnableFlags(Flags.FLAG_RESTRICT_PRIORITY_VALUES)
public void testCalculateAdjustedPriority_withChangeIdDisabled() {
- doReturn(false).when(mPlatformCompat).isChangeEnabledByUidInternalNoLogging(
- eq(BroadcastFilter.CHANGE_RESTRICT_PRIORITY_VALUES), anyInt());
+ doReturn(false).when(mPlatformCompat).isChangeEnabledInternalNoLogging(
+ eq(BroadcastFilter.RESTRICT_PRIORITY_VALUES), any(ApplicationInfo.class));
{
// Pairs of {initial-priority, expected-adjusted-priority}
@@ -132,9 +135,6 @@
@Test
@DisableFlags(Flags.FLAG_RESTRICT_PRIORITY_VALUES)
public void testCalculateAdjustedPriority_withFlagDisabled() {
- doReturn(true).when(mPlatformCompat).isChangeEnabledByUidInternalNoLogging(
- eq(BroadcastFilter.CHANGE_RESTRICT_PRIORITY_VALUES), anyInt());
-
{
// Pairs of {initial-priority, expected-adjusted-priority}
final Pair<Integer, Integer>[] priorities = new Pair[] {
@@ -170,7 +170,7 @@
@DisableFlags(Flags.FLAG_RESTRICT_PRIORITY_VALUES)
public void testCalculateAdjustedPriority_withFlagDisabled_withChangeIdDisabled() {
doReturn(false).when(mPlatformCompat).isChangeEnabledByUidInternalNoLogging(
- eq(BroadcastFilter.CHANGE_RESTRICT_PRIORITY_VALUES), anyInt());
+ eq(BroadcastFilter.RESTRICT_PRIORITY_VALUES), anyInt());
{
// Pairs of {initial-priority, expected-adjusted-priority}
@@ -215,6 +215,7 @@
final String errorMsg = String.format("owner=%d; actualPriority=%d; expectedPriority=%d",
owningUid, priority, expectedAdjustedPriority);
assertWithMessage(errorMsg).that(BroadcastFilter.calculateAdjustedPriority(
- owningUid, priority, mPlatformCompat)).isEqualTo(expectedAdjustedPriority);
+ owningUid, priority, mock(ApplicationInfo.class), mPlatformCompat))
+ .isEqualTo(expectedAdjustedPriority);
}
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java
index 1481085..82237bc 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java
@@ -47,6 +47,7 @@
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.doAnswer;
@@ -184,7 +185,10 @@
}
private static BroadcastFilter makeMockRegisteredReceiver() {
- return mock(BroadcastFilter.class);
+ final BroadcastFilter filter = mock(BroadcastFilter.class);
+ final ApplicationInfo info = makeApplicationInfo(PACKAGE_ORANGE);
+ doReturn(info).when(filter).getApplicationInfo();
+ return filter;
}
private BroadcastRecord makeBroadcastRecord(Intent intent) {
@@ -716,9 +720,9 @@
@EnableFlags(Flags.FLAG_LIMIT_PRIORITY_SCOPE)
@Test
public void testRunnableAt_Cached_Prioritized_NonDeferrable_changeIdDisabled() {
- doReturn(false).when(mPlatformCompat).isChangeEnabledByUidInternalNoLogging(
- eq(BroadcastRecord.CHANGE_LIMIT_PRIORITY_SCOPE),
- eq(getUidForPackage(PACKAGE_GREEN)));
+ doReturn(false).when(mPlatformCompat).isChangeEnabledInternalNoLogging(
+ eq(BroadcastRecord.LIMIT_PRIORITY_SCOPE),
+ argThat(appInfoEquals(getUidForPackage(PACKAGE_GREEN))));
final List receivers = List.of(
withPriority(makeManifestReceiver(PACKAGE_RED, PACKAGE_RED), 10),
withPriority(makeManifestReceiver(PACKAGE_GREEN, PACKAGE_GREEN), -10));
@@ -1288,9 +1292,9 @@
@SuppressWarnings("GuardedBy")
@Test
public void testDeliveryGroupPolicy_prioritized_diffReceivers_changeIdDisabled() {
- doReturn(false).when(mPlatformCompat).isChangeEnabledByUidInternalNoLogging(
- eq(BroadcastRecord.CHANGE_LIMIT_PRIORITY_SCOPE),
- eq(getUidForPackage(PACKAGE_GREEN)));
+ doReturn(false).when(mPlatformCompat).isChangeEnabledInternalNoLogging(
+ eq(BroadcastRecord.LIMIT_PRIORITY_SCOPE),
+ argThat(appInfoEquals(getUidForPackage(PACKAGE_GREEN))));
final Intent screenOn = new Intent(Intent.ACTION_SCREEN_ON);
final Intent screenOff = new Intent(Intent.ACTION_SCREEN_OFF);
@@ -1823,9 +1827,9 @@
@SuppressWarnings("GuardedBy")
@Test
public void testDeliveryDeferredForCached_changeIdDisabled() throws Exception {
- doReturn(false).when(mPlatformCompat).isChangeEnabledByUidInternalNoLogging(
- eq(BroadcastRecord.CHANGE_LIMIT_PRIORITY_SCOPE),
- eq(getUidForPackage(PACKAGE_GREEN)));
+ doReturn(false).when(mPlatformCompat).isChangeEnabledInternalNoLogging(
+ eq(BroadcastRecord.LIMIT_PRIORITY_SCOPE),
+ argThat(appInfoEquals(getUidForPackage(PACKAGE_GREEN))));
final ProcessRecord greenProcess = makeProcessRecord(makeApplicationInfo(PACKAGE_GREEN));
final ProcessRecord redProcess = makeProcessRecord(makeApplicationInfo(PACKAGE_RED));
@@ -2027,9 +2031,9 @@
@Test
public void testDeliveryDeferredForCached_withInfiniteDeferred_changeIdDisabled()
throws Exception {
- doReturn(false).when(mPlatformCompat).isChangeEnabledByUidInternalNoLogging(
- eq(BroadcastRecord.CHANGE_LIMIT_PRIORITY_SCOPE),
- eq(getUidForPackage(PACKAGE_GREEN)));
+ doReturn(false).when(mPlatformCompat).isChangeEnabledInternalNoLogging(
+ eq(BroadcastRecord.LIMIT_PRIORITY_SCOPE),
+ argThat(appInfoEquals(getUidForPackage(PACKAGE_GREEN))));
final ProcessRecord greenProcess = makeProcessRecord(makeApplicationInfo(PACKAGE_GREEN));
final ProcessRecord redProcess = makeProcessRecord(makeApplicationInfo(PACKAGE_RED));
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
index 9d92d5f..ea80f28 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
@@ -1659,8 +1659,9 @@
final ProcessRecord receiverGreenApp = makeActiveProcessRecord(PACKAGE_GREEN);
final ProcessRecord receiverYellowApp = makeActiveProcessRecord(PACKAGE_YELLOW);
- doReturn(false).when(mPlatformCompat).isChangeEnabledByUidInternalNoLogging(
- eq(BroadcastRecord.CHANGE_LIMIT_PRIORITY_SCOPE), eq(receiverBlueApp.uid));
+ doReturn(false).when(mPlatformCompat).isChangeEnabledInternalNoLogging(
+ eq(BroadcastRecord.LIMIT_PRIORITY_SCOPE),
+ argThat(appInfoEquals(receiverBlueApp.uid)));
// Enqueue a normal broadcast that will go to several processes, and
// then enqueue a foreground broadcast that risks reordering
@@ -2471,8 +2472,9 @@
final ProcessRecord receiverBlueApp = makeActiveProcessRecord(PACKAGE_BLUE);
final ProcessRecord receiverGreenApp = makeActiveProcessRecord(PACKAGE_GREEN);
- doReturn(false).when(mPlatformCompat).isChangeEnabledByUidInternalNoLogging(
- eq(BroadcastRecord.CHANGE_LIMIT_PRIORITY_SCOPE), eq(receiverBlueApp.uid));
+ doReturn(false).when(mPlatformCompat).isChangeEnabledInternalNoLogging(
+ eq(BroadcastRecord.LIMIT_PRIORITY_SCOPE),
+ argThat(appInfoEquals(receiverBlueApp.uid)));
mUidObserver.onUidStateChanged(receiverGreenApp.info.uid,
ActivityManager.PROCESS_STATE_TOP, 0, ActivityManager.PROCESS_CAPABILITY_NONE);
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastRecordTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastRecordTest.java
index a424bfdb..8482fd6 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastRecordTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastRecordTest.java
@@ -19,12 +19,12 @@
import static android.app.ActivityManager.PROCESS_STATE_UNKNOWN;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
-import static com.android.server.am.BroadcastRecord.CHANGE_LIMIT_PRIORITY_SCOPE;
import static com.android.server.am.BroadcastRecord.DELIVERY_DEFERRED;
import static com.android.server.am.BroadcastRecord.DELIVERY_DELIVERED;
import static com.android.server.am.BroadcastRecord.DELIVERY_PENDING;
import static com.android.server.am.BroadcastRecord.DELIVERY_SKIPPED;
import static com.android.server.am.BroadcastRecord.DELIVERY_TIMEOUT;
+import static com.android.server.am.BroadcastRecord.LIMIT_PRIORITY_SCOPE;
import static com.android.server.am.BroadcastRecord.calculateBlockedUntilBeyondCount;
import static com.android.server.am.BroadcastRecord.calculateDeferUntilActive;
import static com.android.server.am.BroadcastRecord.calculateUrgent;
@@ -35,7 +35,8 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
import android.app.BackgroundStartPrivileges;
@@ -63,6 +64,7 @@
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentMatcher;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.junit.MockitoJUnitRunner;
@@ -108,8 +110,8 @@
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- doReturn(true).when(mPlatformCompat).isChangeEnabledByUidInternalNoLogging(
- eq(BroadcastRecord.CHANGE_LIMIT_PRIORITY_SCOPE), anyInt());
+ doReturn(true).when(mPlatformCompat).isChangeEnabledInternalNoLogging(
+ eq(BroadcastRecord.LIMIT_PRIORITY_SCOPE), any(ApplicationInfo.class));
}
@Test
@@ -222,8 +224,8 @@
@EnableFlags(Flags.FLAG_LIMIT_PRIORITY_SCOPE)
@Test
public void testIsPrioritized_withDifferentPriorities_withFirstUidChangeIdDisabled() {
- doReturn(false).when(mPlatformCompat).isChangeEnabledByUidInternalNoLogging(
- eq(BroadcastRecord.CHANGE_LIMIT_PRIORITY_SCOPE), eq(getAppId(1)));
+ doReturn(false).when(mPlatformCompat).isChangeEnabledInternalNoLogging(
+ eq(BroadcastRecord.LIMIT_PRIORITY_SCOPE), argThat(appInfoEquals(getAppId(1))));
assertTrue(isPrioritized(List.of(
createResolveInfo(PACKAGE1, getAppId(1), 10),
@@ -256,8 +258,8 @@
@EnableFlags(Flags.FLAG_LIMIT_PRIORITY_SCOPE)
@Test
public void testIsPrioritized_withDifferentPriorities_withLastUidChangeIdDisabled() {
- doReturn(false).when(mPlatformCompat).isChangeEnabledByUidInternalNoLogging(
- eq(BroadcastRecord.CHANGE_LIMIT_PRIORITY_SCOPE), eq(getAppId(3)));
+ doReturn(false).when(mPlatformCompat).isChangeEnabledInternalNoLogging(
+ eq(BroadcastRecord.LIMIT_PRIORITY_SCOPE), argThat(appInfoEquals(getAppId(3))));
assertTrue(isPrioritized(List.of(
createResolveInfo(PACKAGE1, getAppId(1), 10),
@@ -294,8 +296,8 @@
@EnableFlags(Flags.FLAG_LIMIT_PRIORITY_SCOPE)
@Test
public void testIsPrioritized_withDifferentPriorities_withUidChangeIdDisabled() {
- doReturn(false).when(mPlatformCompat).isChangeEnabledByUidInternalNoLogging(
- eq(BroadcastRecord.CHANGE_LIMIT_PRIORITY_SCOPE), eq(getAppId(2)));
+ doReturn(false).when(mPlatformCompat).isChangeEnabledInternalNoLogging(
+ eq(BroadcastRecord.LIMIT_PRIORITY_SCOPE), argThat(appInfoEquals(getAppId(2))));
assertTrue(isPrioritized(List.of(
createResolveInfo(PACKAGE1, getAppId(1), 10),
@@ -328,10 +330,10 @@
@EnableFlags(Flags.FLAG_LIMIT_PRIORITY_SCOPE)
@Test
public void testIsPrioritized_withDifferentPriorities_withMultipleUidChangeIdDisabled() {
- doReturn(false).when(mPlatformCompat).isChangeEnabledByUidInternalNoLogging(
- eq(BroadcastRecord.CHANGE_LIMIT_PRIORITY_SCOPE), eq(getAppId(1)));
- doReturn(false).when(mPlatformCompat).isChangeEnabledByUidInternalNoLogging(
- eq(BroadcastRecord.CHANGE_LIMIT_PRIORITY_SCOPE), eq(getAppId(2)));
+ doReturn(false).when(mPlatformCompat).isChangeEnabledInternalNoLogging(
+ eq(BroadcastRecord.LIMIT_PRIORITY_SCOPE), argThat(appInfoEquals(getAppId(1))));
+ doReturn(false).when(mPlatformCompat).isChangeEnabledInternalNoLogging(
+ eq(BroadcastRecord.LIMIT_PRIORITY_SCOPE), argThat(appInfoEquals(getAppId(2))));
assertTrue(isPrioritized(List.of(
createResolveInfo(PACKAGE1, getAppId(1), 10),
@@ -362,10 +364,10 @@
assertArrayEquals(new int[] {0, 0, 1, 1, 3},
calculateBlockedUntilBeyondCount(List.of(
createResolveInfo(PACKAGE1, getAppId(1), 20),
- createResolveInfo(PACKAGE2, getAppId(3), 20),
+ createResolveInfo(PACKAGE3, getAppId(3), 20),
createResolveInfo(PACKAGE3, getAppId(3), 10),
createResolveInfo(PACKAGE3, getAppId(3), 0),
- createResolveInfo(PACKAGE3, getAppId(2), 0)), false, mPlatformCompat));
+ createResolveInfo(PACKAGE2, getAppId(2), 0)), false, mPlatformCompat));
}
@Test
@@ -592,8 +594,8 @@
@EnableFlags(Flags.FLAG_LIMIT_PRIORITY_SCOPE)
@Test
public void testSetDeliveryState_DeferUntilActive_changeIdDisabled() {
- doReturn(false).when(mPlatformCompat).isChangeEnabledByUidInternalNoLogging(
- eq(BroadcastRecord.CHANGE_LIMIT_PRIORITY_SCOPE), eq(getAppId(1)));
+ doReturn(false).when(mPlatformCompat).isChangeEnabledInternalNoLogging(
+ eq(BroadcastRecord.LIMIT_PRIORITY_SCOPE), argThat(appInfoEquals(getAppId(1))));
final BroadcastRecord r = createBroadcastRecord(
new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED), List.of(
createResolveInfo(PACKAGE1, getAppId(1), 10),
@@ -960,8 +962,8 @@
createResolveInfo(PACKAGE2, getAppId(2)),
createResolveInfo(PACKAGE3, getAppId(3)))));
- doReturn(false).when(mPlatformCompat).isChangeEnabledByUidInternalNoLogging(
- eq(BroadcastRecord.CHANGE_LIMIT_PRIORITY_SCOPE), eq(getAppId(1)));
+ doReturn(false).when(mPlatformCompat).isChangeEnabledInternalNoLogging(
+ eq(BroadcastRecord.LIMIT_PRIORITY_SCOPE), argThat(appInfoEquals(getAppId(1))));
assertArrayEquals(new boolean[] {false, true, true}, calculateChangeState(
List.of(createResolveInfo(PACKAGE1, getAppId(1)),
createResolveInfo(PACKAGE2, getAppId(2)),
@@ -969,11 +971,11 @@
assertArrayEquals(new boolean[] {false, true, false, true}, calculateChangeState(
List.of(createResolveInfo(PACKAGE1, getAppId(1)),
createResolveInfo(PACKAGE2, getAppId(2)),
- createResolveInfo(PACKAGE2, getAppId(1)),
+ createResolveInfo(PACKAGE1, getAppId(1)),
createResolveInfo(PACKAGE3, getAppId(3)))));
- doReturn(false).when(mPlatformCompat).isChangeEnabledByUidInternalNoLogging(
- eq(BroadcastRecord.CHANGE_LIMIT_PRIORITY_SCOPE), eq(getAppId(2)));
+ doReturn(false).when(mPlatformCompat).isChangeEnabledInternalNoLogging(
+ eq(BroadcastRecord.LIMIT_PRIORITY_SCOPE), argThat(appInfoEquals(getAppId(2))));
assertArrayEquals(new boolean[] {false, false, true}, calculateChangeState(
List.of(createResolveInfo(PACKAGE1, getAppId(1)),
createResolveInfo(PACKAGE2, getAppId(2)),
@@ -987,8 +989,8 @@
createResolveInfo(PACKAGE2, getAppId(2)),
createResolveInfo(PACKAGE3, getAppId(3)))));
- doReturn(false).when(mPlatformCompat).isChangeEnabledByUidInternalNoLogging(
- eq(BroadcastRecord.CHANGE_LIMIT_PRIORITY_SCOPE), eq(getAppId(3)));
+ doReturn(false).when(mPlatformCompat).isChangeEnabledInternalNoLogging(
+ eq(BroadcastRecord.LIMIT_PRIORITY_SCOPE), argThat(appInfoEquals(getAppId(3))));
assertArrayEquals(new boolean[] {false, false, false}, calculateChangeState(
List.of(createResolveInfo(PACKAGE1, getAppId(1)),
createResolveInfo(PACKAGE2, getAppId(2)),
@@ -998,14 +1000,14 @@
List.of(createResolveInfo(PACKAGE1, getAppId(1)),
createResolveInfo(PACKAGE3, getAppId(3)),
createResolveInfo(PACKAGE2, getAppId(2)),
- createResolveInfo(PACKAGE2, getAppId(1)),
+ createResolveInfo(PACKAGE1, getAppId(1)),
createResolveInfo(PACKAGE2, getAppId(2)),
createResolveInfo(PACKAGE3, getAppId(3)))));
}
private boolean[] calculateChangeState(List<Object> receivers) {
return BroadcastRecord.calculateChangeStateForReceivers(receivers,
- CHANGE_LIMIT_PRIORITY_SCOPE, mPlatformCompat);
+ LIMIT_PRIORITY_SCOPE, mPlatformCompat);
}
private static void cleanupDisabledPackageReceivers(BroadcastRecord record,
@@ -1185,4 +1187,8 @@
assertEquals("deferred", expectedDeferredCount, r.deferredCount);
assertEquals("beyond", expectedBeyondCount, r.beyondCount);
}
+
+ private ArgumentMatcher<ApplicationInfo> appInfoEquals(int uid) {
+ return test -> (test.uid == uid);
+ }
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
index f82a860..a9569b4 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
@@ -1427,6 +1427,31 @@
@SuppressWarnings("GuardedBy")
@Test
+ public void testUpdateOomAdj_DoOne_Service_NotPerceptible_AboveClient() {
+ ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
+ MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
+ ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
+ MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
+ ProcessRecord service = spy(makeDefaultProcessRecord(MOCKAPP3_PID, MOCKAPP3_UID,
+ MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
+ bindService(app, client, null, null, Context.BIND_NOT_PERCEPTIBLE, mock(IBinder.class));
+ bindService(service, app, null, null, Context.BIND_ABOVE_CLIENT, mock(IBinder.class));
+ mProcessStateController.setRunningRemoteAnimation(client, true);
+ mProcessStateController.updateHasAboveClientLocked(app.mServices);
+ setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ updateOomAdj(client, app, service);
+
+ final int expectedAdj;
+ if (Flags.addModifyRawOomAdjServiceLevel()) {
+ expectedAdj = SERVICE_ADJ;
+ } else {
+ expectedAdj = CACHED_APP_MIN_ADJ;
+ }
+ assertEquals(expectedAdj, app.mState.getSetAdj());
+ }
+
+ @SuppressWarnings("GuardedBy")
+ @Test
public void testUpdateOomAdj_DoOne_Service_NotVisible() {
ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
@@ -2906,7 +2931,7 @@
// Simulate binding to a service in the same process using BIND_ABOVE_CLIENT and
// verify that its OOM adjustment level is unaffected.
bindService(service, app, null, null, Context.BIND_ABOVE_CLIENT, mock(IBinder.class));
- app.mServices.updateHasAboveClientLocked();
+ mProcessStateController.updateHasAboveClientLocked(app.mServices);
assertTrue(app.mServices.hasAboveClient());
updateOomAdj(app);
@@ -2928,7 +2953,7 @@
// Simulate binding to a service in the same process using BIND_ABOVE_CLIENT and
// verify that its OOM adjustment level is unaffected.
bindService(app, app, null, null, Context.BIND_ABOVE_CLIENT, mock(IBinder.class));
- app.mServices.updateHasAboveClientLocked();
+ mProcessStateController.updateHasAboveClientLocked(app.mServices);
assertFalse(app.mServices.hasAboveClient());
updateOomAdj(app);
@@ -2983,7 +3008,7 @@
// Since sr.app is null, this service cannot be in the same process as the
// client so we expect the BIND_ABOVE_CLIENT adjustment to take effect.
- app.mServices.updateHasAboveClientLocked();
+ mProcessStateController.updateHasAboveClientLocked(app.mServices);
updateOomAdj(app);
assertTrue(app.mServices.hasAboveClient());
assertNotEquals(FOREGROUND_APP_ADJ, app.mState.getSetAdj());
@@ -3306,7 +3331,7 @@
if (Flags.pushGlobalStateToOomadjuster()) {
mProcessStateController.setBackupTarget(app, app.userId);
} else {
- BackupRecord backupTarget = new BackupRecord(null, 0, 0, 0);
+ BackupRecord backupTarget = new BackupRecord(null, 0, 0, 0, true);
backupTarget.app = app;
doReturn(backupTarget).when(mService.mBackupTargets).get(anyInt());
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/backup/BackupAgentConnectionManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/backup/BackupAgentConnectionManagerTest.java
new file mode 100644
index 0000000..19e43b6
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/backup/BackupAgentConnectionManagerTest.java
@@ -0,0 +1,381 @@
+/*
+ * 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.server.backup;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+
+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.reset;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.ActivityManager;
+import android.app.ActivityManagerInternal;
+import android.app.ApplicationThreadConstants;
+import android.app.IActivityManager;
+import android.app.IBackupAgent;
+import android.app.backup.BackupAnnotations.BackupDestination;
+import android.app.backup.BackupAnnotations.OperationType;
+import android.compat.testing.PlatformCompatChangeRule;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.os.Process;
+import android.os.UserHandle;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.annotations.Presubmit;
+import android.platform.test.flag.junit.SetFlagsRule;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.LocalServices;
+import com.android.server.backup.internal.LifecycleOperationStorage;
+
+import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges;
+import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
+
+import com.google.common.collect.ImmutableSet;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestRule;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.MockitoSession;
+import org.mockito.quality.Strictness;
+
+import java.util.Set;
+
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class BackupAgentConnectionManagerTest {
+ private static final String TEST_PACKAGE = "com.test.package";
+
+ @Rule
+ public TestRule compatChangeRule = new PlatformCompatChangeRule();
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
+ @Mock
+ IActivityManager mActivityManager;
+ @Mock
+ ActivityManagerInternal mActivityManagerInternal;
+ @Mock
+ LifecycleOperationStorage mOperationStorage;
+ @Mock
+ UserBackupManagerService mUserBackupManagerService;
+ @Mock
+ IBackupAgent.Stub mBackupAgentStub;
+ @Mock
+ PackageManager mPackageManager;
+
+ private BackupAgentConnectionManager mConnectionManager;
+ private MockitoSession mSession;
+ private ApplicationInfo mTestApplicationInfo;
+ private IBackupAgent mBackupAgentResult;
+ private Thread mTestThread;
+
+ @Before
+ public void setUp() throws Exception {
+ mSession = mockitoSession().initMocks(this).mockStatic(ActivityManager.class).mockStatic(
+ LocalServices.class).strictness(Strictness.LENIENT).startMocking();
+ MockitoAnnotations.initMocks(this);
+
+ doReturn(mActivityManager).when(ActivityManager::getService);
+ doReturn(mActivityManagerInternal).when(
+ () -> LocalServices.getService(ActivityManagerInternal.class));
+ // Real package manager throws if a property is not defined.
+ when(mPackageManager.getPropertyAsUser(any(), any(), any(), anyInt())).thenThrow(
+ new PackageManager.NameNotFoundException());
+
+ mConnectionManager = spy(
+ new BackupAgentConnectionManager(mOperationStorage, mPackageManager,
+ mUserBackupManagerService, UserHandle.USER_SYSTEM));
+
+ mTestApplicationInfo = new ApplicationInfo();
+ mTestApplicationInfo.packageName = TEST_PACKAGE;
+
+ mBackupAgentResult = null;
+ mTestThread = null;
+ }
+
+ @After
+ public void tearDown() {
+ if (mSession != null) {
+ mSession.finishMocking();
+ }
+ }
+
+ @Test
+ public void bindToAgentSynchronous_amReturnsFailure_returnsNullAndClearsPendingBackups()
+ throws Exception {
+ when(mActivityManager.bindBackupAgent(eq(TEST_PACKAGE), anyInt(), anyInt(),
+ anyInt(), anyBoolean())).thenReturn(false);
+
+ IBackupAgent result = mConnectionManager.bindToAgentSynchronous(mTestApplicationInfo,
+ ApplicationThreadConstants.BACKUP_MODE_FULL, BackupDestination.CLOUD);
+
+ assertThat(result).isNull();
+ verify(mActivityManagerInternal).clearPendingBackup(UserHandle.USER_SYSTEM);
+ }
+
+ @Test
+ public void bindToAgentSynchronous_agentDisconnectedCalled_returnsNullAndClearsPendingBackups()
+ throws Exception {
+ when(mActivityManager.bindBackupAgent(eq(TEST_PACKAGE), anyInt(), anyInt(),
+ anyInt(), anyBoolean())).thenReturn(true);
+ // This is so that IBackupAgent.Stub.asInterface() works.
+ when(mBackupAgentStub.queryLocalInterface(any())).thenReturn(mBackupAgentStub);
+ when(mConnectionManager.getCallingUid()).thenReturn(Process.SYSTEM_UID);
+
+ // This is going to block until it receives the callback so we need to run it on a
+ // separate thread.
+ Thread testThread = new Thread(() -> setBackupAgentResult(
+ mConnectionManager.bindToAgentSynchronous(mTestApplicationInfo,
+ ApplicationThreadConstants.BACKUP_MODE_FULL, BackupDestination.CLOUD)),
+ "backup-agent-connection-manager-test");
+ testThread.start();
+ // Give the testThread a head start, otherwise agentConnected() might run before
+ // bindToAgentSynchronous() is called.
+ Thread.sleep(500);
+ mConnectionManager.agentDisconnected(TEST_PACKAGE);
+ testThread.join();
+
+ assertThat(mBackupAgentResult).isNull();
+ verify(mActivityManagerInternal).clearPendingBackup(UserHandle.USER_SYSTEM);
+ }
+
+ @Test
+ public void bindToAgentSynchronous_agentConnectedCalled_returnsBackupAgent() throws Exception {
+ when(mActivityManager.bindBackupAgent(eq(TEST_PACKAGE), anyInt(), anyInt(),
+ anyInt(), anyBoolean())).thenReturn(true);
+ // This is so that IBackupAgent.Stub.asInterface() works.
+ when(mBackupAgentStub.queryLocalInterface(any())).thenReturn(mBackupAgentStub);
+ when(mConnectionManager.getCallingUid()).thenReturn(Process.SYSTEM_UID);
+
+ // This is going to block until it receives the callback so we need to run it on a
+ // separate thread.
+ Thread testThread = new Thread(() -> setBackupAgentResult(
+ mConnectionManager.bindToAgentSynchronous(mTestApplicationInfo,
+ ApplicationThreadConstants.BACKUP_MODE_FULL, BackupDestination.CLOUD)),
+ "backup-agent-connection-manager-test");
+ testThread.start();
+ // Give the testThread a head start, otherwise agentConnected() might run before
+ // bindToAgentSynchronous() is called.
+ Thread.sleep(500);
+ mConnectionManager.agentConnected(TEST_PACKAGE, mBackupAgentStub);
+ testThread.join();
+
+ assertThat(mBackupAgentResult).isEqualTo(mBackupAgentStub);
+ verify(mActivityManagerInternal, never()).clearPendingBackup(anyInt());
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_ENABLE_RESTRICTED_MODE_CHANGES)
+ public void bindToAgentSynchronous_restrictedModeChangesFlagOff_shouldUseRestrictedMode()
+ throws Exception {
+ mConnectionManager.bindToAgentSynchronous(mTestApplicationInfo,
+ ApplicationThreadConstants.BACKUP_MODE_FULL, BackupDestination.CLOUD);
+
+ verify(mActivityManager).bindBackupAgent(eq(TEST_PACKAGE), anyInt(), anyInt(), anyInt(),
+ /* useRestrictedMode= */ eq(true));
+ // Make sure we never hit the code that checks the property.
+ verify(mPackageManager, never()).getPropertyAsUser(any(), any(), any(), anyInt());
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_RESTRICTED_MODE_CHANGES)
+ public void bindToAgentSynchronous_keyValueBackup_shouldNotUseRestrictedMode()
+ throws Exception {
+ mConnectionManager.bindToAgentSynchronous(mTestApplicationInfo,
+ ApplicationThreadConstants.BACKUP_MODE_INCREMENTAL, BackupDestination.CLOUD);
+
+ verify(mActivityManager).bindBackupAgent(eq(TEST_PACKAGE), anyInt(), anyInt(), anyInt(),
+ /* useRestrictedMode= */ eq(false));
+ // Make sure we never hit the code that checks the property.
+ verify(mPackageManager, never()).getPropertyAsUser(any(), any(), any(), anyInt());
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_RESTRICTED_MODE_CHANGES)
+ public void bindToAgentSynchronous_keyValueRestore_shouldNotUseRestrictedMode()
+ throws Exception {
+ mConnectionManager.bindToAgentSynchronous(mTestApplicationInfo,
+ ApplicationThreadConstants.BACKUP_MODE_RESTORE, BackupDestination.CLOUD);
+
+ verify(mActivityManager).bindBackupAgent(eq(TEST_PACKAGE), anyInt(), anyInt(), anyInt(),
+ /* useRestrictedMode= */ eq(false));
+ // Make sure we never hit the code that checks the property.
+ verify(mPackageManager, never()).getPropertyAsUser(any(), any(), any(), anyInt());
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_RESTRICTED_MODE_CHANGES)
+ public void bindToAgentSynchronous_packageOptedIn_shouldUseRestrictedMode() throws Exception {
+ reset(mPackageManager);
+ when(mPackageManager.getPropertyAsUser(
+ eq(PackageManager.PROPERTY_USE_RESTRICTED_BACKUP_MODE), eq(TEST_PACKAGE), any(),
+ anyInt())).thenReturn(new PackageManager.Property(
+ PackageManager.PROPERTY_USE_RESTRICTED_BACKUP_MODE, /* value= */ true,
+ TEST_PACKAGE, /* className= */ null));
+
+ mConnectionManager.bindToAgentSynchronous(mTestApplicationInfo,
+ ApplicationThreadConstants.BACKUP_MODE_FULL, BackupDestination.CLOUD);
+
+ verify(mActivityManager).bindBackupAgent(eq(TEST_PACKAGE), anyInt(), anyInt(), anyInt(),
+ /* useRestrictedMode= */ eq(true));
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_RESTRICTED_MODE_CHANGES)
+ public void bindToAgentSynchronous_packageOptedOut_shouldNotUseRestrictedMode()
+ throws Exception {
+ reset(mPackageManager);
+ when(mPackageManager.getPropertyAsUser(
+ eq(PackageManager.PROPERTY_USE_RESTRICTED_BACKUP_MODE), eq(TEST_PACKAGE), any(),
+ anyInt())).thenReturn(new PackageManager.Property(
+ PackageManager.PROPERTY_USE_RESTRICTED_BACKUP_MODE, /* value= */ false,
+ TEST_PACKAGE, /* className= */ null));
+
+ mConnectionManager.bindToAgentSynchronous(mTestApplicationInfo,
+ ApplicationThreadConstants.BACKUP_MODE_FULL, BackupDestination.CLOUD);
+
+ verify(mActivityManager).bindBackupAgent(eq(TEST_PACKAGE), anyInt(), anyInt(), anyInt(),
+ /* useRestrictedMode= */ eq(false));
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_RESTRICTED_MODE_CHANGES)
+ @DisableCompatChanges({BackupAgentConnectionManager.OS_DECIDES_BACKUP_RESTRICTED_MODE})
+ public void bindToAgentSynchronous_targetSdkBelowB_shouldUseRestrictedMode() throws Exception {
+ reset(mPackageManager);
+ // Mock that the app has not explicitly set the property.
+ when(mPackageManager.getPropertyAsUser(
+ eq(PackageManager.PROPERTY_USE_RESTRICTED_BACKUP_MODE), eq(TEST_PACKAGE), any(),
+ anyInt())).thenThrow(new PackageManager.NameNotFoundException());
+
+ mConnectionManager.bindToAgentSynchronous(mTestApplicationInfo,
+ ApplicationThreadConstants.BACKUP_MODE_FULL, BackupDestination.CLOUD);
+
+ verify(mActivityManager).bindBackupAgent(eq(TEST_PACKAGE), anyInt(), anyInt(), anyInt(),
+ /* useRestrictedMode= */ eq(true));
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_RESTRICTED_MODE_CHANGES)
+ @EnableCompatChanges({BackupAgentConnectionManager.OS_DECIDES_BACKUP_RESTRICTED_MODE})
+ public void bindToAgentSynchronous_targetSdkB_notInList_shouldUseRestrictedMode()
+ throws Exception {
+ reset(mPackageManager);
+ // Mock that the app has not explicitly set the property.
+ when(mPackageManager.getPropertyAsUser(
+ eq(PackageManager.PROPERTY_USE_RESTRICTED_BACKUP_MODE), eq(TEST_PACKAGE), any(),
+ anyInt())).thenThrow(new PackageManager.NameNotFoundException());
+ mConnectionManager.clearNoRestrictedModePackages();
+
+ mConnectionManager.bindToAgentSynchronous(mTestApplicationInfo,
+ ApplicationThreadConstants.BACKUP_MODE_FULL, BackupDestination.CLOUD);
+
+ verify(mActivityManager).bindBackupAgent(eq(TEST_PACKAGE), anyInt(), anyInt(), anyInt(),
+ /* useRestrictedMode= */ eq(true));
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_RESTRICTED_MODE_CHANGES)
+ @EnableCompatChanges({BackupAgentConnectionManager.OS_DECIDES_BACKUP_RESTRICTED_MODE})
+ public void bindToAgentSynchronous_forRestore_targetSdkB_inList_shouldNotUseRestrictedMode()
+ throws Exception {
+ reset(mPackageManager);
+ // Mock that the app has not explicitly set the property.
+ when(mPackageManager.getPropertyAsUser(
+ eq(PackageManager.PROPERTY_USE_RESTRICTED_BACKUP_MODE), eq(TEST_PACKAGE), any(),
+ anyInt())).thenThrow(new PackageManager.NameNotFoundException());
+ mConnectionManager.setNoRestrictedModePackages(Set.of(TEST_PACKAGE), OperationType.RESTORE);
+
+ mConnectionManager.bindToAgentSynchronous(mTestApplicationInfo,
+ ApplicationThreadConstants.BACKUP_MODE_RESTORE_FULL, BackupDestination.CLOUD);
+
+ verify(mActivityManager).bindBackupAgent(eq(TEST_PACKAGE), anyInt(), anyInt(), anyInt(),
+ /* useRestrictedMode= */ eq(false));
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_RESTRICTED_MODE_CHANGES)
+ @EnableCompatChanges({BackupAgentConnectionManager.OS_DECIDES_BACKUP_RESTRICTED_MODE})
+ public void bindToAgentSynchronous_forBackup_targetSdkB_inList_shouldNotUseRestrictedMode()
+ throws Exception {
+ reset(mPackageManager);
+ // Mock that the app has not explicitly set the property.
+ when(mPackageManager.getPropertyAsUser(
+ eq(PackageManager.PROPERTY_USE_RESTRICTED_BACKUP_MODE), eq(TEST_PACKAGE), any(),
+ anyInt())).thenThrow(new PackageManager.NameNotFoundException());
+ mConnectionManager.setNoRestrictedModePackages(Set.of(TEST_PACKAGE), OperationType.BACKUP);
+
+ mConnectionManager.bindToAgentSynchronous(mTestApplicationInfo,
+ ApplicationThreadConstants.BACKUP_MODE_FULL, BackupDestination.CLOUD);
+
+ verify(mActivityManager).bindBackupAgent(eq(TEST_PACKAGE), anyInt(), anyInt(), anyInt(),
+ /* useRestrictedMode= */ eq(false));
+ }
+
+ @Test
+ public void agentDisconnected_cancelsCurrentOperations() throws Exception {
+ when(mOperationStorage.operationTokensForPackage(eq(TEST_PACKAGE))).thenReturn(
+ ImmutableSet.of(123, 456, 789));
+ when(mConnectionManager.getThreadForCancellation(any())).thenAnswer(invocation -> {
+ Thread testThread = new Thread((Runnable) invocation.getArgument(0),
+ "agent-disconnected-test");
+ setTestThread(testThread);
+ return testThread;
+ });
+
+ mConnectionManager.agentDisconnected(TEST_PACKAGE);
+
+ mTestThread.join();
+ verify(mUserBackupManagerService).handleCancel(eq(123), eq(true));
+ verify(mUserBackupManagerService).handleCancel(eq(456), eq(true));
+ verify(mUserBackupManagerService).handleCancel(eq(789), eq(true));
+ }
+
+ @Test
+ public void unbindAgent_callsAmUnbindBackupAgent() throws Exception {
+ mConnectionManager.unbindAgent(mTestApplicationInfo);
+
+ verify(mActivityManager).unbindBackupAgent(eq(mTestApplicationInfo));
+ }
+
+ // Needed because variables can't be assigned directly inside lambdas in Java.
+ private void setBackupAgentResult(IBackupAgent result) {
+ mBackupAgentResult = result;
+ }
+
+ // Needed because variables can't be assigned directly inside lambdas in Java.
+ private void setTestThread(Thread thread) {
+ mTestThread = thread;
+ }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/backup/UserBackupManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/backup/UserBackupManagerServiceTest.java
index 65286d9..7a9e96f 100644
--- a/services/tests/mockingservicestests/src/com/android/server/backup/UserBackupManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/backup/UserBackupManagerServiceTest.java
@@ -21,38 +21,38 @@
import static com.google.common.truth.Truth.assertThat;
-import static org.junit.Assert.fail;
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.argThat;
import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.never;
import static org.mockito.Mockito.when;
import android.annotation.UserIdInt;
+import android.app.ActivityManagerInternal;
+import android.app.IActivityManager;
import android.app.backup.BackupAgent;
-import android.app.backup.BackupAnnotations;
import android.app.backup.BackupAnnotations.BackupDestination;
+import android.app.backup.BackupAnnotations.OperationType;
import android.app.backup.BackupRestoreEventLogger.DataTypeResult;
import android.app.backup.IBackupManagerMonitor;
import android.app.backup.IBackupObserver;
import android.app.job.JobInfo;
import android.app.job.JobScheduler;
+import android.compat.testing.PlatformCompatChangeRule;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Handler;
import android.platform.test.annotations.Presubmit;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
import android.testing.TestableContext;
import android.util.FeatureFlagUtils;
import android.util.KeyValueListParser;
import androidx.test.core.app.ApplicationProvider;
-import androidx.test.filters.FlakyTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.server.backup.internal.BackupHandler;
@@ -64,11 +64,11 @@
import com.android.server.backup.utils.BackupEligibilityRules;
import com.android.server.backup.utils.BackupManagerMonitorEventSender;
-import com.google.common.collect.ImmutableSet;
-
import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.TestRule;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -77,7 +77,6 @@
import java.util.Arrays;
import java.util.List;
-import java.util.function.IntConsumer;
@Presubmit
@RunWith(AndroidJUnit4.class)
@@ -85,9 +84,13 @@
private static final String TEST_PACKAGE = "package1";
private static final String[] TEST_PACKAGES = new String[] { TEST_PACKAGE };
private static final String TEST_TRANSPORT = "transport";
- private static final int WORKER_THREAD_TIMEOUT_MILLISECONDS = 100;
@UserIdInt private static final int USER_ID = 0;
+ @Rule
+ public TestRule compatChangeRule = new PlatformCompatChangeRule();
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
@Mock IBackupManagerMonitor mBackupManagerMonitor;
@Mock IBackupObserver mBackupObserver;
@Mock PackageManager mPackageManager;
@@ -99,10 +102,14 @@
@Mock JobScheduler mJobScheduler;
@Mock BackupHandler mBackupHandler;
@Mock BackupManagerMonitorEventSender mBackupManagerMonitorEventSender;
+ @Mock IActivityManager mActivityManager;
+ @Mock
+ ActivityManagerInternal mActivityManagerInternal;
private TestableContext mContext;
private MockitoSession mSession;
private TestBackupService mService;
+ private ApplicationInfo mTestPackageApplicationInfo;
@Before
public void setUp() throws Exception {
@@ -120,12 +127,14 @@
mContext.getTestablePermissions().setPermission(android.Manifest.permission.BACKUP,
PackageManager.PERMISSION_GRANTED);
- mService = new TestBackupService(mContext, mPackageManager, mOperationStorage,
- mTransportManager, mBackupHandler);
+ mService = new TestBackupService();
mService.setEnabled(true);
mService.setSetupComplete(true);
mService.enqueueFullBackup("com.test.backup.app", /* lastBackedUp= */ 0);
- }
+
+ mTestPackageApplicationInfo = new ApplicationInfo();
+ mTestPackageApplicationInfo.packageName = TEST_PACKAGE;
+ }
@After
public void tearDown() {
@@ -256,33 +265,6 @@
}
@Test
- @FlakyTest
- public void testAgentDisconnected_cancelsCurrentOperations() throws Exception {
- when(mOperationStorage.operationTokensForPackage(eq("com.android.foo"))).thenReturn(
- ImmutableSet.of(123, 456, 789)
- );
-
- mService.agentDisconnected("com.android.foo");
-
- mService.waitForAsyncOperation();
- verify(mOperationStorage).cancelOperation(eq(123), eq(true), any(IntConsumer.class));
- verify(mOperationStorage).cancelOperation(eq(456), eq(true), any());
- verify(mOperationStorage).cancelOperation(eq(789), eq(true), any());
- }
-
- @Test
- public void testAgentDisconnected_unknownPackageName_cancelsNothing() throws Exception {
- when(mOperationStorage.operationTokensForPackage(eq("com.android.foo"))).thenReturn(
- ImmutableSet.of()
- );
-
- mService.agentDisconnected("com.android.foo");
-
- verify(mOperationStorage, never())
- .cancelOperation(anyInt(), anyBoolean(), any(IntConsumer.class));
- }
-
- @Test
public void testReportDelayedRestoreResult_sendsLogsToMonitor() throws Exception {
PackageInfo packageInfo = getPackageInfo(TEST_PACKAGE);
when(mPackageManager.getPackageInfoAsUser(anyString(),
@@ -298,9 +280,8 @@
new DataTypeResult(/* dataType */ "type_2"));
mService.reportDelayedRestoreResult(TEST_PACKAGE, results);
-
verify(mBackupManagerMonitorEventSender).sendAgentLoggingResults(
- eq(packageInfo), eq(results), eq(BackupAnnotations.OperationType.RESTORE));
+ eq(packageInfo), eq(results), eq(OperationType.RESTORE));
}
private static PackageInfo getPackageInfo(String packageName) {
@@ -314,13 +295,9 @@
boolean isEnabledStatePersisted = false;
boolean shouldUseNewBackupEligibilityRules = false;
- private volatile Thread mWorkerThread = null;
-
- TestBackupService(Context context, PackageManager packageManager,
- LifecycleOperationStorage operationStorage, TransportManager transportManager,
- BackupHandler backupHandler) {
- super(context, packageManager, operationStorage, transportManager, backupHandler,
- createConstants(context));
+ TestBackupService() {
+ super(mContext, mPackageManager, mOperationStorage, mTransportManager, mBackupHandler,
+ createConstants(mContext), mActivityManager, mActivityManagerInternal);
}
private static BackupManagerConstants createConstants(Context context) {
@@ -352,26 +329,8 @@
}
@Override
- Thread getThreadForAsyncOperation(String operationName, Runnable operation) {
- mWorkerThread = super.getThreadForAsyncOperation(operationName, operation);
- return mWorkerThread;
- }
-
- @Override
BackupManagerMonitorEventSender getBMMEventSender(IBackupManagerMonitor monitor) {
return mBackupManagerMonitorEventSender;
}
-
- private void waitForAsyncOperation() {
- if (mWorkerThread == null) {
- return;
- }
-
- try {
- mWorkerThread.join(/* millis */ WORKER_THREAD_TIMEOUT_MILLISECONDS);
- } catch (InterruptedException e) {
- fail("Failed waiting for worker thread to complete: " + e.getMessage());
- }
- }
}
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/backup/fullbackup/PerformFullTransportBackupTaskTest.java b/services/tests/mockingservicestests/src/com/android/server/backup/fullbackup/PerformFullTransportBackupTaskTest.java
index 9474253..e618433 100644
--- a/services/tests/mockingservicestests/src/com/android/server/backup/fullbackup/PerformFullTransportBackupTaskTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/backup/fullbackup/PerformFullTransportBackupTaskTest.java
@@ -18,34 +18,100 @@
import static org.junit.Assert.assertThrows;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.when;
+import android.app.backup.BackupAnnotations;
+import android.app.backup.BackupTransport;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
import android.platform.test.annotations.Presubmit;
import androidx.test.runner.AndroidJUnit4;
+import com.android.server.backup.BackupAgentConnectionManager;
+import com.android.server.backup.BackupAgentTimeoutParameters;
+import com.android.server.backup.OperationStorage;
import com.android.server.backup.TransportManager;
import com.android.server.backup.UserBackupManagerService;
+import com.android.server.backup.transport.BackupTransportClient;
+import com.android.server.backup.transport.TransportConnection;
+import com.android.server.backup.utils.BackupEligibilityRules;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+
@Presubmit
@RunWith(AndroidJUnit4.class)
public class PerformFullTransportBackupTaskTest {
+ private static final String TEST_PACKAGE_1 = "package1";
+ private static final String TEST_PACKAGE_2 = "package2";
+
+ @Mock
+ BackupAgentTimeoutParameters mBackupAgentTimeoutParameters;
+ @Mock
+ BackupEligibilityRules mBackupEligibilityRules;
@Mock
UserBackupManagerService mBackupManagerService;
@Mock
+ BackupAgentConnectionManager mBackupAgentConnectionManager;
+ @Mock
+ BackupTransportClient mBackupTransportClient;
+ @Mock
+ CountDownLatch mLatch;
+ @Mock
+ OperationStorage mOperationStorage;
+ @Mock
+ PackageManager mPackageManager;
+ @Mock
+ TransportConnection mTransportConnection;
+ @Mock
TransportManager mTransportManager;
+ @Mock
+ UserBackupManagerService.BackupWakeLock mWakeLock;
+
+ private final List<String> mEligiblePackages = new ArrayList<>();
+
+ private PerformFullTransportBackupTask mTask;
@Before
- public void setUp() {
+ public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
+ when(mBackupManagerService.getPackageManager()).thenReturn(mPackageManager);
+ when(mBackupManagerService.getQueueLock()).thenReturn("something!");
+ when(mBackupManagerService.isEnabled()).thenReturn(true);
+ when(mBackupManagerService.getWakelock()).thenReturn(mWakeLock);
+ when(mBackupManagerService.isSetupComplete()).thenReturn(true);
+ when(mBackupManagerService.getAgentTimeoutParameters()).thenReturn(
+ mBackupAgentTimeoutParameters);
+ when(mBackupManagerService.getBackupAgentConnectionManager()).thenReturn(
+ mBackupAgentConnectionManager);
when(mBackupManagerService.getTransportManager()).thenReturn(mTransportManager);
+ when(mTransportManager.getCurrentTransportClient(any())).thenReturn(mTransportConnection);
+ when(mTransportConnection.connectOrThrow(any())).thenReturn(mBackupTransportClient);
+ when(mTransportConnection.connect(any())).thenReturn(mBackupTransportClient);
+ when(mBackupTransportClient.performFullBackup(any(), any(), anyInt())).thenReturn(
+ BackupTransport.TRANSPORT_ERROR);
+ when(mBackupEligibilityRules.appIsEligibleForBackup(
+ argThat(app -> mEligiblePackages.contains(app.packageName)))).thenReturn(
+ true);
+ when(mBackupEligibilityRules.appGetsFullBackup(
+ argThat(app -> mEligiblePackages.contains(app.packageName)))).thenReturn(
+ true);
}
@Test
@@ -70,4 +136,49 @@
/* backupEligibilityRules */ null);
});
}
+
+ @Test
+ public void run_setsAndClearsNoRestrictedModePackages() throws Exception {
+ mockPackageEligibleForFullBackup(TEST_PACKAGE_1);
+ mockPackageEligibleForFullBackup(TEST_PACKAGE_2);
+ createTask(new String[] {TEST_PACKAGE_1, TEST_PACKAGE_2});
+ when(mBackupTransportClient.getPackagesThatShouldNotUseRestrictedMode(any(),
+ anyInt())).thenReturn(Set.of("package1"));
+
+ mTask.run();
+
+ InOrder inOrder = inOrder(mBackupAgentConnectionManager);
+ inOrder.verify(mBackupAgentConnectionManager).setNoRestrictedModePackages(
+ eq(Set.of("package1")),
+ eq(BackupAnnotations.OperationType.BACKUP));
+ inOrder.verify(mBackupAgentConnectionManager).clearNoRestrictedModePackages();
+ }
+
+ private void createTask(String[] packageNames) {
+ mTask = PerformFullTransportBackupTask
+ .newWithCurrentTransport(
+ mBackupManagerService,
+ mOperationStorage,
+ /* observer */ null,
+ /* whichPackages */ packageNames,
+ /* updateSchedule */ false,
+ /* runningJob */ null,
+ mLatch,
+ /* backupObserver */ null,
+ /* monitor */ null,
+ /* userInitiated */ false,
+ /* caller */ null,
+ mBackupEligibilityRules);
+ }
+
+ private void mockPackageEligibleForFullBackup(String packageName) throws Exception {
+ mEligiblePackages.add(packageName);
+ ApplicationInfo appInfo = new ApplicationInfo();
+ appInfo.packageName = packageName;
+ PackageInfo packageInfo = new PackageInfo();
+ packageInfo.packageName = packageName;
+ packageInfo.applicationInfo = appInfo;
+ when(mPackageManager.getPackageInfoAsUser(eq(packageName), anyInt(), anyInt())).thenReturn(
+ packageInfo);
+ }
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/backup/restore/PerformUnifiedRestoreTaskTest.java b/services/tests/mockingservicestests/src/com/android/server/backup/restore/PerformUnifiedRestoreTaskTest.java
index 414532b..351aac3 100644
--- a/services/tests/mockingservicestests/src/com/android/server/backup/restore/PerformUnifiedRestoreTaskTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/backup/restore/PerformUnifiedRestoreTaskTest.java
@@ -23,8 +23,10 @@
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.app.backup.BackupAnnotations;
import android.app.backup.BackupDataInput;
import android.app.backup.BackupDataOutput;
import android.app.backup.BackupTransport;
@@ -42,6 +44,7 @@
import androidx.test.runner.AndroidJUnit4;
import com.android.modules.utils.testing.TestableDeviceConfig;
+import com.android.server.backup.BackupAgentConnectionManager;
import com.android.server.backup.Flags;
import com.android.server.backup.UserBackupManagerService;
import com.android.server.backup.internal.BackupHandler;
@@ -91,6 +94,10 @@
private UserBackupManagerService mBackupManagerService;
@Mock
private TransportConnection mTransportConnection;
+ @Mock
+ private BackupTransportClient mBackupTransportClient;
+ @Mock
+ private BackupAgentConnectionManager mBackupAgentConnectionManager;
private Set<String> mExcludedkeys = new HashSet<>();
private Map<String, String> mBackupData = new HashMap<>();
@@ -118,6 +125,9 @@
mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+ when(mBackupManagerService.getBackupAgentConnectionManager()).thenReturn(
+ mBackupAgentConnectionManager);
+
mBackupDataSource = new ArrayDeque<>(mBackupData.keySet());
when(mBackupDataInput.readNextHeader())
.then((Answer<Boolean>) invocation -> !mBackupDataSource.isEmpty());
@@ -151,6 +161,23 @@
}
@Test
+ public void setNoRestrictedModePackages_callsTransportAndSetsValue() throws Exception {
+ PackageInfo packageInfo1 = new PackageInfo();
+ packageInfo1.packageName = "package1";
+ PackageInfo packageInfo2 = new PackageInfo();
+ packageInfo2.packageName = "package2";
+ when(mBackupTransportClient.getPackagesThatShouldNotUseRestrictedMode(any(),
+ anyInt())).thenReturn(Set.of("package1"));
+
+ mRestoreTask.setNoRestrictedModePackages(mBackupTransportClient,
+ new PackageInfo[]{packageInfo1, packageInfo2});
+
+ verify(mBackupAgentConnectionManager).setNoRestrictedModePackages(
+ eq(Set.of("package1")),
+ eq(BackupAnnotations.OperationType.RESTORE));
+ }
+
+ @Test
public void testFilterExcludedKeys() throws Exception {
when(mBackupManagerService.getExcludedRestoreKeys(eq(PACKAGE_NAME)))
.thenReturn(mExcludedkeys);
diff --git a/services/tests/mockingservicestests/src/com/android/server/backup/transport/BackupTransportClientTest.java b/services/tests/mockingservicestests/src/com/android/server/backup/transport/BackupTransportClientTest.java
index 2d7d46f..13e3207 100644
--- a/services/tests/mockingservicestests/src/com/android/server/backup/transport/BackupTransportClientTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/backup/transport/BackupTransportClientTest.java
@@ -19,7 +19,14 @@
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.verify;
+import android.app.backup.BackupAnnotations.OperationType;
import android.app.backup.BackupTransport;
import android.app.backup.IBackupManagerMonitor;
import android.app.backup.RestoreDescription;
@@ -38,15 +45,31 @@
import com.android.internal.backup.ITransportStatusCallback;
import com.android.internal.infra.AndroidFuture;
+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;
+import java.util.Set;
@Presubmit
@RunWith(AndroidJUnit4.class)
public class BackupTransportClientTest {
+ @Mock
+ IBackupTransport mMockBackupTransport;
+
+ private BackupTransportClient mMockingTransportClient;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mMockingTransportClient = new BackupTransportClient(
+ mMockBackupTransport);
+ }
+
private static class TestFuturesFakeTransportBinder extends FakeTransportBinderBase {
public final Object mLock = new Object();
@@ -128,6 +151,70 @@
thread.join();
}
+ @Test
+ public void getPackagesThatShouldNotUseRestrictedMode_passesSetAsListToBinder()
+ throws Exception {
+ mockGetPackagesThatShouldNotUseRestrictedModeReturn(List.of("package1", "package2"));
+
+ mMockingTransportClient.getPackagesThatShouldNotUseRestrictedMode(
+ Set.of("package1", "package2"),
+ OperationType.BACKUP);
+
+ verify(mMockBackupTransport).getPackagesThatShouldNotUseRestrictedMode(
+ argThat(list -> Set.copyOf(list).equals(Set.of("package1", "package2"))),
+ eq(OperationType.BACKUP), any());
+ }
+
+ @Test
+ public void getPackagesThatShouldNotUseRestrictedMode_forRestore_callsBinderForRestore()
+ throws Exception {
+ mockGetPackagesThatShouldNotUseRestrictedModeReturn(null);
+
+ mMockingTransportClient.getPackagesThatShouldNotUseRestrictedMode(
+ Set.of(),
+ OperationType.RESTORE);
+
+ verify(mMockBackupTransport).getPackagesThatShouldNotUseRestrictedMode(any(),
+ eq(OperationType.RESTORE), any());
+ }
+
+ @Test
+ public void getPackagesThatShouldNotUseRestrictedMode_forBackup_callsBinderForBackup()
+ throws Exception {
+ mockGetPackagesThatShouldNotUseRestrictedModeReturn(null);
+
+ mMockingTransportClient.getPackagesThatShouldNotUseRestrictedMode(
+ Set.of(),
+ OperationType.BACKUP);
+
+ verify(mMockBackupTransport).getPackagesThatShouldNotUseRestrictedMode(any(),
+ eq(OperationType.BACKUP), any());
+ }
+
+ @Test
+ public void getPackagesThatShouldNotUseRestrictedMode_nullResult_returnsEmptySet()
+ throws Exception {
+ mockGetPackagesThatShouldNotUseRestrictedModeReturn(null);
+
+ Set<String> result = mMockingTransportClient.getPackagesThatShouldNotUseRestrictedMode(
+ Set.of(),
+ OperationType.BACKUP);
+
+ assertThat(result).isEqualTo(Set.of());
+ }
+
+ @Test
+ public void getPackagesThatShouldNotUseRestrictedMode_returnsResultAsSet()
+ throws Exception {
+ mockGetPackagesThatShouldNotUseRestrictedModeReturn(List.of("package1", "package2"));
+
+ Set<String> result = mMockingTransportClient.getPackagesThatShouldNotUseRestrictedMode(
+ Set.of("package1", "package2"),
+ OperationType.BACKUP);
+
+ assertThat(result).isEqualTo(Set.of("package1", "package2"));
+ }
+
private static class TestCallbacksFakeTransportBinder extends FakeTransportBinderBase {
public final Object mLock = new Object();
@@ -158,7 +245,6 @@
assertThat(status).isEqualTo(123);
}
-
@Test
public void testFinishBackup_completesLater_returnsStatus() throws Exception {
TestCallbacksFakeTransportBinder binder = new TestCallbacksFakeTransportBinder();
@@ -211,6 +297,14 @@
thread.join();
}
+ private void mockGetPackagesThatShouldNotUseRestrictedModeReturn(List<String> returnList)
+ throws Exception {
+ doAnswer(
+ i -> ((AndroidFuture<List<String>>) i.getArguments()[2]).complete(returnList)).when(
+ mMockBackupTransport).getPackagesThatShouldNotUseRestrictedMode(any(), anyInt(),
+ any());
+ }
+
// Convenience layer so we only need to fake specific methods useful for each test case.
private static class FakeTransportBinderBase implements IBackupTransport {
@Override public void name(AndroidFuture<String> f) throws RemoteException {}
@@ -258,6 +352,10 @@
@Override
public void getBackupManagerMonitor(AndroidFuture<IBackupManagerMonitor> resultFuture)
throws RemoteException {}
+ @Override
+ public void getPackagesThatShouldNotUseRestrictedMode(List<String> packageNames,
+ int operationType, AndroidFuture<List<String>> resultFuture)
+ throws RemoteException {}
@Override public IBinder asBinder() {
return null;
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/crashrecovery/Android.bp b/services/tests/mockingservicestests/src/com/android/server/crashrecovery/Android.bp
index 1f88c29..8eae9c7d 100644
--- a/services/tests/mockingservicestests/src/com/android/server/crashrecovery/Android.bp
+++ b/services/tests/mockingservicestests/src/com/android/server/crashrecovery/Android.bp
@@ -37,7 +37,7 @@
"truth",
"flag-junit",
] + select(soong_config_variable("ANDROID", "release_crashrecovery_module"), {
- "true": ["service-crashrecovery.impl"],
+ "true": ["service-crashrecovery-pre-jarjar"],
default: [],
}),
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
index d66bb00..c6870ad 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
@@ -65,6 +65,7 @@
import android.app.usage.UsageEvents;
import android.app.usage.UsageStatsManager;
import android.app.usage.UsageStatsManagerInternal;
+import android.compat.testing.PlatformCompatChangeRule;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ApplicationInfo;
@@ -103,10 +104,13 @@
import com.android.server.job.controllers.QuotaController.TimingSession;
import com.android.server.usage.AppStandbyInternal;
+import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.TestRule;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.ArgumentMatchers;
@@ -135,6 +139,9 @@
private static final int SOURCE_USER_ID = 0;
@Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
+ @Rule
+ public TestRule compatChangeRule = new PlatformCompatChangeRule();
private QuotaController mQuotaController;
private QuotaController.QcConstants mQcConstants;
private JobSchedulerService.Constants mConstants = new JobSchedulerService.Constants();
@@ -303,7 +310,7 @@
private int getProcessStateQuotaFreeThreshold() {
synchronized (mQuotaController.mLock) {
- return mQuotaController.getProcessStateQuotaFreeThreshold();
+ return mQuotaController.getProcessStateQuotaFreeThreshold(mSourceUid);
}
}
@@ -5197,6 +5204,101 @@
assertFalse(jobBg2.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
}
+ @Test
+ @EnableCompatChanges({QuotaController.OVERRIDE_QUOTA_ENFORCEMENT_TO_TOP_STARTED_JOBS,
+ QuotaController.OVERRIDE_QUOTA_ENFORCEMENT_TO_FGS_JOBS})
+ @RequiresFlagsEnabled({Flags.FLAG_ENFORCE_QUOTA_POLICY_TO_TOP_STARTED_JOBS,
+ Flags.FLAG_ENFORCE_QUOTA_POLICY_TO_FGS_JOBS})
+ public void testTracking_OutOfQuota_ForegroundAndBackground_CompactChangeOverrides() {
+ setDischarging();
+
+ JobStatus jobBg = createJobStatus("testTracking_OutOfQuota_ForegroundAndBackground", 1);
+ JobStatus jobTop = createJobStatus("testTracking_OutOfQuota_ForegroundAndBackground", 2);
+ trackJobs(jobBg, jobTop);
+ setStandbyBucket(WORKING_INDEX, jobTop, jobBg); // 2 hour window
+ // Now the package only has 20 seconds to run.
+ final long remainingTimeMs = 20 * SECOND_IN_MILLIS;
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(
+ JobSchedulerService.sElapsedRealtimeClock.millis() - HOUR_IN_MILLIS,
+ 10 * MINUTE_IN_MILLIS - remainingTimeMs, 1), false);
+
+ InOrder inOrder = inOrder(mJobSchedulerService);
+
+ // UID starts out inactive.
+ setProcessState(ActivityManager.PROCESS_STATE_SERVICE);
+ // Start the job.
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.prepareForExecutionLocked(jobBg);
+ }
+ advanceElapsedClock(remainingTimeMs / 2);
+ // New job starts after UID is in the foreground. Since the app is now in the foreground, it
+ // should continue to have remainingTimeMs / 2 time remaining.
+ setProcessState(ActivityManager.PROCESS_STATE_TOP);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.prepareForExecutionLocked(jobTop);
+ }
+ advanceElapsedClock(remainingTimeMs);
+
+ // Wait for some extra time to allow for job processing.
+ inOrder.verify(mJobSchedulerService,
+ timeout(remainingTimeMs + 2 * SECOND_IN_MILLIS).times(0))
+ .onControllerStateChanged(argThat(jobs -> jobs.size() > 0));
+ synchronized (mQuotaController.mLock) {
+ assertEquals(remainingTimeMs / 2,
+ mQuotaController.getRemainingExecutionTimeLocked(jobBg));
+ assertEquals(remainingTimeMs / 2,
+ mQuotaController.getRemainingExecutionTimeLocked(jobTop));
+ }
+ // Go to a background state.
+ setProcessState(ActivityManager.PROCESS_STATE_TOP_SLEEPING);
+ advanceElapsedClock(remainingTimeMs / 2 + 1);
+ // Only Bg job will be changed from in-quota to out-of-quota.
+ inOrder.verify(mJobSchedulerService,
+ timeout(remainingTimeMs / 2 + 2 * SECOND_IN_MILLIS).times(1))
+ .onControllerStateChanged(argThat(jobs -> jobs.size() == 1));
+ // Top job should still be allowed to run.
+ assertFalse(jobBg.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
+ assertTrue(jobTop.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
+
+ // New jobs to run.
+ JobStatus jobBg2 = createJobStatus("testTracking_OutOfQuota_ForegroundAndBackground", 3);
+ JobStatus jobTop2 = createJobStatus("testTracking_OutOfQuota_ForegroundAndBackground", 4);
+ JobStatus jobFg = createJobStatus("testTracking_OutOfQuota_ForegroundAndBackground", 5);
+ setStandbyBucket(WORKING_INDEX, jobBg2, jobTop2, jobFg);
+
+ advanceElapsedClock(20 * SECOND_IN_MILLIS);
+ setProcessState(ActivityManager.PROCESS_STATE_TOP);
+ inOrder.verify(mJobSchedulerService, timeout(SECOND_IN_MILLIS).times(1))
+ .onControllerStateChanged(argThat(jobs -> jobs.size() == 1));
+ trackJobs(jobFg, jobTop);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.prepareForExecutionLocked(jobTop);
+ }
+ assertTrue(jobTop.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
+ assertTrue(jobFg.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
+ assertTrue(jobBg.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
+
+ // App still in foreground so everything should be in quota.
+ advanceElapsedClock(20 * SECOND_IN_MILLIS);
+ setProcessState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
+ assertTrue(jobTop.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
+ assertTrue(jobFg.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
+ assertTrue(jobBg.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
+
+ advanceElapsedClock(20 * SECOND_IN_MILLIS);
+ setProcessState(ActivityManager.PROCESS_STATE_SERVICE);
+ inOrder.verify(mJobSchedulerService, timeout(SECOND_IN_MILLIS).times(1))
+ .onControllerStateChanged(argThat(jobs -> jobs.size() == 2));
+ // App is now in background and out of quota. Fg should now change to out of quota since it
+ // wasn't started. Top should remain in quota since it started when the app was in TOP.
+ assertTrue(jobTop.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
+ assertFalse(jobFg.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
+ assertFalse(jobBg.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
+ trackJobs(jobBg2);
+ assertFalse(jobBg2.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
+ }
+
/**
* Tests that TOP jobs are stopped when an app runs out of quota.
*/
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/InstallDependencyHelperTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/InstallDependencyHelperTest.java
index f6c644e..20ac078 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/InstallDependencyHelperTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/InstallDependencyHelperTest.java
@@ -27,6 +27,7 @@
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
+import android.content.Context;
import android.content.pm.SharedLibraryInfo;
import android.content.pm.parsing.ApkLite;
import android.content.pm.parsing.ApkLiteParseUtils;
@@ -34,6 +35,8 @@
import android.content.pm.parsing.result.ParseResult;
import android.content.pm.parsing.result.ParseTypeImpl;
import android.os.FileUtils;
+import android.os.Handler;
+import android.os.Looper;
import android.os.OutcomeReceiver;
import android.platform.test.annotations.Presubmit;
import android.platform.test.annotations.RequiresFlagsEnabled;
@@ -71,13 +74,17 @@
private static final String PUSH_FILE_DIR = "/data/local/tmp/tests/smockingservicestest/pm/";
private static final String TEST_APP_USING_SDK1_AND_SDK2 = "HelloWorldUsingSdk1And2.apk";
+ private final Handler mHandler = new Handler(Looper.getMainLooper());
+
@Mock private SharedLibrariesImpl mSharedLibraries;
+ @Mock private Context mContext;
+ @Mock private Computer mComputer;
private InstallDependencyHelper mInstallDependencyHelper;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- mInstallDependencyHelper = new InstallDependencyHelper(mSharedLibraries);
+ mInstallDependencyHelper = new InstallDependencyHelper(mContext, mSharedLibraries);
}
@Test
@@ -88,7 +95,8 @@
PackageLite pkg = getPackageLite(TEST_APP_USING_SDK1_AND_SDK2);
CallbackHelper callback = new CallbackHelper(/*expectSuccess=*/ false);
- mInstallDependencyHelper.resolveLibraryDependenciesIfNeeded(pkg, callback);
+ mInstallDependencyHelper.resolveLibraryDependenciesIfNeeded(pkg, mComputer,
+ 0, mHandler, callback);
callback.assertFailure();
assertThat(callback.error).hasMessageThat().contains("xyz");
@@ -104,11 +112,12 @@
.thenReturn(missingDependency);
CallbackHelper callback = new CallbackHelper(/*expectSuccess=*/ false);
- mInstallDependencyHelper.resolveLibraryDependenciesIfNeeded(pkg, callback);
+ mInstallDependencyHelper.resolveLibraryDependenciesIfNeeded(pkg, mComputer,
+ 0, mHandler, callback);
callback.assertFailure();
assertThat(callback.error).hasMessageThat().contains(
- "Failed to bind to Dependency Installer");
+ "Dependency Installer Service not found");
}
@@ -121,7 +130,8 @@
.thenReturn(missingDependency);
CallbackHelper callback = new CallbackHelper(/*expectSuccess=*/ true);
- mInstallDependencyHelper.resolveLibraryDependenciesIfNeeded(pkg, callback);
+ mInstallDependencyHelper.resolveLibraryDependenciesIfNeeded(pkg, mComputer,
+ 0, mHandler, callback);
callback.assertSuccess();
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/rollback/Android.bp b/services/tests/mockingservicestests/src/com/android/server/rollback/Android.bp
index 2f23e02..5a802d9 100644
--- a/services/tests/mockingservicestests/src/com/android/server/rollback/Android.bp
+++ b/services/tests/mockingservicestests/src/com/android/server/rollback/Android.bp
@@ -35,7 +35,7 @@
"truth",
"flag-junit",
] + select(soong_config_variable("ANDROID", "release_crashrecovery_module"), {
- "true": ["service-crashrecovery.impl"],
+ "true": ["service-crashrecovery-pre-jarjar"],
default: [],
}),
diff --git a/services/tests/performancehinttests/src/com/android/server/power/hint/HintManagerServiceTest.java b/services/tests/performancehinttests/src/com/android/server/power/hint/HintManagerServiceTest.java
index 58489f3..0881b4c 100644
--- a/services/tests/performancehinttests/src/com/android/server/power/hint/HintManagerServiceTest.java
+++ b/services/tests/performancehinttests/src/com/android/server/power/hint/HintManagerServiceTest.java
@@ -18,6 +18,7 @@
import static com.android.server.power.hint.HintManagerService.CLEAN_UP_UID_DELAY_MILLIS;
+import static com.android.server.power.hint.HintManagerService.DEFAULT_HEADROOM_PID;
import static com.google.common.truth.Truth.assertThat;
@@ -51,11 +52,15 @@
import android.hardware.common.fmq.MQDescriptor;
import android.hardware.power.ChannelConfig;
import android.hardware.power.ChannelMessage;
+import android.hardware.power.CpuHeadroomParams;
+import android.hardware.power.GpuHeadroomParams;
import android.hardware.power.IPower;
import android.hardware.power.SessionConfig;
import android.hardware.power.SessionTag;
import android.hardware.power.WorkDuration;
import android.os.Binder;
+import android.os.CpuHeadroomParamsInternal;
+import android.os.GpuHeadroomParamsInternal;
import android.os.IBinder;
import android.os.IHintSession;
import android.os.PerformanceHintManager;
@@ -128,11 +133,11 @@
private static final long[] TIMESTAMPS_ZERO = new long[] {};
private static final long[] TIMESTAMPS_TWO = new long[] {1L, 2L};
private static final WorkDuration[] WORK_DURATIONS_FIVE = new WorkDuration[] {
- makeWorkDuration(1L, 11L, 1L, 8L, 4L),
- makeWorkDuration(2L, 13L, 2L, 8L, 6L),
- makeWorkDuration(3L, 333333333L, 3L, 8L, 333333333L),
- makeWorkDuration(2L, 13L, 2L, 0L, 6L),
- makeWorkDuration(2L, 13L, 2L, 8L, 0L),
+ makeWorkDuration(1L, 11L, 1L, 8L, 4L),
+ makeWorkDuration(2L, 13L, 2L, 8L, 6L),
+ makeWorkDuration(3L, 333333333L, 3L, 8L, 333333333L),
+ makeWorkDuration(2L, 13L, 2L, 0L, 6L),
+ makeWorkDuration(2L, 13L, 2L, 8L, 0L),
};
private static final String TEST_APP_NAME = "com.android.test.app";
@@ -187,17 +192,17 @@
when(mNativeWrapperMock.halCreateHintSessionWithConfig(eq(TGID), eq(UID),
eq(SESSION_TIDS_A), eq(DEFAULT_TARGET_DURATION), anyInt(),
any(SessionConfig.class))).thenAnswer(fakeCreateWithConfig(SESSION_PTRS[0],
- SESSION_IDS[0]));
+ SESSION_IDS[0]));
when(mNativeWrapperMock.halCreateHintSessionWithConfig(eq(TGID), eq(UID),
eq(SESSION_TIDS_B), eq(DOUBLED_TARGET_DURATION), anyInt(),
any(SessionConfig.class))).thenAnswer(fakeCreateWithConfig(SESSION_PTRS[1],
- SESSION_IDS[1]));
+ SESSION_IDS[1]));
when(mNativeWrapperMock.halCreateHintSessionWithConfig(eq(TGID), eq(UID),
eq(SESSION_TIDS_C), eq(0L), anyInt(),
any(SessionConfig.class))).thenAnswer(fakeCreateWithConfig(SESSION_PTRS[2],
- SESSION_IDS[2]));
+ SESSION_IDS[2]));
- when(mIPowerMock.getInterfaceVersion()).thenReturn(5);
+ when(mIPowerMock.getInterfaceVersion()).thenReturn(6);
when(mIPowerMock.getSessionChannel(anyInt(), anyInt())).thenReturn(mConfig);
LocalServices.removeServiceForTest(ActivityManagerInternal.class);
LocalServices.addService(ActivityManagerInternal.class, mAmInternalMock);
@@ -217,8 +222,8 @@
when(mNativeWrapperMock.halCreateHintSession(eq(TGID), eq(UID), eq(SESSION_TIDS_C),
eq(0L))).thenReturn(SESSION_PTRS[2]);
when(mNativeWrapperMock.halCreateHintSessionWithConfig(anyInt(), anyInt(),
- any(int[].class), anyLong(), anyInt(),
- any(SessionConfig.class))).thenThrow(new UnsupportedOperationException());
+ any(int[].class), anyLong(), anyInt(),
+ any(SessionConfig.class))).thenThrow(new UnsupportedOperationException());
}
static class NativeWrapperFake extends NativeWrapper {
@@ -337,7 +342,7 @@
SESSION_TIDS_C, 0L, SessionTag.OTHER, new SessionConfig());
assertNotNull(c);
verify(mNativeWrapperMock, times(3)).halCreateHintSession(anyInt(), anyInt(),
- any(int[].class), anyLong());
+ any(int[].class), anyLong());
}
@Test
@@ -487,7 +492,7 @@
AppHintSession a = (AppHintSession) service.getBinderServiceInstance()
.createHintSessionWithConfig(token, SESSION_TIDS_A, DEFAULT_TARGET_DURATION,
- SessionTag.OTHER, new SessionConfig());
+ SessionTag.OTHER, new SessionConfig());
a.sendHint(PerformanceHintManager.Session.CPU_LOAD_RESET);
verify(mNativeWrapperMock, times(1)).halSendHint(anyLong(),
@@ -514,7 +519,7 @@
AppHintSession a = (AppHintSession) service.getBinderServiceInstance()
.createHintSessionWithConfig(token, SESSION_TIDS_A, DEFAULT_TARGET_DURATION,
- SessionTag.OTHER, new SessionConfig());
+ SessionTag.OTHER, new SessionConfig());
service.mUidObserver.onUidStateChanged(
a.mUid, ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND, 0, 0);
@@ -1096,4 +1101,157 @@
verify(mIPowerMock, times(1)).getSessionChannel(eq(TGID), eq(UID));
assertTrue(service.hasChannel(TGID, UID));
}
+
+ @Test
+ public void testHeadroomPowerHalNotSupported() throws Exception {
+ when(mIPowerMock.getInterfaceVersion()).thenReturn(5);
+ HintManagerService service = createService();
+ assertThrows(UnsupportedOperationException.class, () -> {
+ service.getBinderServiceInstance().getCpuHeadroom(null);
+ });
+ assertThrows(UnsupportedOperationException.class, () -> {
+ service.getBinderServiceInstance().getGpuHeadroom(null);
+ });
+ assertThrows(UnsupportedOperationException.class, () -> {
+ service.getBinderServiceInstance().getCpuHeadroomMinIntervalMillis();
+ });
+ assertThrows(UnsupportedOperationException.class, () -> {
+ service.getBinderServiceInstance().getGpuHeadroomMinIntervalMillis();
+ });
+ }
+
+ @Test
+ public void testCpuHeadroomCache() throws Exception {
+ when(mIPowerMock.getCpuHeadroomMinIntervalMillis()).thenReturn(2000L);
+ CpuHeadroomParamsInternal params1 = new CpuHeadroomParamsInternal();
+ CpuHeadroomParams halParams1 = new CpuHeadroomParams();
+ halParams1.calculationType = CpuHeadroomParams.CalculationType.MIN;
+ halParams1.selectionType = CpuHeadroomParams.SelectionType.ALL;
+ halParams1.pid = Process.myPid();
+
+ CpuHeadroomParamsInternal params2 = new CpuHeadroomParamsInternal();
+ params2.usesDeviceHeadroom = true;
+ params2.calculationType = CpuHeadroomParams.CalculationType.AVERAGE;
+ params2.selectionType = CpuHeadroomParams.SelectionType.PER_CORE;
+ CpuHeadroomParams halParams2 = new CpuHeadroomParams();
+ halParams2.calculationType = CpuHeadroomParams.CalculationType.AVERAGE;
+ halParams2.selectionType = CpuHeadroomParams.SelectionType.PER_CORE;
+ halParams2.pid = DEFAULT_HEADROOM_PID;
+
+ float[] headroom1 = new float[] {0.1f};
+ when(mIPowerMock.getCpuHeadroom(eq(halParams1))).thenReturn(headroom1);
+ float[] headroom2 = new float[] {0.1f, 0.5f};
+ when(mIPowerMock.getCpuHeadroom(eq(halParams2))).thenReturn(headroom2);
+
+ HintManagerService service = createService();
+ clearInvocations(mIPowerMock);
+
+ service.getBinderServiceInstance().getCpuHeadroomMinIntervalMillis();
+ verify(mIPowerMock, times(0)).getCpuHeadroomMinIntervalMillis();
+ service.getBinderServiceInstance().getCpuHeadroom(params1);
+ verify(mIPowerMock, times(1)).getCpuHeadroom(eq(halParams1));
+ service.getBinderServiceInstance().getCpuHeadroom(params2);
+ verify(mIPowerMock, times(1)).getCpuHeadroom(eq(halParams2));
+
+ // verify cache is working
+ clearInvocations(mIPowerMock);
+ assertArrayEquals(headroom1, service.getBinderServiceInstance().getCpuHeadroom(params1),
+ 0.01f);
+ assertArrayEquals(headroom2, service.getBinderServiceInstance().getCpuHeadroom(params2),
+ 0.01f);
+ verify(mIPowerMock, times(0)).getCpuHeadroom(any());
+
+ // after 1 more second it should be served with cache still
+ Thread.sleep(1000);
+ clearInvocations(mIPowerMock);
+ assertArrayEquals(headroom1, service.getBinderServiceInstance().getCpuHeadroom(params1),
+ 0.01f);
+ assertArrayEquals(headroom2, service.getBinderServiceInstance().getCpuHeadroom(params2),
+ 0.01f);
+ verify(mIPowerMock, times(0)).getCpuHeadroom(any());
+
+ // after 1.5 more second it should be served with cache still as timer reset
+ Thread.sleep(1500);
+ clearInvocations(mIPowerMock);
+ assertArrayEquals(headroom1, service.getBinderServiceInstance().getCpuHeadroom(params1),
+ 0.01f);
+ assertArrayEquals(headroom2, service.getBinderServiceInstance().getCpuHeadroom(params2),
+ 0.01f);
+ verify(mIPowerMock, times(0)).getCpuHeadroom(any());
+
+ // after 2+ seconds it should be served from HAL as it exceeds 2000 millis interval
+ Thread.sleep(2100);
+ clearInvocations(mIPowerMock);
+ assertArrayEquals(headroom1, service.getBinderServiceInstance().getCpuHeadroom(params1),
+ 0.01f);
+ assertArrayEquals(headroom2, service.getBinderServiceInstance().getCpuHeadroom(params2),
+ 0.01f);
+ verify(mIPowerMock, times(1)).getCpuHeadroom(eq(halParams1));
+ verify(mIPowerMock, times(1)).getCpuHeadroom(eq(halParams2));
+ }
+
+ @Test
+ public void testGpuHeadroomCache() throws Exception {
+ when(mIPowerMock.getGpuHeadroomMinIntervalMillis()).thenReturn(2000L);
+ GpuHeadroomParamsInternal params1 = new GpuHeadroomParamsInternal();
+ GpuHeadroomParams halParams1 = new GpuHeadroomParams();
+ halParams1.calculationType = GpuHeadroomParams.CalculationType.MIN;
+
+ GpuHeadroomParamsInternal params2 = new GpuHeadroomParamsInternal();
+ GpuHeadroomParams halParams2 = new GpuHeadroomParams();
+ params2.calculationType =
+ halParams2.calculationType = GpuHeadroomParams.CalculationType.AVERAGE;
+
+ float headroom1 = 0.1f;
+ when(mIPowerMock.getGpuHeadroom(eq(halParams1))).thenReturn(headroom1);
+ float headroom2 = 0.2f;
+ when(mIPowerMock.getGpuHeadroom(eq(halParams2))).thenReturn(headroom2);
+ HintManagerService service = createService();
+ clearInvocations(mIPowerMock);
+
+ service.getBinderServiceInstance().getGpuHeadroomMinIntervalMillis();
+ verify(mIPowerMock, times(0)).getGpuHeadroomMinIntervalMillis();
+ assertEquals(headroom1, service.getBinderServiceInstance().getGpuHeadroom(params1),
+ 0.01f);
+ assertEquals(headroom2, service.getBinderServiceInstance().getGpuHeadroom(params2),
+ 0.01f);
+ verify(mIPowerMock, times(1)).getGpuHeadroom(eq(halParams1));
+ verify(mIPowerMock, times(1)).getGpuHeadroom(eq(halParams2));
+
+ // verify cache is working
+ clearInvocations(mIPowerMock);
+ assertEquals(headroom1, service.getBinderServiceInstance().getGpuHeadroom(params1),
+ 0.01f);
+ assertEquals(headroom2, service.getBinderServiceInstance().getGpuHeadroom(params2),
+ 0.01f);
+ verify(mIPowerMock, times(0)).getGpuHeadroom(any());
+
+ // after 1 more second it should be served with cache still
+ Thread.sleep(1000);
+ clearInvocations(mIPowerMock);
+ assertEquals(headroom1, service.getBinderServiceInstance().getGpuHeadroom(params1),
+ 0.01f);
+ assertEquals(headroom2, service.getBinderServiceInstance().getGpuHeadroom(params2),
+ 0.01f);
+ verify(mIPowerMock, times(0)).getGpuHeadroom(any());
+
+ // after 1.5 more second it should be served with cache still as timer reset
+ Thread.sleep(1500);
+ clearInvocations(mIPowerMock);
+ assertEquals(headroom1, service.getBinderServiceInstance().getGpuHeadroom(params1),
+ 0.01f);
+ assertEquals(headroom2, service.getBinderServiceInstance().getGpuHeadroom(params2),
+ 0.01f);
+ verify(mIPowerMock, times(0)).getGpuHeadroom(any());
+
+ // after 2+ seconds it should be served from HAL as it exceeds 2000 millis interval
+ Thread.sleep(2100);
+ clearInvocations(mIPowerMock);
+ assertEquals(headroom1, service.getBinderServiceInstance().getGpuHeadroom(params1),
+ 0.01f);
+ assertEquals(headroom2, service.getBinderServiceInstance().getGpuHeadroom(params2),
+ 0.01f);
+ verify(mIPowerMock, times(1)).getGpuHeadroom(eq(halParams1));
+ verify(mIPowerMock, times(1)).getGpuHeadroom(eq(halParams2));
+ }
}
diff --git a/services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java
index 359cf63..b48c2d7 100644
--- a/services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java
+++ b/services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java
@@ -2705,12 +2705,11 @@
verify(mInvalidateInteractiveCachesMock).call();
listener.get().onDisplayGroupAdded(nonDefaultDisplayGroupId);
- verify(mInvalidateInteractiveCachesMock, times(2)).call();
mService.setWakefulnessLocked(Display.DEFAULT_DISPLAY_GROUP, WAKEFULNESS_ASLEEP,
mClock.now(), 0, PowerManager.GO_TO_SLEEP_REASON_APPLICATION, 0, null, null);
- verify(mInvalidateInteractiveCachesMock, times(3)).call();
+ verify(mInvalidateInteractiveCachesMock, times(2)).call();
}
@Test
@@ -2732,12 +2731,11 @@
verify(mInvalidateInteractiveCachesMock).call();
listener.get().onDisplayGroupAdded(nonDefaultDisplayGroupId);
- verify(mInvalidateInteractiveCachesMock, times(2)).call();
mService.setWakefulnessLocked(nonDefaultDisplayGroupId, WAKEFULNESS_ASLEEP, mClock.now(),
0, PowerManager.GO_TO_SLEEP_REASON_APPLICATION, 0, null, null);
- verify(mInvalidateInteractiveCachesMock, times(3)).call();
+ verify(mInvalidateInteractiveCachesMock, times(2)).call();
}
@Test
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/WakelockPowerStatsCollectorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/WakelockPowerStatsCollectorTest.java
index 0d5d277..ed927c6 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/WakelockPowerStatsCollectorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/WakelockPowerStatsCollectorTest.java
@@ -26,8 +26,7 @@
import android.os.Process;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
-import android.platform.test.ravenwood.RavenwoodConfig;
-import android.platform.test.ravenwood.RavenwoodConfig.Config;
+import android.platform.test.ravenwood.RavenwoodRule;
import com.android.internal.os.PowerStats;
import com.android.server.power.feature.flags.Flags;
@@ -39,10 +38,9 @@
public class WakelockPowerStatsCollectorTest {
- @Config
- public static final RavenwoodConfig sConfig =
- new RavenwoodConfig.Builder()
- .setProvideMainThread(true)
+ @Rule
+ public final RavenwoodRule mRule =
+ new RavenwoodRule.Builder()
.setSystemPropertyImmutable(
"persist.sys.com.android.server.power.feature.flags."
+ "framework_wakelock_info-override",
diff --git a/services/tests/security/forensic/src/com/android/server/security/forensic/ForensicServiceTest.java b/services/tests/security/forensic/src/com/android/server/security/forensic/ForensicServiceTest.java
index 40e0034..03c449c 100644
--- a/services/tests/security/forensic/src/com/android/server/security/forensic/ForensicServiceTest.java
+++ b/services/tests/security/forensic/src/com/android/server/security/forensic/ForensicServiceTest.java
@@ -16,9 +16,13 @@
package com.android.server.security.forensic;
+import static android.Manifest.permission.MANAGE_FORENSIC_STATE;
+import static android.Manifest.permission.READ_FORENSIC_STATE;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
@@ -27,57 +31,63 @@
import static org.mockito.Mockito.verify;
import android.annotation.SuppressLint;
+import android.app.admin.ConnectEvent;
+import android.app.admin.DnsEvent;
+import android.app.admin.SecurityLog.SecurityEvent;
import android.content.Context;
import android.os.Looper;
+import android.os.PermissionEnforcer;
import android.os.RemoteException;
+import android.os.test.FakePermissionEnforcer;
import android.os.test.TestLooper;
import android.security.forensic.ForensicEvent;
import android.security.forensic.IForensicServiceCommandCallback;
import android.security.forensic.IForensicServiceStateCallback;
-import android.util.ArrayMap;
import androidx.test.core.app.ApplicationProvider;
import com.android.server.ServiceThread;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import java.util.ArrayList;
import java.util.List;
-import java.util.Map;
public class ForensicServiceTest {
private static final int STATE_UNKNOWN = IForensicServiceStateCallback.State.UNKNOWN;
- private static final int STATE_INVISIBLE = IForensicServiceStateCallback.State.INVISIBLE;
- private static final int STATE_VISIBLE = IForensicServiceStateCallback.State.VISIBLE;
+ private static final int STATE_DISABLED = IForensicServiceStateCallback.State.DISABLED;
private static final int STATE_ENABLED = IForensicServiceStateCallback.State.ENABLED;
private static final int ERROR_UNKNOWN = IForensicServiceCommandCallback.ErrorCode.UNKNOWN;
private static final int ERROR_PERMISSION_DENIED =
IForensicServiceCommandCallback.ErrorCode.PERMISSION_DENIED;
- private static final int ERROR_INVALID_STATE_TRANSITION =
- IForensicServiceCommandCallback.ErrorCode.INVALID_STATE_TRANSITION;
- private static final int ERROR_BACKUP_TRANSPORT_UNAVAILABLE =
- IForensicServiceCommandCallback.ErrorCode.BACKUP_TRANSPORT_UNAVAILABLE;
+ private static final int ERROR_TRANSPORT_UNAVAILABLE =
+ IForensicServiceCommandCallback.ErrorCode.TRANSPORT_UNAVAILABLE;
private static final int ERROR_DATA_SOURCE_UNAVAILABLE =
IForensicServiceCommandCallback.ErrorCode.DATA_SOURCE_UNAVAILABLE;
private Context mContext;
- private BackupTransportConnection mBackupTransportConnection;
+ private ForensicEventTransportConnection mForensicEventTransportConnection;
private DataAggregator mDataAggregator;
private ForensicService mForensicService;
private TestLooper mTestLooper;
private Looper mLooper;
private TestLooper mTestLooperOfDataAggregator;
private Looper mLooperOfDataAggregator;
+ private FakePermissionEnforcer mPermissionEnforcer;
@SuppressLint("VisibleForTests")
@Before
public void setUp() {
mContext = spy(ApplicationProvider.getApplicationContext());
+ mPermissionEnforcer = new FakePermissionEnforcer();
+ mPermissionEnforcer.grant(READ_FORENSIC_STATE);
+ mPermissionEnforcer.grant(MANAGE_FORENSIC_STATE);
+
mTestLooper = new TestLooper();
mLooper = mTestLooper.getLooper();
mTestLooperOfDataAggregator = new TestLooper();
@@ -87,217 +97,101 @@
}
@Test
- public void testMonitorState_Invisible() throws RemoteException {
+ public void testAddStateCallback_NoPermission() {
+ mPermissionEnforcer.revoke(READ_FORENSIC_STATE);
StateCallback scb = new StateCallback();
assertEquals(STATE_UNKNOWN, scb.mState);
- mForensicService.getBinderService().monitorState(scb);
- mTestLooper.dispatchAll();
- assertEquals(STATE_INVISIBLE, scb.mState);
+ assertThrows(SecurityException.class,
+ () -> mForensicService.getBinderService().addStateCallback(scb));
}
@Test
- public void testMonitorState_Invisible_TwoMonitors() throws RemoteException {
+ public void testRemoveStateCallback_NoPermission() {
+ mPermissionEnforcer.revoke(READ_FORENSIC_STATE);
+ StateCallback scb = new StateCallback();
+ assertEquals(STATE_UNKNOWN, scb.mState);
+ assertThrows(SecurityException.class,
+ () -> mForensicService.getBinderService().removeStateCallback(scb));
+ }
+
+ @Test
+ public void testEnable_NoPermission() {
+ mPermissionEnforcer.revoke(MANAGE_FORENSIC_STATE);
+
+ CommandCallback ccb = new CommandCallback();
+ assertThrows(SecurityException.class,
+ () -> mForensicService.getBinderService().enable(ccb));
+ }
+
+ @Test
+ public void testDisable_NoPermission() {
+ mPermissionEnforcer.revoke(MANAGE_FORENSIC_STATE);
+
+ CommandCallback ccb = new CommandCallback();
+ assertThrows(SecurityException.class,
+ () -> mForensicService.getBinderService().disable(ccb));
+ }
+
+ @Test
+ public void testAddStateCallback_Disabled() throws RemoteException {
+ StateCallback scb = new StateCallback();
+ assertEquals(STATE_UNKNOWN, scb.mState);
+ mForensicService.getBinderService().addStateCallback(scb);
+ mTestLooper.dispatchAll();
+ assertEquals(STATE_DISABLED, scb.mState);
+ }
+
+ @Test
+ public void testAddStateCallback_Disabled_TwoStateCallbacks() throws RemoteException {
StateCallback scb1 = new StateCallback();
assertEquals(STATE_UNKNOWN, scb1.mState);
- mForensicService.getBinderService().monitorState(scb1);
+ mForensicService.getBinderService().addStateCallback(scb1);
mTestLooper.dispatchAll();
- assertEquals(STATE_INVISIBLE, scb1.mState);
+ assertEquals(STATE_DISABLED, scb1.mState);
StateCallback scb2 = new StateCallback();
assertEquals(STATE_UNKNOWN, scb2.mState);
- mForensicService.getBinderService().monitorState(scb2);
+ mForensicService.getBinderService().addStateCallback(scb2);
mTestLooper.dispatchAll();
- assertEquals(STATE_INVISIBLE, scb2.mState);
+ assertEquals(STATE_DISABLED, scb2.mState);
}
@Test
- public void testMakeVisible_FromInvisible() throws RemoteException {
- StateCallback scb = new StateCallback();
- assertEquals(STATE_UNKNOWN, scb.mState);
- mForensicService.getBinderService().monitorState(scb);
- mTestLooper.dispatchAll();
- assertEquals(STATE_INVISIBLE, scb.mState);
-
- CommandCallback ccb = new CommandCallback();
- mForensicService.getBinderService().makeVisible(ccb);
- mTestLooper.dispatchAll();
- assertEquals(STATE_VISIBLE, scb.mState);
- assertNull(ccb.mErrorCode);
- }
-
- @Test
- public void testMakeVisible_FromInvisible_TwoMonitors() throws RemoteException {
- mForensicService.setState(STATE_INVISIBLE);
+ public void testRemoveStateCallback() throws RemoteException {
+ mForensicService.setState(STATE_DISABLED);
StateCallback scb1 = new StateCallback();
StateCallback scb2 = new StateCallback();
- mForensicService.getBinderService().monitorState(scb1);
- mForensicService.getBinderService().monitorState(scb2);
+ mForensicService.getBinderService().addStateCallback(scb1);
+ mForensicService.getBinderService().addStateCallback(scb2);
mTestLooper.dispatchAll();
- assertEquals(STATE_INVISIBLE, scb1.mState);
- assertEquals(STATE_INVISIBLE, scb2.mState);
+ assertEquals(STATE_DISABLED, scb1.mState);
+ assertEquals(STATE_DISABLED, scb2.mState);
doReturn(true).when(mDataAggregator).initialize();
+ doReturn(true).when(mForensicEventTransportConnection).initialize();
- CommandCallback ccb = new CommandCallback();
- mForensicService.getBinderService().makeVisible(ccb);
- mTestLooper.dispatchAll();
- assertEquals(STATE_VISIBLE, scb1.mState);
- assertEquals(STATE_VISIBLE, scb2.mState);
- assertNull(ccb.mErrorCode);
- }
-
- @Test
- public void testMakeVisible_FromInvisible_TwoMonitors_DataSourceUnavailable()
- throws RemoteException {
- mForensicService.setState(STATE_INVISIBLE);
- StateCallback scb1 = new StateCallback();
- StateCallback scb2 = new StateCallback();
- mForensicService.getBinderService().monitorState(scb1);
- mForensicService.getBinderService().monitorState(scb2);
- mTestLooper.dispatchAll();
- assertEquals(STATE_INVISIBLE, scb1.mState);
- assertEquals(STATE_INVISIBLE, scb2.mState);
-
- doReturn(false).when(mDataAggregator).initialize();
-
- CommandCallback ccb = new CommandCallback();
- mForensicService.getBinderService().makeVisible(ccb);
- mTestLooper.dispatchAll();
- assertEquals(STATE_INVISIBLE, scb1.mState);
- assertEquals(STATE_INVISIBLE, scb2.mState);
- assertNotNull(ccb.mErrorCode);
- assertEquals(ERROR_DATA_SOURCE_UNAVAILABLE, ccb.mErrorCode.intValue());
- }
-
- @Test
- public void testMakeVisible_FromVisible_TwoMonitors() throws RemoteException {
- mForensicService.setState(STATE_VISIBLE);
- StateCallback scb1 = new StateCallback();
- StateCallback scb2 = new StateCallback();
- mForensicService.getBinderService().monitorState(scb1);
- mForensicService.getBinderService().monitorState(scb2);
- mTestLooper.dispatchAll();
- assertEquals(STATE_VISIBLE, scb1.mState);
- assertEquals(STATE_VISIBLE, scb2.mState);
-
- CommandCallback ccb = new CommandCallback();
- mForensicService.getBinderService().makeVisible(ccb);
- mTestLooper.dispatchAll();
- assertEquals(STATE_VISIBLE, scb1.mState);
- assertEquals(STATE_VISIBLE, scb2.mState);
- assertNull(ccb.mErrorCode);
- }
-
- @Test
- public void testMakeVisible_FromEnabled_TwoMonitors() throws RemoteException {
- mForensicService.setState(STATE_ENABLED);
- StateCallback scb1 = new StateCallback();
- StateCallback scb2 = new StateCallback();
- mForensicService.getBinderService().monitorState(scb1);
- mForensicService.getBinderService().monitorState(scb2);
- mTestLooper.dispatchAll();
- assertEquals(STATE_ENABLED, scb1.mState);
- assertEquals(STATE_ENABLED, scb2.mState);
-
- CommandCallback ccb = new CommandCallback();
- mForensicService.getBinderService().makeVisible(ccb);
- mTestLooper.dispatchAll();
- assertEquals(STATE_ENABLED, scb1.mState);
- assertEquals(STATE_ENABLED, scb2.mState);
- assertNotNull(ccb.mErrorCode);
- assertEquals(ERROR_INVALID_STATE_TRANSITION, ccb.mErrorCode.intValue());
- }
-
- @Test
- public void testMakeInvisible_FromInvisible_TwoMonitors() throws RemoteException {
- mForensicService.setState(STATE_INVISIBLE);
- StateCallback scb1 = new StateCallback();
- StateCallback scb2 = new StateCallback();
- mForensicService.getBinderService().monitorState(scb1);
- mForensicService.getBinderService().monitorState(scb2);
- mTestLooper.dispatchAll();
- assertEquals(STATE_INVISIBLE, scb1.mState);
- assertEquals(STATE_INVISIBLE, scb2.mState);
-
- CommandCallback ccb = new CommandCallback();
- mForensicService.getBinderService().makeInvisible(ccb);
- mTestLooper.dispatchAll();
- assertEquals(STATE_INVISIBLE, scb1.mState);
- assertEquals(STATE_INVISIBLE, scb2.mState);
- assertNull(ccb.mErrorCode);
- }
-
- @Test
- public void testMakeInvisible_FromVisible_TwoMonitors() throws RemoteException {
- mForensicService.setState(STATE_VISIBLE);
- StateCallback scb1 = new StateCallback();
- StateCallback scb2 = new StateCallback();
- mForensicService.getBinderService().monitorState(scb1);
- mForensicService.getBinderService().monitorState(scb2);
- mTestLooper.dispatchAll();
- assertEquals(STATE_VISIBLE, scb1.mState);
- assertEquals(STATE_VISIBLE, scb2.mState);
-
- CommandCallback ccb = new CommandCallback();
- mForensicService.getBinderService().makeInvisible(ccb);
- mTestLooper.dispatchAll();
- assertEquals(STATE_INVISIBLE, scb1.mState);
- assertEquals(STATE_INVISIBLE, scb2.mState);
- assertNull(ccb.mErrorCode);
- }
-
- @Test
- public void testMakeInvisible_FromEnabled_TwoMonitors() throws RemoteException {
- mForensicService.setState(STATE_ENABLED);
- StateCallback scb1 = new StateCallback();
- StateCallback scb2 = new StateCallback();
- mForensicService.getBinderService().monitorState(scb1);
- mForensicService.getBinderService().monitorState(scb2);
- mTestLooper.dispatchAll();
- assertEquals(STATE_ENABLED, scb1.mState);
- assertEquals(STATE_ENABLED, scb2.mState);
-
- CommandCallback ccb = new CommandCallback();
- mForensicService.getBinderService().makeInvisible(ccb);
- mTestLooper.dispatchAll();
- assertEquals(STATE_INVISIBLE, scb1.mState);
- assertEquals(STATE_INVISIBLE, scb2.mState);
- assertNull(ccb.mErrorCode);
- }
-
-
- @Test
- public void testEnable_FromInvisible_TwoMonitors() throws RemoteException {
- mForensicService.setState(STATE_INVISIBLE);
- StateCallback scb1 = new StateCallback();
- StateCallback scb2 = new StateCallback();
- mForensicService.getBinderService().monitorState(scb1);
- mForensicService.getBinderService().monitorState(scb2);
- mTestLooper.dispatchAll();
- assertEquals(STATE_INVISIBLE, scb1.mState);
- assertEquals(STATE_INVISIBLE, scb2.mState);
+ mForensicService.getBinderService().removeStateCallback(scb2);
CommandCallback ccb = new CommandCallback();
mForensicService.getBinderService().enable(ccb);
mTestLooper.dispatchAll();
- assertEquals(STATE_INVISIBLE, scb1.mState);
- assertEquals(STATE_INVISIBLE, scb2.mState);
- assertNotNull(ccb.mErrorCode);
- assertEquals(ERROR_INVALID_STATE_TRANSITION, ccb.mErrorCode.intValue());
+ assertEquals(STATE_ENABLED, scb1.mState);
+ assertEquals(STATE_DISABLED, scb2.mState);
+ assertNull(ccb.mErrorCode);
}
@Test
- public void testEnable_FromVisible_TwoMonitors() throws RemoteException {
- mForensicService.setState(STATE_VISIBLE);
+ public void testEnable_FromDisabled_TwoStateCallbacks() throws RemoteException {
+ mForensicService.setState(STATE_DISABLED);
StateCallback scb1 = new StateCallback();
StateCallback scb2 = new StateCallback();
- mForensicService.getBinderService().monitorState(scb1);
- mForensicService.getBinderService().monitorState(scb2);
+ mForensicService.getBinderService().addStateCallback(scb1);
+ mForensicService.getBinderService().addStateCallback(scb2);
mTestLooper.dispatchAll();
- assertEquals(STATE_VISIBLE, scb1.mState);
- assertEquals(STATE_VISIBLE, scb2.mState);
+ assertEquals(STATE_DISABLED, scb1.mState);
+ assertEquals(STATE_DISABLED, scb2.mState);
- doReturn(true).when(mBackupTransportConnection).initialize();
+ doReturn(true).when(mForensicEventTransportConnection).initialize();
CommandCallback ccb = new CommandCallback();
mForensicService.getBinderService().enable(ccb);
@@ -310,35 +204,13 @@
}
@Test
- public void testEnable_FromVisible_TwoMonitors_BackupTransportUnavailable()
+ public void testEnable_FromEnabled_TwoStateCallbacks()
throws RemoteException {
- mForensicService.setState(STATE_VISIBLE);
- StateCallback scb1 = new StateCallback();
- StateCallback scb2 = new StateCallback();
- mForensicService.getBinderService().monitorState(scb1);
- mForensicService.getBinderService().monitorState(scb2);
- mTestLooper.dispatchAll();
- assertEquals(STATE_VISIBLE, scb1.mState);
- assertEquals(STATE_VISIBLE, scb2.mState);
-
- doReturn(false).when(mBackupTransportConnection).initialize();
-
- CommandCallback ccb = new CommandCallback();
- mForensicService.getBinderService().enable(ccb);
- mTestLooper.dispatchAll();
- assertEquals(STATE_VISIBLE, scb1.mState);
- assertEquals(STATE_VISIBLE, scb2.mState);
- assertNotNull(ccb.mErrorCode);
- assertEquals(ERROR_BACKUP_TRANSPORT_UNAVAILABLE, ccb.mErrorCode.intValue());
- }
-
- @Test
- public void testEnable_FromEnabled_TwoMonitors() throws RemoteException {
mForensicService.setState(STATE_ENABLED);
StateCallback scb1 = new StateCallback();
StateCallback scb2 = new StateCallback();
- mForensicService.getBinderService().monitorState(scb1);
- mForensicService.getBinderService().monitorState(scb2);
+ mForensicService.getBinderService().addStateCallback(scb1);
+ mForensicService.getBinderService().addStateCallback(scb2);
mTestLooper.dispatchAll();
assertEquals(STATE_ENABLED, scb1.mState);
assertEquals(STATE_ENABLED, scb2.mState);
@@ -346,62 +218,44 @@
CommandCallback ccb = new CommandCallback();
mForensicService.getBinderService().enable(ccb);
mTestLooper.dispatchAll();
+
assertEquals(STATE_ENABLED, scb1.mState);
assertEquals(STATE_ENABLED, scb2.mState);
assertNull(ccb.mErrorCode);
}
@Test
- public void testDisable_FromInvisible_TwoMonitors() throws RemoteException {
- mForensicService.setState(STATE_INVISIBLE);
+ public void testDisable_FromDisabled_TwoStateCallbacks() throws RemoteException {
+ mForensicService.setState(STATE_DISABLED);
StateCallback scb1 = new StateCallback();
StateCallback scb2 = new StateCallback();
- mForensicService.getBinderService().monitorState(scb1);
- mForensicService.getBinderService().monitorState(scb2);
+ mForensicService.getBinderService().addStateCallback(scb1);
+ mForensicService.getBinderService().addStateCallback(scb2);
mTestLooper.dispatchAll();
- assertEquals(STATE_INVISIBLE, scb1.mState);
- assertEquals(STATE_INVISIBLE, scb2.mState);
+ assertEquals(STATE_DISABLED, scb1.mState);
+ assertEquals(STATE_DISABLED, scb2.mState);
CommandCallback ccb = new CommandCallback();
mForensicService.getBinderService().disable(ccb);
mTestLooper.dispatchAll();
- assertEquals(STATE_INVISIBLE, scb1.mState);
- assertEquals(STATE_INVISIBLE, scb2.mState);
- assertNotNull(ccb.mErrorCode);
- assertEquals(ERROR_INVALID_STATE_TRANSITION, ccb.mErrorCode.intValue());
- }
- @Test
- public void testDisable_FromVisible_TwoMonitors() throws RemoteException {
- mForensicService.setState(STATE_VISIBLE);
- StateCallback scb1 = new StateCallback();
- StateCallback scb2 = new StateCallback();
- mForensicService.getBinderService().monitorState(scb1);
- mForensicService.getBinderService().monitorState(scb2);
- mTestLooper.dispatchAll();
- assertEquals(STATE_VISIBLE, scb1.mState);
- assertEquals(STATE_VISIBLE, scb2.mState);
-
- CommandCallback ccb = new CommandCallback();
- mForensicService.getBinderService().disable(ccb);
- mTestLooper.dispatchAll();
- assertEquals(STATE_VISIBLE, scb1.mState);
- assertEquals(STATE_VISIBLE, scb2.mState);
+ assertEquals(STATE_DISABLED, scb1.mState);
+ assertEquals(STATE_DISABLED, scb2.mState);
assertNull(ccb.mErrorCode);
}
@Test
- public void testDisable_FromEnabled_TwoMonitors() throws RemoteException {
+ public void testDisable_FromEnabled_TwoStateCallbacks() throws RemoteException {
mForensicService.setState(STATE_ENABLED);
StateCallback scb1 = new StateCallback();
StateCallback scb2 = new StateCallback();
- mForensicService.getBinderService().monitorState(scb1);
- mForensicService.getBinderService().monitorState(scb2);
+ mForensicService.getBinderService().addStateCallback(scb1);
+ mForensicService.getBinderService().addStateCallback(scb2);
mTestLooper.dispatchAll();
assertEquals(STATE_ENABLED, scb1.mState);
assertEquals(STATE_ENABLED, scb2.mState);
- doNothing().when(mBackupTransportConnection).release();
+ doNothing().when(mForensicEventTransportConnection).release();
ServiceThread mockThread = spy(ServiceThread.class);
mDataAggregator.setHandler(mLooperOfDataAggregator, mockThread);
@@ -412,56 +266,74 @@
mTestLooperOfDataAggregator.dispatchAll();
// TODO: We can verify the data sources once we implement them.
verify(mockThread, times(1)).quitSafely();
- assertEquals(STATE_VISIBLE, scb1.mState);
- assertEquals(STATE_VISIBLE, scb2.mState);
+ assertEquals(STATE_DISABLED, scb1.mState);
+ assertEquals(STATE_DISABLED, scb2.mState);
assertNull(ccb.mErrorCode);
}
+ @Ignore("Enable once the ForensicEventTransportConnection is ready")
+ @Test
+ public void testEnable_FromDisable_TwoStateCallbacks_TransportUnavailable()
+ throws RemoteException {
+ mForensicService.setState(STATE_DISABLED);
+ StateCallback scb1 = new StateCallback();
+ StateCallback scb2 = new StateCallback();
+ mForensicService.getBinderService().addStateCallback(scb1);
+ mForensicService.getBinderService().addStateCallback(scb2);
+ mTestLooper.dispatchAll();
+ assertEquals(STATE_DISABLED, scb1.mState);
+ assertEquals(STATE_DISABLED, scb2.mState);
+
+ doReturn(false).when(mForensicEventTransportConnection).initialize();
+
+ CommandCallback ccb = new CommandCallback();
+ mForensicService.getBinderService().enable(ccb);
+ mTestLooper.dispatchAll();
+ assertEquals(STATE_DISABLED, scb1.mState);
+ assertEquals(STATE_DISABLED, scb2.mState);
+ assertNotNull(ccb.mErrorCode);
+ assertEquals(ERROR_TRANSPORT_UNAVAILABLE, ccb.mErrorCode.intValue());
+ }
+
@Test
public void testDataAggregator_AddBatchData() {
mForensicService.setState(STATE_ENABLED);
ServiceThread mockThread = spy(ServiceThread.class);
mDataAggregator.setHandler(mLooperOfDataAggregator, mockThread);
- String eventOneType = "event_one_type";
- String eventOneMapKey = "event_one_map_key";
- String eventOneMapVal = "event_one_map_val";
- Map<String, String> eventOneMap = new ArrayMap<String, String>();
- eventOneMap.put(eventOneMapKey, eventOneMapVal);
- ForensicEvent eventOne = new ForensicEvent(eventOneType, eventOneMap);
+ SecurityEvent securityEvent = new SecurityEvent(0, new byte[0]);
+ ForensicEvent eventOne = new ForensicEvent(securityEvent);
- String eventTwoType = "event_two_type";
- String eventTwoMapKey = "event_two_map_key";
- String eventTwoMapVal = "event_two_map_val";
- Map<String, String> eventTwoMap = new ArrayMap<String, String>();
- eventTwoMap.put(eventTwoMapKey, eventTwoMapVal);
- ForensicEvent eventTwo = new ForensicEvent(eventTwoType, eventTwoMap);
+ ConnectEvent connectEvent = new ConnectEvent("127.0.0.1", 80, null, 0);
+ ForensicEvent eventTwo = new ForensicEvent(connectEvent);
+
+ DnsEvent dnsEvent = new DnsEvent(null, new String[] {"127.0.0.1"}, 1, null, 0);
+ ForensicEvent eventThree = new ForensicEvent(dnsEvent);
List<ForensicEvent> events = new ArrayList<>();
events.add(eventOne);
events.add(eventTwo);
+ events.add(eventThree);
- doReturn(true).when(mBackupTransportConnection).addData(any());
+ doReturn(true).when(mForensicEventTransportConnection).addData(any());
mDataAggregator.addBatchData(events);
mTestLooperOfDataAggregator.dispatchAll();
mTestLooper.dispatchAll();
ArgumentCaptor<List<ForensicEvent>> captor = ArgumentCaptor.forClass(List.class);
- verify(mBackupTransportConnection).addData(captor.capture());
+ verify(mForensicEventTransportConnection).addData(captor.capture());
List<ForensicEvent> receivedEvents = captor.getValue();
- assertEquals(receivedEvents.size(), 2);
+ assertEquals(receivedEvents.size(), 3);
- assertEquals(receivedEvents.getFirst().getType(), eventOneType);
- assertEquals(receivedEvents.getFirst().getKeyValuePairs().size(), 1);
- assertEquals(receivedEvents.getFirst().getKeyValuePairs().get(eventOneMapKey),
- eventOneMapVal);
+ assertEquals(receivedEvents.get(0).getType(), ForensicEvent.SECURITY_EVENT);
+ assertNotNull(receivedEvents.get(0).getSecurityEvent());
- assertEquals(receivedEvents.getLast().getType(), eventTwoType);
- assertEquals(receivedEvents.getLast().getKeyValuePairs().size(), 1);
- assertEquals(receivedEvents.getLast().getKeyValuePairs().get(eventTwoMapKey),
- eventTwoMapVal);
+ assertEquals(receivedEvents.get(1).getType(), ForensicEvent.NETWORK_EVENT_CONNECT);
+ assertNotNull(receivedEvents.get(1).getConnectEvent());
+ assertEquals(receivedEvents.get(2).getType(), ForensicEvent.NETWORK_EVENT_DNS);
+ assertNotNull(receivedEvents.get(2).getDnsEvent());
}
private class MockInjector implements ForensicService.Injector {
@@ -476,6 +348,10 @@
return mContext;
}
+ @Override
+ public PermissionEnforcer getPermissionEnforcer() {
+ return mPermissionEnforcer;
+ }
@Override
public Looper getLooper() {
@@ -483,9 +359,9 @@
}
@Override
- public BackupTransportConnection getBackupTransportConnection() {
- mBackupTransportConnection = spy(new BackupTransportConnection(mContext));
- return mBackupTransportConnection;
+ public ForensicEventTransportConnection getForensicEventransportConnection() {
+ mForensicEventTransportConnection = spy(new ForensicEventTransportConnection(mContext));
+ return mForensicEventTransportConnection;
}
@Override
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
index f165667..0c058df 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -97,7 +97,7 @@
"com_android_server_accessibility_flags_lib",
"locksettings_flags_lib",
] + select(soong_config_variable("ANDROID", "release_crashrecovery_module"), {
- "true": ["service-crashrecovery.impl"],
+ "true": ["service-crashrecovery-pre-jarjar"],
default: [],
}),
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 0988eea..a55346c 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/FlashNotificationsControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/FlashNotificationsControllerTest.java
@@ -430,7 +430,7 @@
AudioPlaybackConfiguration config = new AudioPlaybackConfiguration(
mock(PlayerBase.PlayerIdCard.class), 0, 0, 0);
config.handleStateEvent(AudioPlaybackConfiguration.PLAYER_STATE_STARTED,
- AudioPlaybackConfiguration.PLAYER_DEVICEID_INVALID);
+ AudioPlaybackConfiguration.PLAYER_DEVICEIDS_INVALID);
AudioAttributes.Builder builder = new AudioAttributes.Builder();
builder.setUsage(AudioAttributes.USAGE_ALARM);
diff --git a/services/tests/servicestests/src/com/android/server/audio/LoudnessCodecHelperTest.java b/services/tests/servicestests/src/com/android/server/audio/LoudnessCodecHelperTest.java
index 84c0ab3..0d44021 100644
--- a/services/tests/servicestests/src/com/android/server/audio/LoudnessCodecHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/audio/LoudnessCodecHelperTest.java
@@ -314,12 +314,13 @@
AudioDeviceInfo[] devicesStatic = AudioManager.getDevicesStatic(GET_DEVICES_OUTPUTS);
assumeTrue(devIdx < devicesStatic.length);
Log.d(TAG, "Out devices number " + devicesStatic.length + ". Picking index " + devIdx);
- int deviceId = devicesStatic[devIdx].getId();
+ int[] deviceIds = new int[1];
+ deviceIds[0] = devicesStatic[devIdx].getId();
PlayerBase.PlayerIdCard idCard = Mockito.mock(PlayerBase.PlayerIdCard.class);
AudioPlaybackConfiguration apc =
new AudioPlaybackConfiguration(idCard, piid, /*uid=*/1, /*pid=*/myPid());
- apc.handleStateEvent(PLAYER_UPDATE_DEVICE_ID, deviceId);
+ apc.handleStateEvent(PLAYER_UPDATE_DEVICE_ID, deviceIds);
apc.handleSessionIdEvent(sessionId);
apc.handleAudioAttributesEvent(new AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_MEDIA)
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/PreAuthInfoTest.java b/services/tests/servicestests/src/com/android/server/biometrics/PreAuthInfoTest.java
index b81bf3c..f6f831f 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/PreAuthInfoTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/PreAuthInfoTest.java
@@ -21,6 +21,7 @@
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
import static android.hardware.biometrics.BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE;
import static android.hardware.biometrics.BiometricManager.BIOMETRIC_ERROR_NOT_ENABLED_FOR_APPS;
+import static android.hardware.biometrics.BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE;
import static com.android.server.biometrics.sensors.LockoutTracker.LOCKOUT_NONE;
@@ -354,6 +355,21 @@
assertThat(preAuthInfo.getIsMandatoryBiometricsAuthentication()).isTrue();
}
+ @Test
+ public void prioritizeStrengthErrorBeforeCameraUnavailableError() throws Exception {
+ final BiometricSensor sensor = getFaceSensorWithStrength(
+ BiometricManager.Authenticators.BIOMETRIC_WEAK);
+ final PromptInfo promptInfo = new PromptInfo();
+ promptInfo.setAuthenticators(BiometricManager.Authenticators.BIOMETRIC_STRONG);
+ promptInfo.setNegativeButtonText(TEST_PACKAGE_NAME);
+ final PreAuthInfo preAuthInfo = PreAuthInfo.create(mTrustManager, mDevicePolicyManager,
+ mSettingObserver, List.of(sensor), USER_ID , promptInfo, TEST_PACKAGE_NAME,
+ false /* checkDevicePolicyManager */, mContext, mBiometricCameraManager,
+ mUserManager);
+
+ assertThat(preAuthInfo.getCanAuthenticateResult()).isEqualTo(BIOMETRIC_ERROR_NO_HARDWARE);
+ }
+
private BiometricSensor getFingerprintSensor() {
BiometricSensor sensor = new BiometricSensor(mContext, SENSOR_ID_FINGERPRINT,
TYPE_FINGERPRINT, BiometricManager.Authenticators.BIOMETRIC_STRONG,
@@ -372,9 +388,10 @@
return sensor;
}
- private BiometricSensor getFaceSensor() {
+ private BiometricSensor getFaceSensorWithStrength(
+ @BiometricManager.Authenticators.Types int sensorStrength) {
BiometricSensor sensor = new BiometricSensor(mContext, SENSOR_ID_FACE, TYPE_FACE,
- BiometricManager.Authenticators.BIOMETRIC_STRONG, mFaceAuthenticator) {
+ sensorStrength, mFaceAuthenticator) {
@Override
boolean confirmationAlwaysRequired(int userId) {
return false;
@@ -388,4 +405,8 @@
return sensor;
}
+
+ private BiometricSensor getFaceSensor() {
+ return getFaceSensorWithStrength(BiometricManager.Authenticators.BIOMETRIC_STRONG);
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/audio/VirtualAudioControllerTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/audio/VirtualAudioControllerTest.java
index 51c2ad1..687a1ab 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/audio/VirtualAudioControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/audio/VirtualAudioControllerTest.java
@@ -200,7 +200,9 @@
AudioPlaybackConfiguration audioPlaybackConfiguration =
new AudioPlaybackConfiguration(
playerIdCard, /* piid= */ 1000, appUid, /* pid= */ 1000);
- audioPlaybackConfiguration.handleStateEvent(PLAYER_STATE_STARTED, /* deviceId= */1);
+ int[] deviceIds = new int[1];
+ deviceIds[0] = 1;
+ audioPlaybackConfiguration.handleStateEvent(PLAYER_STATE_STARTED, deviceIds);
configs.add(audioPlaybackConfiguration);
}
return configs;
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 698bda3..4c381eb 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
@@ -24,6 +24,7 @@
import android.app.admin.DevicePolicyManagerInternal;
import android.app.admin.DevicePolicyManagerLiteInternal;
import android.app.backup.IBackupManager;
+import android.app.supervision.SupervisionManagerInternal;
import android.app.usage.UsageStatsManagerInternal;
import android.content.Context;
import android.content.Intent;
@@ -488,6 +489,11 @@
public Context createContextAsUser(UserHandle user) {
return context;
}
+
+ @Override
+ SupervisionManagerInternal getSupervisionManager() {
+ return services.supervisionManagerInternal;
+ }
}
static class TransferOwnershipMetadataManagerMockInjector extends
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index cb4269a..cf5dc4b 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -109,6 +109,7 @@
import android.app.admin.PreferentialNetworkServiceConfig;
import android.app.admin.SystemUpdatePolicy;
import android.app.admin.WifiSsidPolicy;
+import android.app.admin.flags.Flags;
import android.app.role.RoleManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -134,6 +135,10 @@
import android.os.UserHandle;
import android.os.UserManager;
import android.platform.test.annotations.Presubmit;
+import android.platform.test.annotations.RequiresFlagsDisabled;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.provider.DeviceConfig;
import android.provider.Settings;
import android.security.KeyChain;
@@ -165,6 +170,7 @@
import org.junit.After;
import org.junit.Before;
import org.junit.Ignore;
+import org.junit.Rule;
import org.junit.Test;
import org.mockito.Mockito;
import org.mockito.internal.util.collections.Sets;
@@ -207,6 +213,9 @@
public static final String INVALID_CALLING_IDENTITY_MSG = "Calling identity is not authorized";
public static final String ONGOING_CALL_MSG = "ongoing call on the device";
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
// TODO replace all instances of this with explicit {@link #mServiceContext}.
@Deprecated
private DpmMockContext mContext;
@@ -4425,6 +4434,7 @@
}
@Test
+ @Ignore("b/359188869")
public void testSetAutoTimeEnabledWithPOOfOrganizationOwnedDevice() throws Exception {
setupProfileOwner();
configureProfileOwnerOfOrgOwnedDevice(admin1, CALLER_USER_HANDLE);
@@ -4902,6 +4912,7 @@
}
@Test
+ @RequiresFlagsDisabled(Flags.FLAG_SECONDARY_LOCKSCREEN_API_ENABLED)
public void testSecondaryLockscreen_profileOwner() throws Exception {
mContext.binder.callingUid = DpmMockContext.CALLER_UID;
@@ -4930,6 +4941,7 @@
}
@Test
+ @RequiresFlagsDisabled(Flags.FLAG_SECONDARY_LOCKSCREEN_API_ENABLED)
public void testSecondaryLockscreen_deviceOwner() throws Exception {
mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
@@ -4948,6 +4960,7 @@
}
@Test
+ @RequiresFlagsDisabled(Flags.FLAG_SECONDARY_LOCKSCREEN_API_ENABLED)
public void testSecondaryLockscreen_nonOwner() throws Exception {
mContext.binder.callingUid = DpmMockContext.CALLER_UID;
@@ -4964,6 +4977,7 @@
}
@Test
+ @RequiresFlagsDisabled(Flags.FLAG_SECONDARY_LOCKSCREEN_API_ENABLED)
public void testSecondaryLockscreen_nonSupervisionApp() throws Exception {
mContext.binder.callingUid = DpmMockContext.CALLER_UID;
@@ -4996,6 +5010,51 @@
}
@Test
+ @RequiresFlagsEnabled(Flags.FLAG_SECONDARY_LOCKSCREEN_API_ENABLED)
+ public void testIsSecondaryLockscreenEnabled() throws Exception {
+ mContext.binder.callingUid = DpmMockContext.CALLER_UID;
+
+ verifyIsSecondaryLockscreenEnabled(false);
+ verifyIsSecondaryLockscreenEnabled(true);
+ }
+
+ private void verifyIsSecondaryLockscreenEnabled(boolean expected) throws Exception {
+ reset(getServices().supervisionManagerInternal);
+
+ doReturn(expected).when(getServices().supervisionManagerInternal)
+ .isSupervisionLockscreenEnabledForUser(anyInt());
+
+ final boolean enabled = dpm.isSecondaryLockscreenEnabled(UserHandle.of(CALLER_USER_HANDLE));
+ verify(getServices().supervisionManagerInternal)
+ .isSupervisionLockscreenEnabledForUser(CALLER_USER_HANDLE);
+
+ assertThat(enabled).isEqualTo(expected);
+ }
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_SECONDARY_LOCKSCREEN_API_ENABLED)
+ public void testSetSecondaryLockscreenEnabled() throws Exception {
+ mContext.binder.callingUid = DpmMockContext.CALLER_UID;
+
+ verifySetSecondaryLockscreenEnabled(false);
+ verifySetSecondaryLockscreenEnabled(true);
+ }
+
+ private void verifySetSecondaryLockscreenEnabled(boolean enabled) throws Exception {
+ reset(getServices().supervisionManagerInternal);
+
+ dpm.setSecondaryLockscreenEnabled(admin1, enabled);
+ verify(getServices().supervisionManagerInternal).setSupervisionLockscreenEnabledForUser(
+ CALLER_USER_HANDLE, enabled, null);
+
+ reset(getServices().supervisionManagerInternal);
+
+ dpm.setSecondaryLockscreenEnabled(enabled, new PersistableBundle());
+ verify(getServices().supervisionManagerInternal).setSupervisionLockscreenEnabledForUser(
+ eq(CALLER_USER_HANDLE), eq(enabled), any(PersistableBundle.class));
+ }
+
+ @Test
public void testIsDeviceManaged() throws Exception {
mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
setupDeviceOwner();
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 2e200a9..3e4448c1 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
@@ -35,6 +35,7 @@
import android.app.admin.DevicePolicyManager;
import android.app.backup.IBackupManager;
import android.app.role.RoleManager;
+import android.app.supervision.SupervisionManagerInternal;
import android.app.usage.UsageStatsManagerInternal;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -77,8 +78,8 @@
import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.LockSettingsInternal;
import com.android.server.AlarmManagerInternal;
-import com.android.server.pdb.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.pm.pkg.PackageState;
@@ -149,6 +150,7 @@
public final BuildMock buildMock = new BuildMock();
public final File dataDir;
public final PolicyPathProvider pathProvider;
+ public final SupervisionManagerInternal supervisionManagerInternal;
private final Map<String, PackageState> mTestPackageStates = new ArrayMap<>();
@@ -203,6 +205,7 @@
roleManager = realContext.getSystemService(RoleManager.class);
roleManagerForMock = mock(RoleManagerForMock.class);
subscriptionManager = mock(SubscriptionManager.class);
+ supervisionManagerInternal = mock(SupervisionManagerInternal.class);
// Package manager is huge, so we use a partial mock instead.
packageManager = spy(realContext.getPackageManager());
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 73aec63..b2a7d20 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
@@ -532,6 +532,8 @@
MediaProjectionManagerService.MediaProjection projection = startProjectionPreconditions();
projection.start(mIMediaProjectionCallback);
+ doReturn(PackageManager.PERMISSION_DENIED).when(mPackageManager).checkPermission(
+ RECORD_SENSITIVE_CONTENT, projection.packageName);
doReturn(true).when(mKeyguardManager).isKeyguardLocked();
MediaProjectionManagerService.BinderService mediaProjectionBinderService =
mService.new BinderService(mContext);
@@ -540,50 +542,6 @@
verify(mContext, never()).startActivityAsUser(any(), any());
}
- @EnableFlags(android.companion.virtualdevice.flags
- .Flags.FLAG_MEDIA_PROJECTION_KEYGUARD_RESTRICTIONS)
- @Test
- public void testKeyguardLocked_stopsActiveProjection() throws Exception {
- MediaProjectionManagerService service =
- new MediaProjectionManagerService(mContext, mMediaProjectionMetricsLoggerInjector);
- MediaProjectionManagerService.MediaProjection projection =
- startProjectionPreconditions(service);
- projection.start(mIMediaProjectionCallback);
- projection.notifyVirtualDisplayCreated(10);
-
- assertThat(service.getActiveProjectionInfo()).isNotNull();
-
- doReturn(PackageManager.PERMISSION_DENIED).when(mPackageManager)
- .checkPermission(RECORD_SENSITIVE_CONTENT, projection.packageName);
- service.onKeyguardLockedStateChanged(true);
-
- verify(mMediaProjectionMetricsLogger).logStopped(UID, TARGET_UID_UNKNOWN);
- assertThat(service.getActiveProjectionInfo()).isNull();
- assertThat(mIMediaProjectionCallback.mLatch.await(5, TimeUnit.SECONDS)).isTrue();
- }
-
- @EnableFlags(android.companion.virtualdevice.flags
- .Flags.FLAG_MEDIA_PROJECTION_KEYGUARD_RESTRICTIONS)
- @Test
- public void testKeyguardLocked_packageAllowlisted_doesNotStopActiveProjection()
- throws NameNotFoundException {
- MediaProjectionManagerService service =
- new MediaProjectionManagerService(mContext, mMediaProjectionMetricsLoggerInjector);
- MediaProjectionManagerService.MediaProjection projection =
- startProjectionPreconditions(service);
- projection.start(mIMediaProjectionCallback);
- projection.notifyVirtualDisplayCreated(10);
-
- assertThat(service.getActiveProjectionInfo()).isNotNull();
-
- doReturn(PackageManager.PERMISSION_GRANTED).when(mPackageManager).checkPermission(
- RECORD_SENSITIVE_CONTENT, projection.packageName);
- service.onKeyguardLockedStateChanged(true);
-
- verifyZeroInteractions(mMediaProjectionMetricsLogger);
- assertThat(service.getActiveProjectionInfo()).isNotNull();
- }
-
@Test
public void stop_noActiveProjections_doesNotLog() throws Exception {
MediaProjectionManagerService service =
@@ -706,7 +664,7 @@
mClockInjector);
MediaProjectionManagerService.MediaProjection projection = createProjectionPreconditions(
service);
- mClock.fastForward(projection.mDefaultTimeoutMs + 10);
+ mClock.fastForward(projection.mDefaultTimeoutMillis + 10);
// Immediate timeout - so no longer valid.
assertThat(projection.isValid()).isFalse();
diff --git a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionStopControllerTest.java b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionStopControllerTest.java
new file mode 100644
index 0000000..affcfc1
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionStopControllerTest.java
@@ -0,0 +1,468 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.media.projection;
+
+
+import static android.Manifest.permission.RECORD_SENSITIVE_CONTENT;
+import static android.provider.Settings.Global.DISABLE_SCREEN_SHARE_PROTECTIONS_FOR_APPS_AND_NOTIFICATIONS;
+import static android.view.Display.INVALID_DISPLAY;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.nullable;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.Manifest;
+import android.annotation.SuppressLint;
+import android.app.ActivityManagerInternal;
+import android.app.AppOpsManager;
+import android.app.Instrumentation;
+import android.app.KeyguardManager;
+import android.app.role.RoleManager;
+import android.companion.AssociationRequest;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.ApplicationInfoFlags;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.media.projection.MediaProjectionManager;
+import android.os.UserHandle;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.annotations.Presubmit;
+import android.platform.test.flag.junit.SetFlagsRule;
+import android.provider.Settings;
+import android.telecom.TelecomManager;
+import android.testing.TestableContext;
+import android.util.ArraySet;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.server.LocalServices;
+import com.android.server.SystemConfig;
+import com.android.server.wm.WindowManagerInternal;
+
+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.mockito.Answers;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Objects;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
+
+/**
+ * Tests for the {@link MediaProjectionStopController} class.
+ * <p>
+ * Build/Install/Run:
+ * atest FrameworksServicesTests:MediaProjectionStopControllerTest
+ */
+@SmallTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+@SuppressLint({"UseCheckPermission", "VisibleForTests", "MissingPermission"})
+public class MediaProjectionStopControllerTest {
+ private static final int UID = 10;
+ private static final String PACKAGE_NAME = "test.package";
+ private final ApplicationInfo mAppInfo = new ApplicationInfo();
+ @Rule
+ public final TestableContext mContext = spy(
+ new TestableContext(InstrumentationRegistry.getInstrumentation().getContext()));
+
+ private final MediaProjectionManagerService.Injector mMediaProjectionMetricsLoggerInjector =
+ new MediaProjectionManagerService.Injector() {
+ @Override
+ MediaProjectionMetricsLogger mediaProjectionMetricsLogger(Context context) {
+ return mMediaProjectionMetricsLogger;
+ }
+ };
+
+ private MediaProjectionManagerService mService;
+
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
+ @Mock
+ private ActivityManagerInternal mAmInternal;
+ @Mock
+ private PackageManager mPackageManager;
+ @Mock
+ private KeyguardManager mKeyguardManager;
+ @Mock
+ private TelecomManager mTelecomManager;
+
+ private AppOpsManager mAppOpsManager;
+ @Mock
+ private MediaProjectionMetricsLogger mMediaProjectionMetricsLogger;
+ @Mock
+ private Consumer<Integer> mStopConsumer;
+
+ private MediaProjectionStopController mStopController;
+
+ @Before
+ public void setup() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ LocalServices.removeServiceForTest(ActivityManagerInternal.class);
+ LocalServices.addService(ActivityManagerInternal.class, mAmInternal);
+
+ mAppOpsManager = mockAppOpsManager();
+ mContext.addMockSystemService(AppOpsManager.class, mAppOpsManager);
+ mContext.addMockSystemService(KeyguardManager.class, mKeyguardManager);
+ mContext.addMockSystemService(TelecomManager.class, mTelecomManager);
+ mContext.setMockPackageManager(mPackageManager);
+
+ mStopController = new MediaProjectionStopController(mContext, mStopConsumer);
+ mService = new MediaProjectionManagerService(mContext,
+ mMediaProjectionMetricsLoggerInjector);
+
+ mAppInfo.targetSdkVersion = 35;
+ }
+
+ private static AppOpsManager mockAppOpsManager() {
+ return mock(AppOpsManager.class, invocationOnMock -> {
+ if (invocationOnMock.getMethod().getName().startsWith("noteOp")) {
+ // Mockito will return 0 for non-stubbed method which corresponds to MODE_ALLOWED
+ // and is not what we want.
+ return AppOpsManager.MODE_IGNORED;
+ }
+ return Answers.RETURNS_DEFAULTS.answer(invocationOnMock);
+ });
+ }
+
+ @After
+ public void tearDown() {
+ LocalServices.removeServiceForTest(ActivityManagerInternal.class);
+ LocalServices.removeServiceForTest(WindowManagerInternal.class);
+ }
+
+ @Test
+ @EnableFlags(
+ android.companion.virtualdevice.flags.Flags.FLAG_MEDIA_PROJECTION_KEYGUARD_RESTRICTIONS)
+ public void testMediaProjectionNotRestricted() throws Exception {
+ when(mKeyguardManager.isKeyguardLocked()).thenReturn(false);
+
+ assertThat(mStopController.isStartForbidden(
+ createMediaProjection(PACKAGE_NAME))).isFalse();
+ }
+
+ @Test
+ @EnableFlags(
+ android.companion.virtualdevice.flags.Flags.FLAG_MEDIA_PROJECTION_KEYGUARD_RESTRICTIONS)
+ public void testMediaProjectionRestricted() throws Exception {
+ MediaProjectionManagerService.MediaProjection mediaProjection = createMediaProjection();
+ mediaProjection.notifyVirtualDisplayCreated(1);
+ doReturn(PackageManager.PERMISSION_DENIED).when(mPackageManager).checkPermission(
+ RECORD_SENSITIVE_CONTENT, mediaProjection.packageName);
+ when(mKeyguardManager.isKeyguardLocked()).thenReturn(true);
+
+ assertThat(mStopController.isStartForbidden(mediaProjection)).isTrue();
+ }
+
+ @Test
+ public void testExemptFromStoppingNullProjection() throws Exception {
+ assertThat(mStopController.isExemptFromStopping(null,
+ MediaProjectionStopController.STOP_REASON_UNKNOWN)).isTrue();
+ }
+
+ @Test
+ public void testExemptFromStoppingInvalidProjection() throws Exception {
+ assertThat(mStopController.isExemptFromStopping(createMediaProjection(null),
+ MediaProjectionStopController.STOP_REASON_UNKNOWN)).isTrue();
+ }
+
+ @Test
+ public void testExemptFromStoppingDisableScreenshareProtections() throws Exception {
+ MediaProjectionManagerService.MediaProjection mediaProjection = createMediaProjection();
+ doReturn(PackageManager.PERMISSION_DENIED).when(mPackageManager).checkPermission(
+ RECORD_SENSITIVE_CONTENT, mediaProjection.packageName);
+ int value = Settings.Global.getInt(mContext.getContentResolver(),
+ DISABLE_SCREEN_SHARE_PROTECTIONS_FOR_APPS_AND_NOTIFICATIONS, 0);
+ try {
+ Settings.Global.putInt(mContext.getContentResolver(),
+ DISABLE_SCREEN_SHARE_PROTECTIONS_FOR_APPS_AND_NOTIFICATIONS, 1);
+
+ assertThat(mStopController.isExemptFromStopping(mediaProjection,
+ MediaProjectionStopController.STOP_REASON_UNKNOWN)).isTrue();
+ } finally {
+ Settings.Global.putInt(mContext.getContentResolver(),
+ DISABLE_SCREEN_SHARE_PROTECTIONS_FOR_APPS_AND_NOTIFICATIONS, value);
+ }
+ }
+
+ @Test
+ public void testExemptFromStoppingHasOpProjectMedia() throws Exception {
+ MediaProjectionManagerService.MediaProjection mediaProjection = createMediaProjection();
+ doReturn(PackageManager.PERMISSION_DENIED).when(mPackageManager).checkPermission(
+ RECORD_SENSITIVE_CONTENT, mediaProjection.packageName);
+ doReturn(AppOpsManager.MODE_ALLOWED).when(mAppOpsManager)
+ .noteOpNoThrow(eq(AppOpsManager.OP_PROJECT_MEDIA),
+ eq(mediaProjection.uid), eq(mediaProjection.packageName),
+ nullable(String.class),
+ nullable(String.class));
+ assertThat(mStopController.isExemptFromStopping(mediaProjection,
+ MediaProjectionStopController.STOP_REASON_UNKNOWN)).isTrue();
+ }
+
+ @Test
+ public void testExemptFromStoppingHasAppStreamingRole() throws Exception {
+ runWithRole(
+ AssociationRequest.DEVICE_PROFILE_APP_STREAMING,
+ () -> {
+ try {
+ MediaProjectionManagerService.MediaProjection mediaProjection =
+ createMediaProjection();
+ doReturn(PackageManager.PERMISSION_DENIED).when(
+ mPackageManager).checkPermission(
+ RECORD_SENSITIVE_CONTENT, mediaProjection.packageName);
+ assertThat(mStopController.isExemptFromStopping(mediaProjection,
+ MediaProjectionStopController.STOP_REASON_UNKNOWN)).isTrue();
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ });
+ }
+
+ @Test
+ public void testExemptFromStoppingIsBugreportAllowlisted() throws Exception {
+ ArraySet<String> packages = SystemConfig.getInstance().getBugreportWhitelistedPackages();
+ if (packages.isEmpty()) {
+ return;
+ }
+ MediaProjectionManagerService.MediaProjection mediaProjection = createMediaProjection(
+ packages.valueAt(0));
+ doReturn(PackageManager.PERMISSION_DENIED).when(mPackageManager).checkPermission(
+ RECORD_SENSITIVE_CONTENT, mediaProjection.packageName);
+ assertThat(mStopController.isExemptFromStopping(mediaProjection,
+ MediaProjectionStopController.STOP_REASON_UNKNOWN)).isTrue();
+ }
+
+ @Test
+ public void testExemptFromStoppingHasNoDisplay() throws Exception {
+ MediaProjectionManagerService.MediaProjection mediaProjection = createMediaProjection(
+ PACKAGE_NAME);
+ doReturn(PackageManager.PERMISSION_DENIED).when(mPackageManager).checkPermission(
+ RECORD_SENSITIVE_CONTENT, mediaProjection.packageName);
+ assertThat(mStopController.isExemptFromStopping(mediaProjection,
+ MediaProjectionStopController.STOP_REASON_UNKNOWN)).isTrue();
+ }
+
+ @Test
+ public void testExemptFromStoppingHasRecordSensitiveContentPermission() throws Exception {
+ MediaProjectionManagerService.MediaProjection mediaProjection = createMediaProjection();
+ doReturn(PackageManager.PERMISSION_GRANTED).when(mPackageManager).checkPermission(
+ RECORD_SENSITIVE_CONTENT, mediaProjection.packageName);
+ assertThat(mStopController.isExemptFromStopping(mediaProjection,
+ MediaProjectionStopController.STOP_REASON_UNKNOWN)).isTrue();
+ }
+
+ @Test
+ public void testExemptFromStoppingIsFalse() throws Exception {
+ MediaProjectionManagerService.MediaProjection mediaProjection = createMediaProjection();
+ mediaProjection.notifyVirtualDisplayCreated(1);
+ doReturn(PackageManager.PERMISSION_DENIED).when(mPackageManager).checkPermission(
+ RECORD_SENSITIVE_CONTENT, mediaProjection.packageName);
+ assertThat(mStopController.isExemptFromStopping(mediaProjection,
+ MediaProjectionStopController.STOP_REASON_UNKNOWN)).isFalse();
+ }
+
+ @Test
+ @EnableFlags(
+ android.companion.virtualdevice.flags.Flags.FLAG_MEDIA_PROJECTION_KEYGUARD_RESTRICTIONS)
+ public void testKeyguardLockedStateChanged_unlocked() {
+ mStopController.onKeyguardLockedStateChanged(false);
+
+ verify(mStopConsumer, never()).accept(anyInt());
+ }
+
+ @Test
+ @EnableFlags(
+ android.companion.virtualdevice.flags.Flags.FLAG_MEDIA_PROJECTION_KEYGUARD_RESTRICTIONS)
+ public void testKeyguardLockedStateChanged_locked() {
+ mStopController.onKeyguardLockedStateChanged(true);
+
+ verify(mStopConsumer).accept(MediaProjectionStopController.STOP_REASON_KEYGUARD);
+ }
+
+ @Test
+ @EnableFlags(com.android.media.projection.flags.Flags.FLAG_STOP_MEDIA_PROJECTION_ON_CALL_END)
+ public void testCallStateChanged_callStarts() {
+ // Setup call state to false
+ when(mTelecomManager.isInCall()).thenReturn(false);
+ mStopController.callStateChanged();
+
+ clearInvocations(mStopConsumer);
+
+ when(mTelecomManager.isInCall()).thenReturn(true);
+ mStopController.callStateChanged();
+
+ verify(mStopConsumer, never()).accept(anyInt());
+ }
+
+ @Test
+ @EnableFlags(com.android.media.projection.flags.Flags.FLAG_STOP_MEDIA_PROJECTION_ON_CALL_END)
+ public void testCallStateChanged_remainsInCall() {
+ // Setup call state to false
+ when(mTelecomManager.isInCall()).thenReturn(true);
+ mStopController.callStateChanged();
+
+ clearInvocations(mStopConsumer);
+
+ when(mTelecomManager.isInCall()).thenReturn(true);
+ mStopController.callStateChanged();
+
+ verify(mStopConsumer, never()).accept(anyInt());
+ }
+
+ @Test
+ @EnableFlags(com.android.media.projection.flags.Flags.FLAG_STOP_MEDIA_PROJECTION_ON_CALL_END)
+ public void testCallStateChanged_remainsNoCall() {
+ // Setup call state to false
+ when(mTelecomManager.isInCall()).thenReturn(false);
+ mStopController.callStateChanged();
+
+ clearInvocations(mStopConsumer);
+
+ when(mTelecomManager.isInCall()).thenReturn(false);
+ mStopController.callStateChanged();
+
+ verify(mStopConsumer, never()).accept(anyInt());
+ }
+
+ @Test
+ @EnableFlags(com.android.media.projection.flags.Flags.FLAG_STOP_MEDIA_PROJECTION_ON_CALL_END)
+ public void testCallStateChanged_callEnds() {
+ // Setup call state to false
+ when(mTelecomManager.isInCall()).thenReturn(true);
+ mStopController.callStateChanged();
+
+ clearInvocations(mStopConsumer);
+
+ when(mTelecomManager.isInCall()).thenReturn(false);
+ mStopController.callStateChanged();
+
+ verify(mStopConsumer).accept(MediaProjectionStopController.STOP_REASON_CALL_END);
+ }
+
+ @Test
+ @EnableFlags(com.android.media.projection.flags.Flags.FLAG_STOP_MEDIA_PROJECTION_ON_CALL_END)
+ public void testExemptFromStopping_callEnd_callBeforeMediaProjection() throws Exception {
+ when(mTelecomManager.isInCall()).thenReturn(true);
+ mStopController.callStateChanged();
+
+ MediaProjectionManagerService.MediaProjection mediaProjection = createMediaProjection();
+ mediaProjection.notifyVirtualDisplayCreated(1);
+ doReturn(PackageManager.PERMISSION_DENIED).when(mPackageManager).checkPermission(
+ RECORD_SENSITIVE_CONTENT, mediaProjection.packageName);
+
+ assertThat(mStopController.isExemptFromStopping(mediaProjection,
+ MediaProjectionStopController.STOP_REASON_CALL_END)).isFalse();
+ }
+
+ @Test
+ @EnableFlags(com.android.media.projection.flags.Flags.FLAG_STOP_MEDIA_PROJECTION_ON_CALL_END)
+ public void testExemptFromStopping_callEnd_callAfterMediaProjection() throws Exception {
+ MediaProjectionManagerService.MediaProjection mediaProjection = createMediaProjection();
+ mediaProjection.notifyVirtualDisplayCreated(1);
+ doReturn(PackageManager.PERMISSION_DENIED).when(mPackageManager).checkPermission(
+ RECORD_SENSITIVE_CONTENT, mediaProjection.packageName);
+
+ when(mTelecomManager.isInCall()).thenReturn(true);
+ mStopController.callStateChanged();
+
+ assertThat(mStopController.isExemptFromStopping(mediaProjection,
+ MediaProjectionStopController.STOP_REASON_CALL_END)).isTrue();
+ }
+
+ private MediaProjectionManagerService.MediaProjection createMediaProjection()
+ throws NameNotFoundException {
+ return createMediaProjection(PACKAGE_NAME);
+ }
+
+ private MediaProjectionManagerService.MediaProjection createMediaProjection(String packageName)
+ throws NameNotFoundException {
+ doReturn(mAppInfo).when(mPackageManager).getApplicationInfoAsUser(anyString(),
+ any(ApplicationInfoFlags.class), any(UserHandle.class));
+ doReturn(mAppInfo).when(mPackageManager).getApplicationInfoAsUser(Mockito.isNull(),
+ any(ApplicationInfoFlags.class), any(UserHandle.class));
+ return mService.createProjectionInternal(UID, packageName,
+ MediaProjectionManager.TYPE_SCREEN_CAPTURE, false, mContext.getUser(),
+ INVALID_DISPLAY);
+ }
+
+ /**
+ * Run the provided block giving the current context's package the provided role.
+ */
+ @SuppressWarnings("SameParameterValue")
+ private void runWithRole(String role, Runnable block) {
+ Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+ String packageName = mContext.getPackageName();
+ UserHandle user = instrumentation.getTargetContext().getUser();
+ RoleManager roleManager = Objects.requireNonNull(
+ mContext.getSystemService(RoleManager.class));
+ try {
+ CountDownLatch latch = new CountDownLatch(1);
+ instrumentation.getUiAutomation().adoptShellPermissionIdentity(
+ Manifest.permission.MANAGE_ROLE_HOLDERS,
+ Manifest.permission.BYPASS_ROLE_QUALIFICATION);
+
+ roleManager.setBypassingRoleQualification(true);
+ roleManager.addRoleHolderAsUser(role, packageName,
+ /* flags= */ RoleManager.MANAGE_HOLDERS_FLAG_DONT_KILL_APP, user,
+ mContext.getMainExecutor(), success -> {
+ if (success) {
+ latch.countDown();
+ } else {
+ Assert.fail("Couldn't set role for test (failure) " + role);
+ }
+ });
+ assertWithMessage("Couldn't set role for test (timeout) : " + role)
+ .that(latch.await(1, TimeUnit.SECONDS)).isTrue();
+ block.run();
+
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ } finally {
+ roleManager.removeRoleHolderAsUser(role, packageName,
+ /* flags= */ RoleManager.MANAGE_HOLDERS_FLAG_DONT_KILL_APP, user,
+ mContext.getMainExecutor(), (aBool) -> {
+ });
+ roleManager.setBypassingRoleQualification(false);
+ instrumentation.getUiAutomation()
+ .dropShellPermissionIdentity();
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/security/adaptiveauthentication/OWNERS b/services/tests/servicestests/src/com/android/server/security/adaptiveauthentication/OWNERS
deleted file mode 100644
index bc8efa9..0000000
--- a/services/tests/servicestests/src/com/android/server/security/adaptiveauthentication/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-include /services/core/java/com/android/server/security/adaptiveauthentication/OWNERS
\ No newline at end of file
diff --git a/services/tests/servicestests/src/com/android/server/security/adaptiveauthentication/AdaptiveAuthenticationServiceTest.java b/services/tests/servicestests/src/com/android/server/security/authenticationpolicy/AuthenticationPolicyServiceTest.java
similarity index 89%
rename from services/tests/servicestests/src/com/android/server/security/adaptiveauthentication/AdaptiveAuthenticationServiceTest.java
rename to services/tests/servicestests/src/com/android/server/security/authenticationpolicy/AuthenticationPolicyServiceTest.java
index 154494a..ee8eb9b 100644
--- a/services/tests/servicestests/src/com/android/server/security/adaptiveauthentication/AdaptiveAuthenticationServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/security/authenticationpolicy/AuthenticationPolicyServiceTest.java
@@ -14,17 +14,19 @@
* limitations under the License.
*/
-package com.android.server.security.adaptiveauthentication;
+package com.android.server.security.authenticationpolicy;
import static android.adaptiveauth.Flags.FLAG_ENABLE_ADAPTIVE_AUTH;
import static android.adaptiveauth.Flags.FLAG_REPORT_BIOMETRIC_AUTH_ATTEMPTS;
import static android.security.Flags.FLAG_REPORT_PRIMARY_AUTH_ATTEMPTS;
+import static android.security.authenticationpolicy.AuthenticationPolicyManager.ERROR_UNSUPPORTED;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_ADAPTIVE_AUTH_REQUEST;
-import static com.android.server.security.adaptiveauthentication.AdaptiveAuthenticationService.MAX_ALLOWED_FAILED_AUTH_ATTEMPTS;
+import static com.android.server.security.authenticationpolicy.AuthenticationPolicyService.MAX_ALLOWED_FAILED_AUTH_ATTEMPTS;
import static org.junit.Assert.assertEquals;
import static org.junit.Assume.assumeTrue;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
@@ -66,12 +68,12 @@
import org.mockito.MockitoAnnotations;
/**
- * atest FrameworksServicesTests:AdaptiveAuthenticationServiceTest
+ * atest FrameworksServicesTests:AuthenticationPolicyServiceTest
*/
@Presubmit
@SmallTest
@RunWith(AndroidJUnit4.class)
-public class AdaptiveAuthenticationServiceTest {
+public class AuthenticationPolicyServiceTest {
@Rule
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@@ -81,7 +83,7 @@
private static final int REASON_UNKNOWN = 0; // BiometricRequestConstants.RequestReason
private Context mContext;
- private AdaptiveAuthenticationService mAdaptiveAuthenticationService;
+ private AuthenticationPolicyService mAuthenticationPolicyService;
@Mock
LockPatternUtils mLockPatternUtils;
@@ -95,6 +97,8 @@
private WindowManagerInternal mWindowManager;
@Mock
private UserManagerInternal mUserManager;
+ @Mock
+ private SecureLockDeviceServiceInternal mSecureLockDeviceService;
@Captor
ArgumentCaptor<LockSettingsStateListener> mLockSettingsStateListenerCaptor;
@@ -123,10 +127,15 @@
LocalServices.addService(WindowManagerInternal.class, mWindowManager);
LocalServices.removeServiceForTest(UserManagerInternal.class);
LocalServices.addService(UserManagerInternal.class, mUserManager);
+ if (android.security.Flags.secureLockdown()) {
+ LocalServices.removeServiceForTest(SecureLockDeviceServiceInternal.class);
+ LocalServices.addService(SecureLockDeviceServiceInternal.class,
+ mSecureLockDeviceService);
+ }
- mAdaptiveAuthenticationService = new AdaptiveAuthenticationService(
+ mAuthenticationPolicyService = new AuthenticationPolicyService(
mContext, mLockPatternUtils);
- mAdaptiveAuthenticationService.init();
+ mAuthenticationPolicyService.init();
verify(mLockSettings).registerLockSettingsStateListener(
mLockSettingsStateListenerCaptor.capture());
@@ -136,6 +145,12 @@
// Set PRIMARY_USER_ID as the parent of MANAGED_PROFILE_USER_ID
when(mUserManager.getProfileParentId(eq(MANAGED_PROFILE_USER_ID)))
.thenReturn(PRIMARY_USER_ID);
+ if (android.security.Flags.secureLockdown()) {
+ when(mSecureLockDeviceService.enableSecureLockDevice(any()))
+ .thenReturn(ERROR_UNSUPPORTED);
+ when(mSecureLockDeviceService.disableSecureLockDevice(any()))
+ .thenReturn(ERROR_UNSUPPORTED);
+ }
}
@After
@@ -143,6 +158,9 @@
LocalServices.removeServiceForTest(LockSettingsInternal.class);
LocalServices.removeServiceForTest(WindowManagerInternal.class);
LocalServices.removeServiceForTest(UserManagerInternal.class);
+ if (android.security.Flags.secureLockdown()) {
+ LocalServices.removeServiceForTest(SecureLockDeviceServiceInternal.class);
+ }
}
@Test
@@ -318,13 +336,13 @@
private void verifyNotLockDevice(int expectedCntFailedAttempts, int userId) {
assertEquals(expectedCntFailedAttempts,
- mAdaptiveAuthenticationService.mFailedAttemptsForUser.get(userId));
+ mAuthenticationPolicyService.mFailedAttemptsForUser.get(userId));
verify(mWindowManager, never()).lockNow();
}
private void verifyLockDevice(int userId) {
assertEquals(MAX_ALLOWED_FAILED_AUTH_ATTEMPTS,
- mAdaptiveAuthenticationService.mFailedAttemptsForUser.get(userId));
+ mAuthenticationPolicyService.mFailedAttemptsForUser.get(userId));
verify(mLockPatternUtils).requireStrongAuth(
eq(SOME_AUTH_REQUIRED_AFTER_ADAPTIVE_AUTH_REQUEST), eq(userId));
// If userId is MANAGED_PROFILE_USER_ID, the StrongAuthFlag of its parent (PRIMARY_USER_ID)
diff --git a/services/tests/servicestests/src/com/android/server/security/authenticationpolicy/OWNERS b/services/tests/servicestests/src/com/android/server/security/authenticationpolicy/OWNERS
new file mode 100644
index 0000000..4310d1a
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/security/authenticationpolicy/OWNERS
@@ -0,0 +1 @@
+include /services/core/java/com/android/server/security/authenticationpolicy/OWNERS
\ No newline at end of file
diff --git a/services/tests/servicestests/src/com/android/server/supervision/SupervisionServiceTest.kt b/services/tests/servicestests/src/com/android/server/supervision/SupervisionServiceTest.kt
index 79b06236..8290e1c 100644
--- a/services/tests/servicestests/src/com/android/server/supervision/SupervisionServiceTest.kt
+++ b/services/tests/servicestests/src/com/android/server/supervision/SupervisionServiceTest.kt
@@ -20,7 +20,7 @@
import android.content.ComponentName
import android.content.Context
import android.content.pm.UserInfo
-import android.os.Bundle
+import android.os.PersistableBundle
import android.platform.test.annotations.RequiresFlagsEnabled
import android.platform.test.flag.junit.DeviceFlagsValueProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -139,7 +139,7 @@
assertThat(userData.supervisionLockScreenEnabled).isFalse()
assertThat(userData.supervisionLockScreenOptions).isNull()
- service.mInternal.setSupervisionLockscreenEnabledForUser(USER_ID, true, Bundle())
+ service.mInternal.setSupervisionLockscreenEnabledForUser(USER_ID, true, PersistableBundle())
userData = service.getUserDataLocked(USER_ID)
assertThat(userData.supervisionLockScreenEnabled).isTrue()
assertThat(userData.supervisionLockScreenOptions).isNotNull()
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java
index cc02865..6af6542 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java
@@ -84,7 +84,9 @@
import com.android.internal.R;
import com.android.server.UiServiceTestCase;
import com.android.server.notification.GroupHelper.CachedSummary;
+import com.android.server.notification.GroupHelper.FullyQualifiedGroupKey;
import com.android.server.notification.GroupHelper.NotificationAttributes;
+import com.android.server.notification.GroupHelper.NotificationSectioner;
import org.junit.Before;
import org.junit.Rule;
@@ -2298,6 +2300,7 @@
final String pkg = "package";
final String expectedGroupKey_alerting = GroupHelper.getFullAggregateGroupKey(pkg,
AGGREGATE_GROUP_KEY + "AlertingSection", UserHandle.SYSTEM.getIdentifier());
+ final int numNotifications = 2 * AUTOGROUP_AT_COUNT;
int numNotificationChannel1 = 0;
final NotificationChannel channel1 = new NotificationChannel("TEST_CHANNEL_ID1",
"TEST_CHANNEL_ID1", IMPORTANCE_DEFAULT);
@@ -2307,7 +2310,7 @@
final ArrayMap<String, NotificationRecord> summaryByGroup = new ArrayMap<>();
// Post notifications with different channels that autogroup within the same section
NotificationRecord r;
- for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
+ for (int i = 0; i < numNotifications; i++) {
if (i % 2 == 0) {
r = getNotificationRecord(pkg, i, String.valueOf(i),
UserHandle.SYSTEM, "testGrp " + i, false, channel1);
@@ -2324,12 +2327,12 @@
"TEST_CHANNEL_ID1");
verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(),
eq(expectedGroupKey_alerting), anyInt(), eq(expectedSummaryAttr));
- verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString(),
+ verify(mCallback, times(numNotifications)).addAutoGroup(anyString(),
eq(expectedGroupKey_alerting), eq(true));
verify(mCallback, never()).removeAutoGroup(anyString());
verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
- verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyString(),
- any());
+ verify(mCallback, times(numNotifications - AUTOGROUP_AT_COUNT)).updateAutogroupSummary(
+ anyInt(), anyString(), anyString(), any());
Mockito.reset(mCallback);
// Update channel1's importance
@@ -2375,7 +2378,7 @@
final List<NotificationRecord> notificationList = new ArrayList<>();
final String pkg = "package";
final int summaryId = 0;
- final int numChildNotif = 4;
+ final int numChildNotif = 2 * AUTOGROUP_AT_COUNT;
// Create an app-provided group: summary + child notifications
final NotificationChannel channel1 = new NotificationChannel("TEST_CHANNEL_ID1",
@@ -2435,8 +2438,8 @@
eq(expectedGroupKey_social), eq(true));
verify(mCallback, never()).removeAutoGroup(anyString());
verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
- verify(mCallback, times(numChildNotif / 2)).updateAutogroupSummary(anyInt(), anyString(),
- anyString(), any());
+ verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyString(),
+ any());
verify(mCallback, times(numChildNotif)).removeAppProvidedSummaryOnClassification(
anyString(), eq(originalAppGroupKey));
}
@@ -2513,9 +2516,10 @@
final List<NotificationRecord> notificationList = new ArrayList<>();
final ArrayMap<String, NotificationRecord> summaryByGroup = new ArrayMap<>();
final String pkg = "package";
+ final int numChildNotifications = AUTOGROUP_AT_COUNT;
// Post singleton groups, above forced group limit
- for (int i = 0; i < AUTOGROUP_SINGLETONS_AT_COUNT; i++) {
+ for (int i = 0; i < numChildNotifications; i++) {
NotificationRecord summary = getNotificationRecord(pkg, i,
String.valueOf(i), UserHandle.SYSTEM, "testGrp " + i, true);
notificationList.add(summary);
@@ -2545,13 +2549,13 @@
// Check that notifications are forced grouped and app-provided summaries are canceled
verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(),
eq(expectedGroupKey_social), anyInt(), eq(expectedSummaryAttr_social));
- verify(mCallback, times(AUTOGROUP_SINGLETONS_AT_COUNT)).addAutoGroup(anyString(),
+ verify(mCallback, times(numChildNotifications)).addAutoGroup(anyString(),
eq(expectedGroupKey_social), eq(true));
verify(mCallback, never()).removeAutoGroup(anyString());
verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
- verify(mCallback, times(1)).updateAutogroupSummary(anyInt(), anyString(), anyString(),
+ verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyString(),
any());
- verify(mCallback, times(2)).removeAppProvidedSummaryOnClassification(
+ verify(mCallback, times(numChildNotifications)).removeAppProvidedSummaryOnClassification(
anyString(), anyString());
// Adjust group key and cancel summaries
@@ -2593,14 +2597,16 @@
AGGREGATE_GROUP_KEY + "AlertingSection", UserHandle.SYSTEM.getIdentifier());
String expectedTriggeringKey = null;
// Post singleton groups, above forced group limit
- for (int i = 0; i < AUTOGROUP_SINGLETONS_AT_COUNT; i++) {
+ for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
NotificationRecord summary = getNotificationRecord(pkg, i,
String.valueOf(i), UserHandle.SYSTEM, "testGrp " + i, true);
notificationList.add(summary);
NotificationRecord child = getNotificationRecord(pkg, i + 42,
String.valueOf(i + 42), UserHandle.SYSTEM, "testGrp " + i, false);
notificationList.add(child);
- expectedTriggeringKey = child.getKey();
+ if (i == AUTOGROUP_SINGLETONS_AT_COUNT - 1) {
+ expectedTriggeringKey = child.getKey();
+ }
summaryByGroup.put(summary.getGroupKey(), summary);
mGroupHelper.onNotificationPostedWithDelay(child, notificationList, summaryByGroup);
summary.isCanceled = true; // simulate removing the app summary
@@ -2611,14 +2617,8 @@
verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg),
eq(expectedTriggeringKey), eq(expectedGroupKey_alerting), anyInt(),
eq(getNotificationAttributes(BASE_FLAGS)));
- verify(mCallback, times(AUTOGROUP_SINGLETONS_AT_COUNT)).addAutoGroup(anyString(),
- eq(expectedGroupKey_alerting), eq(true));
verify(mCallback, never()).removeAutoGroup(anyString());
verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
- verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyString(),
- any());
- verify(mCallback, times(AUTOGROUP_SINGLETONS_AT_COUNT)).removeAppProvidedSummary(
- anyString());
assertThat(mGroupHelper.findCanceledSummary(pkg, String.valueOf(0), 0,
UserHandle.SYSTEM.getIdentifier())).isNotNull();
assertThat(mGroupHelper.findCanceledSummary(pkg, String.valueOf(1), 1,
@@ -2645,12 +2645,12 @@
// Check that all notifications are moved to the social section group
verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(),
eq(expectedGroupKey_social), anyInt(), eq(expectedSummaryAttr_social));
- verify(mCallback, times(AUTOGROUP_SINGLETONS_AT_COUNT)).addAutoGroup(anyString(),
+ verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString(),
eq(expectedGroupKey_social), eq(true));
// Check that the alerting section group is removed
verify(mCallback, times(1)).removeAutoGroupSummary(anyInt(), eq(pkg),
eq(expectedGroupKey_alerting));
- verify(mCallback, times(AUTOGROUP_SINGLETONS_AT_COUNT)).updateAutogroupSummary(anyInt(),
+ verify(mCallback, times(AUTOGROUP_AT_COUNT - 1)).updateAutogroupSummary(anyInt(),
anyString(), anyString(), any());
}
@@ -2666,7 +2666,7 @@
final String pkg = "package";
final int summaryId = 0;
- final int numChildren = 3;
+ final int numChildren = AUTOGROUP_AT_COUNT;
// Post a regular/valid group: summary + notifications
NotificationRecord summary = getNotificationRecord(pkg, summaryId,
String.valueOf(summaryId), UserHandle.SYSTEM, "testGrp", true);
@@ -2706,13 +2706,211 @@
eq(true));
verify(mCallback, never()).removeAutoGroup(anyString());
verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
- verify(mCallback, times(numChildren - 1)).updateAutogroupSummary(anyInt(), anyString(),
- anyString(), any());
+ verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyString(),
+ any());
verify(mCallback, times(numChildren)).removeAppProvidedSummaryOnClassification(anyString(),
anyString());
}
@Test
+ @EnableFlags({FLAG_NOTIFICATION_FORCE_GROUPING,
+ FLAG_NOTIFICATION_REGROUP_ON_CLASSIFICATION,
+ FLAG_NOTIFICATION_CLASSIFICATION})
+ public void testUnbundleNotification_originalSummaryMissing_autogroupInNewSection() {
+ // Check that unbundled notifications are moved to the original section and aggregated
+ // with existing autogrouped notifications
+ final List<NotificationRecord> notificationList = new ArrayList<>();
+ final ArrayMap<String, NotificationRecord> summaryByGroup = new ArrayMap<>();
+ final String pkg = "package";
+
+ final int summaryId = 0;
+ final int numChildren = AUTOGROUP_AT_COUNT - 1;
+ // Post a regular/valid group: summary + notifications (one less than autogroup limit)
+ NotificationRecord summary = getNotificationRecord(pkg, summaryId,
+ String.valueOf(summaryId), UserHandle.SYSTEM, "testGrp", true);
+ notificationList.add(summary);
+ summaryByGroup.put(summary.getGroupKey(), summary);
+ final String originalAppGroupKey = summary.getGroupKey();
+ final NotificationChannel originalChannel = summary.getChannel();
+ for (int i = 0; i < numChildren; i++) {
+ NotificationRecord child = getNotificationRecord(pkg, i + 42, String.valueOf(i + 42),
+ UserHandle.SYSTEM, "testGrp", false);
+ notificationList.add(child);
+ mGroupHelper.onNotificationPostedWithDelay(child, notificationList, summaryByGroup);
+ }
+
+ // Classify/bundle all child notifications: original group & summary is removed
+ final NotificationChannel socialChannel = new NotificationChannel(
+ NotificationChannel.SOCIAL_MEDIA_ID, NotificationChannel.SOCIAL_MEDIA_ID,
+ IMPORTANCE_DEFAULT);
+ for (NotificationRecord record: notificationList) {
+ if (record.getOriginalGroupKey().contains("testGrp")
+ && record.getNotification().isGroupChild()) {
+ record.updateNotificationChannel(socialChannel);
+ mGroupHelper.onChannelUpdated(record);
+ }
+ }
+
+ // Check that no autogroup summaries were created for the social section
+ verify(mCallback, never()).addAutoGroupSummary(anyInt(), anyString(), anyString(),
+ anyString(), anyInt(), any());
+ verify(mCallback, never()).addAutoGroup(anyString(), anyString(), anyBoolean());
+ verify(mCallback, never()).removeAutoGroup(anyString());
+ verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
+ verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyString(),
+ any());
+ verify(mCallback, times(numChildren)).removeAppProvidedSummaryOnClassification(
+ anyString(), eq(originalAppGroupKey));
+
+ // Cancel summary
+ summary.isCanceled = true;
+ summaryByGroup.clear();
+ notificationList.remove(summary);
+
+ // Add 1 ungrouped notification in the original section
+ NotificationRecord ungroupedNotification = getNotificationRecord(pkg, 4242,
+ String.valueOf(4242), UserHandle.SYSTEM);
+ notificationList.add(ungroupedNotification);
+ mGroupHelper.onNotificationPosted(ungroupedNotification, false);
+
+ // Unbundle the bundled notifications => notifications are moved back to the original group
+ // and an aggregate group is created because autogroup limit is reached
+ reset(mCallback);
+ for (NotificationRecord record: notificationList) {
+ if (record.getNotification().isGroupChild()
+ && record.getOriginalGroupKey().contains("testGrp")
+ && NotificationChannel.SYSTEM_RESERVED_IDS.contains(
+ record.getChannel().getId())) {
+ record.updateNotificationChannel(originalChannel);
+ mGroupHelper.onNotificationUnbundled(record, false);
+ }
+ }
+
+ // Check that a new aggregate group is created
+ final String expectedGroupKey_alerting = GroupHelper.getFullAggregateGroupKey(pkg,
+ AGGREGATE_GROUP_KEY + "AlertingSection", UserHandle.SYSTEM.getIdentifier());
+ verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(),
+ eq(expectedGroupKey_alerting), anyInt(), any());
+ verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString(),
+ eq(expectedGroupKey_alerting), eq(true));
+ verify(mCallback, never()).removeAutoGroup(anyString());
+ verify(mCallback, times(numChildren)).removeAutoGroupSummary(anyInt(), anyString(),
+ anyString());
+ verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyString(),
+ any());
+ }
+
+ @Test
+ @EnableFlags({FLAG_NOTIFICATION_FORCE_GROUPING,
+ FLAG_NOTIFICATION_REGROUP_ON_CLASSIFICATION,
+ FLAG_NOTIFICATION_CLASSIFICATION})
+ public void testUnbundleNotification_originalSummaryExists() {
+ // Check that unbundled notifications are moved to the original section and original group
+ // when the original summary is still present
+ final List<NotificationRecord> notificationList = new ArrayList<>();
+ final ArrayMap<String, NotificationRecord> summaryByGroup = new ArrayMap<>();
+ final String pkg = "package";
+
+ final int summaryId = 0;
+ final int numChildren = AUTOGROUP_AT_COUNT + 1;
+ // Post a regular/valid group: summary + notifications
+ NotificationRecord summary = getNotificationRecord(pkg, summaryId,
+ String.valueOf(summaryId), UserHandle.SYSTEM, "testGrp", true);
+ notificationList.add(summary);
+ summaryByGroup.put(summary.getGroupKey(), summary);
+ final String originalAppGroupKey = summary.getGroupKey();
+ final NotificationChannel originalChannel = summary.getChannel();
+ for (int i = 0; i < numChildren; i++) {
+ NotificationRecord child = getNotificationRecord(pkg, i + 42, String.valueOf(i + 42),
+ UserHandle.SYSTEM, "testGrp", false);
+ notificationList.add(child);
+ mGroupHelper.onNotificationPostedWithDelay(child, notificationList, summaryByGroup);
+ }
+
+ // Classify/bundle child notifications: all except one, to keep the original group
+ final NotificationChannel socialChannel = new NotificationChannel(
+ NotificationChannel.SOCIAL_MEDIA_ID, NotificationChannel.SOCIAL_MEDIA_ID,
+ IMPORTANCE_DEFAULT);
+ final String expectedGroupKey_social = GroupHelper.getFullAggregateGroupKey(pkg,
+ AGGREGATE_GROUP_KEY + "SocialSection", UserHandle.SYSTEM.getIdentifier());
+ final NotificationAttributes expectedSummaryAttr_social = new NotificationAttributes(
+ BASE_FLAGS, mSmallIcon, COLOR_DEFAULT, DEFAULT_VISIBILITY, DEFAULT_GROUP_ALERT,
+ NotificationChannel.SOCIAL_MEDIA_ID);
+ int numChildrenBundled = 0;
+ for (NotificationRecord record: notificationList) {
+ if (record.getOriginalGroupKey().contains("testGrp")
+ && record.getNotification().isGroupChild()) {
+ record.updateNotificationChannel(socialChannel);
+ mGroupHelper.onChannelUpdated(record);
+ numChildrenBundled++;
+ if (numChildrenBundled == AUTOGROUP_AT_COUNT) {
+ break;
+ }
+ }
+ }
+
+ // Check that 1 autogroup summaries were created for the social section
+ verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(),
+ eq(expectedGroupKey_social), anyInt(), eq(expectedSummaryAttr_social));
+ verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString(),
+ eq(expectedGroupKey_social), eq(true));
+ verify(mCallback, never()).removeAutoGroup(anyString());
+ verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
+ verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyString(),
+ any());
+ verify(mCallback, times(AUTOGROUP_AT_COUNT)).removeAppProvidedSummaryOnClassification(
+ anyString(), eq(originalAppGroupKey));
+
+ // Adjust group key and cancel summaries
+ for (NotificationRecord record: notificationList) {
+ if (record.getNotification().isGroupSummary()) {
+ record.isCanceled = true;
+ } else if (record.getOriginalGroupKey().contains("testGrp")
+ && NotificationChannel.SYSTEM_RESERVED_IDS.contains(
+ record.getChannel().getId())) {
+ record.setOverrideGroupKey(expectedGroupKey_social);
+ }
+ }
+
+ // Add 1 ungrouped notification in the original section
+ NotificationRecord ungroupedNotification = getNotificationRecord(pkg, 4242,
+ String.valueOf(4242), UserHandle.SYSTEM);
+ notificationList.add(ungroupedNotification);
+ mGroupHelper.onNotificationPosted(ungroupedNotification, false);
+
+ // Unbundle the bundled notifications => social section summary is destroyed
+ // and notifications are moved back to the original group
+ reset(mCallback);
+ for (NotificationRecord record: notificationList) {
+ if (record.getNotification().isGroupChild()
+ && record.getOriginalGroupKey().contains("testGrp")
+ && NotificationChannel.SYSTEM_RESERVED_IDS.contains(
+ record.getChannel().getId())) {
+ record.updateNotificationChannel(originalChannel);
+ mGroupHelper.onNotificationUnbundled(record, true);
+ }
+ }
+
+ // Check that the autogroup summary for the social section was removed
+ // and that no new autogroup summaries were created
+ verify(mCallback, never()).addAutoGroupSummary(anyInt(), anyString(), anyString(),
+ anyString(), anyInt(), any());
+ verify(mCallback, never()).addAutoGroup(anyString(), anyString(), anyBoolean());
+ verify(mCallback, never()).removeAutoGroup(anyString());
+ verify(mCallback, times(1)).removeAutoGroupSummary(anyInt(), eq(pkg),
+ eq(expectedGroupKey_social));
+ verify(mCallback, times(AUTOGROUP_AT_COUNT - 1)).updateAutogroupSummary(anyInt(), eq(pkg),
+ eq(expectedGroupKey_social), any());
+
+ for (NotificationRecord record: notificationList) {
+ if (record.getNotification().isGroupChild()
+ && record.getOriginalGroupKey().contains("testGrp")) {
+ assertThat(record.getSbn().getOverrideGroupKey()).isNull();
+ }
+ }
+ }
+
+ @Test
@EnableFlags(FLAG_NOTIFICATION_FORCE_GROUPING)
public void testMoveAggregateGroups_updateChannel_groupsUngrouped() {
final String pkg = "package";
@@ -3059,6 +3257,120 @@
@Test
@EnableFlags(FLAG_NOTIFICATION_FORCE_GROUPING)
@DisableFlags(FLAG_NOTIFICATION_FORCE_GROUP_CONVERSATIONS)
+ public void testNonGroupableChildren_singletonGroups_disableConversations() {
+ // Check that singleton groups with children that are not groupable, is not grouped
+ // Even though the group summary is a regular (alerting) notification, the children are
+ // conversations => the group should not be forced grouped.
+ final List<NotificationRecord> notificationList = new ArrayList<>();
+ final ArrayMap<String, NotificationRecord> summaryByGroup = new ArrayMap<>();
+ final String pkg = "package";
+
+ // Trigger notification, ungrouped
+ final int triggerId = 1;
+ NotificationRecord triggerNotification = getNotificationRecord(pkg, triggerId,
+ String.valueOf(triggerId), UserHandle.SYSTEM);
+ notificationList.add(triggerNotification);
+ final NotificationSectioner triggerSection = GroupHelper.getSection(triggerNotification);
+ final FullyQualifiedGroupKey triggerFullAggregateGroupKey = new FullyQualifiedGroupKey(
+ triggerNotification.getUserId(), triggerNotification.getSbn().getPackageName(),
+ triggerSection);
+
+ // Add singleton group with alerting child
+ final String groupName_valid = "testGrp_valid";
+ final int summaryId_valid = 0;
+ NotificationRecord summary = getNotificationRecord(pkg, summaryId_valid,
+ String.valueOf(summaryId_valid), UserHandle.SYSTEM, groupName_valid, true);
+ notificationList.add(summary);
+ summaryByGroup.put(summary.getGroupKey(), summary);
+ final String groupKey_valid = summary.getGroupKey();
+ NotificationRecord child = getNotificationRecord(pkg, summaryId_valid + 42,
+ String.valueOf(summaryId_valid + 42), UserHandle.SYSTEM, groupName_valid, false);
+ notificationList.add(child);
+
+ // Add singleton group with conversation child
+ final String groupName_invalid = "testGrp_invalid";
+ final int summaryId_invalid = 100;
+ summary = getNotificationRecord(pkg, summaryId_invalid,
+ String.valueOf(summaryId_invalid), UserHandle.SYSTEM, groupName_invalid, true);
+ notificationList.add(summary);
+ final String groupKey_invalid = summary.getGroupKey();
+ summaryByGroup.put(summary.getGroupKey(), summary);
+ child = getNotificationRecord(pkg, summaryId_invalid + 42,
+ String.valueOf(summaryId_invalid + 42), UserHandle.SYSTEM, groupName_invalid,
+ false);
+ child = spy(child);
+ when(child.isConversation()).thenReturn(true);
+ notificationList.add(child);
+
+ // Check that the invalid group will not be force grouped
+ final ArrayMap<String, NotificationRecord> sparseGroups = mGroupHelper.getSparseGroups(
+ triggerFullAggregateGroupKey, notificationList, summaryByGroup, triggerSection);
+ assertThat(sparseGroups).containsKey(groupKey_valid);
+ assertThat(sparseGroups).doesNotContainKey(groupKey_invalid);
+ }
+
+ @Test
+ @EnableFlags({FLAG_NOTIFICATION_FORCE_GROUPING, FLAG_NOTIFICATION_FORCE_GROUP_CONVERSATIONS})
+ public void testNonGroupableChildren_singletonGroups_enableConversations() {
+ // Check that singleton groups with children that are not groupable, is not grouped
+ // Conversations are groupable (FLAG_NOTIFICATION_FORCE_GROUP_CONVERSATIONS is enabled)
+ // The invalid group is the alerting notifications: because the triggering notifications'
+ // section is Conversations, so the alerting group should be skipped.
+ final List<NotificationRecord> notificationList = new ArrayList<>();
+ final ArrayMap<String, NotificationRecord> summaryByGroup = new ArrayMap<>();
+ final String pkg = "package";
+
+ // Trigger notification, ungrouped conversation
+ final int triggerId = 1;
+ NotificationRecord triggerNotification = getNotificationRecord(pkg, triggerId,
+ String.valueOf(triggerId), UserHandle.SYSTEM);
+ triggerNotification = spy(triggerNotification);
+ when(triggerNotification.isConversation()).thenReturn(true);
+ notificationList.add(triggerNotification);
+ final NotificationSectioner triggerSection = GroupHelper.getSection(triggerNotification);
+ final FullyQualifiedGroupKey triggerFullAggregateGroupKey = new FullyQualifiedGroupKey(
+ triggerNotification.getUserId(), triggerNotification.getSbn().getPackageName(),
+ triggerSection);
+
+ // Add singleton group with conversation child
+ final String groupName_valid = "testGrp_valid";
+ final int summaryId_valid = 0;
+ NotificationRecord summary = getNotificationRecord(pkg, summaryId_valid,
+ String.valueOf(summaryId_valid), UserHandle.SYSTEM, groupName_valid, true);
+ summary = spy(summary);
+ when(summary.isConversation()).thenReturn(true);
+ notificationList.add(summary);
+ summaryByGroup.put(summary.getGroupKey(), summary);
+ final String groupKey_valid = summary.getGroupKey();
+ NotificationRecord child = getNotificationRecord(pkg, summaryId_valid + 42,
+ String.valueOf(summaryId_valid + 42), UserHandle.SYSTEM, groupName_valid, false);
+ child = spy(child);
+ when(child.isConversation()).thenReturn(true);
+ notificationList.add(child);
+
+ // Add singleton group with non-conversation child
+ final String groupName_invalid = "testGrp_invalid";
+ final int summaryId_invalid = 100;
+ summary = getNotificationRecord(pkg, summaryId_invalid,
+ String.valueOf(summaryId_invalid), UserHandle.SYSTEM, groupName_invalid, true);
+ notificationList.add(summary);
+ final String groupKey_invalid = summary.getGroupKey();
+ summaryByGroup.put(summary.getGroupKey(), summary);
+ child = getNotificationRecord(pkg, summaryId_invalid + 42,
+ String.valueOf(summaryId_invalid + 42), UserHandle.SYSTEM, groupName_invalid,
+ false);
+ notificationList.add(child);
+
+ // Check that the invalid group will not be force grouped
+ final ArrayMap<String, NotificationRecord> sparseGroups = mGroupHelper.getSparseGroups(
+ triggerFullAggregateGroupKey, notificationList, summaryByGroup, triggerSection);
+ assertThat(sparseGroups).containsKey(groupKey_valid);
+ assertThat(sparseGroups).doesNotContainKey(groupKey_invalid);
+ }
+
+ @Test
+ @EnableFlags(FLAG_NOTIFICATION_FORCE_GROUPING)
+ @DisableFlags(FLAG_NOTIFICATION_FORCE_GROUP_CONVERSATIONS)
public void testNonGroupableNotifications() {
// Check that there is no valid section for: conversations, calls, foreground services
NotificationRecord notification_conversation = mock(NotificationRecord.class);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
index 6eb2f71..9eddcc9 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
@@ -720,6 +720,7 @@
}
@Test
+ @EnableFlags(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION)
public void testDefaultAllowedKeyAdjustments_readWriteXml() throws Exception {
mAssistants.loadDefaultsFromConfig(true);
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 704c1b8..e6b4bc9 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -17178,8 +17178,6 @@
@Test
@EnableFlags(android.app.Flags.FLAG_API_RICH_ONGOING)
public void testSetCanBePromoted_granted() throws Exception {
- mContext.getTestablePermissions().setPermission(
- android.Manifest.permission.USE_COLORIZED_NOTIFICATIONS, PERMISSION_GRANTED);
// qualifying posted notification
Notification n = new Notification.Builder(mContext, mTestNotificationChannel.getId())
.setSmallIcon(android.R.drawable.sym_def_app_icon)
@@ -17254,8 +17252,6 @@
@Test
@EnableFlags(android.app.Flags.FLAG_API_RICH_ONGOING)
public void testSetCanBePromoted_granted_onlyNotifiesOnce() throws Exception {
- mContext.getTestablePermissions().setPermission(
- android.Manifest.permission.USE_COLORIZED_NOTIFICATIONS, PERMISSION_GRANTED);
// qualifying posted notification
Notification n = new Notification.Builder(mContext, mTestNotificationChannel.getId())
.setSmallIcon(android.R.drawable.sym_def_app_icon)
@@ -17285,8 +17281,6 @@
@Test
@EnableFlags(android.app.Flags.FLAG_API_RICH_ONGOING)
public void testSetCanBePromoted_revoked() throws Exception {
- mContext.getTestablePermissions().setPermission(
- android.Manifest.permission.USE_COLORIZED_NOTIFICATIONS, PERMISSION_GRANTED);
// start from true state
mBinderService.setCanBePromoted(mPkg, mUid, true, true);
@@ -17350,8 +17344,6 @@
@Test
@EnableFlags(android.app.Flags.FLAG_API_RICH_ONGOING)
public void testSetCanBePromoted_revoked_onlyNotifiesOnce() throws Exception {
- mContext.getTestablePermissions().setPermission(
- android.Manifest.permission.USE_COLORIZED_NOTIFICATIONS, PERMISSION_GRANTED);
// start from true state
mBinderService.setCanBePromoted(mPkg, mUid, true, true);
@@ -17387,8 +17379,6 @@
public void testPostPromotableNotification() throws Exception {
mBinderService.setCanBePromoted(mPkg, mUid, true, true);
assertThat(mBinderService.appCanBePromoted(mPkg, mUid)).isTrue();
- mContext.getTestablePermissions().setPermission(
- android.Manifest.permission.USE_COLORIZED_NOTIFICATIONS, PERMISSION_GRANTED);
Notification n = new Notification.Builder(mContext, mTestNotificationChannel.getId())
.setSmallIcon(android.R.drawable.sym_def_app_icon)
@@ -17415,8 +17405,6 @@
@Test
@EnableFlags(android.app.Flags.FLAG_API_RICH_ONGOING)
public void testPostPromotableNotification_noPermission() throws Exception {
- mContext.getTestablePermissions().setPermission(
- android.Manifest.permission.USE_COLORIZED_NOTIFICATIONS, PERMISSION_GRANTED);
Notification n = new Notification.Builder(mContext, mTestNotificationChannel.getId())
.setSmallIcon(android.R.drawable.sym_def_app_icon)
.setStyle(new Notification.BigTextStyle().setBigContentTitle("BIG"))
@@ -17444,8 +17432,6 @@
@EnableFlags(android.app.Flags.FLAG_API_RICH_ONGOING)
public void testPostPromotableNotification_unimportantNotification() throws Exception {
mBinderService.setCanBePromoted(mPkg, mUid, true, true);
- mContext.getTestablePermissions().setPermission(
- android.Manifest.permission.USE_COLORIZED_NOTIFICATIONS, PERMISSION_GRANTED);
Notification n = new Notification.Builder(mContext, mMinChannel.getId())
.setSmallIcon(android.R.drawable.sym_def_app_icon)
.setStyle(new Notification.BigTextStyle().setBigContentTitle("BIG"))
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
index 020670d..bf61d06 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -72,6 +72,7 @@
import static android.service.notification.ZenModeConfig.ZenRule.OVERRIDE_ACTIVATE;
import static android.service.notification.ZenModeConfig.ZenRule.OVERRIDE_DEACTIVATE;
import static android.service.notification.ZenModeConfig.ZenRule.OVERRIDE_NONE;
+import static android.service.notification.ZenModeConfig.implicitRuleId;
import static android.service.notification.ZenPolicy.PEOPLE_TYPE_CONTACTS;
import static android.service.notification.ZenPolicy.PEOPLE_TYPE_NONE;
import static android.service.notification.ZenPolicy.PEOPLE_TYPE_STARRED;
@@ -201,9 +202,6 @@
import org.mockito.MockitoAnnotations;
import org.xmlpull.v1.XmlPullParserException;
-import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
-import platform.test.runner.parameterized.Parameters;
-
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
@@ -223,6 +221,9 @@
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
+import platform.test.runner.parameterized.Parameters;
+
@SmallTest
@SuppressLint("GuardedBy") // It's ok for this test to access guarded methods from the service.
@RunWith(ParameterizedAndroidJunit4.class)
@@ -7067,6 +7068,50 @@
assertThat(getZenRule(ruleId).getConditionOverride()).isEqualTo(OVERRIDE_NONE);
}
+ @Test
+ @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI})
+ public void setAutomaticZenRuleState_implicitRuleManualActivation_doesNotUseOverride() {
+ mContext.getTestablePermissions().setPermission(Manifest.permission.MANAGE_NOTIFICATIONS,
+ PERMISSION_GRANTED); // So that canManageAZR succeeds.
+ mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(UserHandle.CURRENT, CUSTOM_PKG_NAME,
+ CUSTOM_PKG_UID, ZEN_MODE_IMPORTANT_INTERRUPTIONS);
+ mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(UserHandle.CURRENT, CUSTOM_PKG_NAME,
+ CUSTOM_PKG_UID, ZEN_MODE_OFF);
+ ZenRule implicitRule = getZenRule(implicitRuleId(CUSTOM_PKG_NAME));
+ assertThat(implicitRule.isActive()).isFalse();
+
+ mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, implicitRule.id,
+ new Condition(implicitRule.conditionId, "on!", STATE_TRUE, SOURCE_USER_ACTION),
+ ORIGIN_USER_IN_SYSTEMUI, SYSTEM_UID);
+
+ implicitRule = getZenRule(implicitRuleId(CUSTOM_PKG_NAME));
+ assertThat(mZenModeHelper.getAutomaticZenRuleState(UserHandle.CURRENT, implicitRule.id))
+ .isEqualTo(STATE_TRUE);
+ assertThat(implicitRule.isActive()).isTrue();
+ assertThat(implicitRule.getConditionOverride()).isEqualTo(OVERRIDE_NONE);
+ }
+
+ @Test
+ @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI})
+ public void setAutomaticZenRuleState_implicitRuleManualDeactivation_doesNotUseOverride() {
+ mContext.getTestablePermissions().setPermission(Manifest.permission.MANAGE_NOTIFICATIONS,
+ PERMISSION_GRANTED); // So that canManageAZR succeeds.
+ mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(UserHandle.CURRENT, CUSTOM_PKG_NAME,
+ CUSTOM_PKG_UID, ZEN_MODE_IMPORTANT_INTERRUPTIONS);
+ ZenRule implicitRule = getZenRule(implicitRuleId(CUSTOM_PKG_NAME));
+ assertThat(implicitRule.isActive()).isTrue();
+
+ mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, implicitRule.id,
+ new Condition(implicitRule.conditionId, "off!", STATE_FALSE, SOURCE_USER_ACTION),
+ ORIGIN_USER_IN_SYSTEMUI, SYSTEM_UID);
+
+ implicitRule = getZenRule(implicitRuleId(CUSTOM_PKG_NAME));
+ assertThat(mZenModeHelper.getAutomaticZenRuleState(UserHandle.CURRENT, implicitRule.id))
+ .isEqualTo(STATE_FALSE);
+ assertThat(implicitRule.isActive()).isFalse();
+ assertThat(implicitRule.getConditionOverride()).isEqualTo(OVERRIDE_NONE);
+ }
+
private ZenRule getZenRule(String ruleId) {
return checkNotNull(mZenModeHelper.mConfig.automaticRules.get(ruleId),
"Didn't find rule with id %s", ruleId);
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java
index c7f4056..eb44daa 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java
@@ -747,6 +747,23 @@
}
@Test
+ @DisableFlags(Flags.FLAG_PRIMITIVE_COMPOSITION_ABSOLUTE_DELAY)
+ public void vibrate_singleVibratorComposedAndNoCapability_triggersHalAndReturnsUnsupported() {
+ VibrationEffect effect = VibrationEffect.startComposition()
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f)
+ .compose();
+ HalVibration vibration = startThreadAndDispatcher(effect);
+ waitForCompletion();
+
+ verify(mManagerHooks).noteVibratorOn(eq(UID), eq(0L));
+ verify(mManagerHooks, never()).noteVibratorOff(eq(UID));
+ verify(mControllerCallbacks, never()).onComplete(eq(VIBRATOR_ID), eq(vibration.id));
+ verifyCallbacksTriggered(vibration, Status.IGNORED_UNSUPPORTED);
+ assertTrue(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibration.id).isEmpty());
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_PRIMITIVE_COMPOSITION_ABSOLUTE_DELAY)
public void vibrate_singleVibratorComposedAndNoCapability_ignoresVibration() {
VibrationEffect effect = VibrationEffect.startComposition()
.addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f)
@@ -887,7 +904,7 @@
fakeVibrator.setMaxEnvelopeEffectSize(10);
fakeVibrator.setMinEnvelopeEffectControlPointDurationMillis(20);
- VibrationEffect effect = VibrationEffect.startWaveformEnvelope()
+ VibrationEffect effect = new VibrationEffect.WaveformEnvelopeBuilder()
.addControlPoint(/*amplitude=*/ 0.1f, /*frequencyHz=*/ 60f, /*timeMillis=*/ 20)
.addControlPoint(/*amplitude=*/ 0.3f, /*frequencyHz=*/ 100f, /*timeMillis=*/ 30)
.addControlPoint(/*amplitude=*/ 0.4f, /*frequencyHz=*/ 120f, /*timeMillis=*/ 20)
@@ -922,12 +939,14 @@
fakeVibrator.setMaxEnvelopeEffectSize(10);
fakeVibrator.setMinEnvelopeEffectControlPointDurationMillis(20);
- VibrationEffect effect = VibrationEffect.startWaveformEnvelope(/*initialFrequency=*/ 30)
+ VibrationEffect effect = new VibrationEffect.WaveformEnvelopeBuilder()
+ .setInitialFrequencyHz(/*initialFrequencyHz=*/ 30)
.addControlPoint(/*amplitude=*/ 0.1f, /*frequencyHz=*/ 60f, /*timeMillis=*/ 20)
.addControlPoint(/*amplitude=*/ 0.3f, /*frequencyHz=*/ 100f, /*timeMillis=*/ 30)
.addControlPoint(/*amplitude=*/ 0.4f, /*frequencyHz=*/ 120f, /*timeMillis=*/ 20)
.addControlPoint(/*amplitude=*/ 0.0f, /*frequencyHz=*/ 120f, /*timeMillis=*/ 30)
.build();
+
HalVibration vibration = startThreadAndDispatcher(effect);
waitForCompletion();
@@ -1913,6 +1932,55 @@
fakeVibrator.getEffectSegments(vibration5.id));
}
+ @Test
+ public void vibrate_multipleVibratorsSequentialInSession_runsInOrderWithoutDelaysAndNoOffs() {
+ mockVibrators(1, 2, 3);
+ mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
+ mVibratorProviders.get(2).setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
+ mVibratorProviders.get(2).setSupportedPrimitives(
+ VibrationEffect.Composition.PRIMITIVE_CLICK);
+ mVibratorProviders.get(3).setSupportedEffects(VibrationEffect.EFFECT_CLICK);
+
+ CombinedVibration effect = CombinedVibration.startSequential()
+ .addNext(3,
+ VibrationEffect.get(VibrationEffect.EFFECT_CLICK),
+ /* delay= */ TEST_TIMEOUT_MILLIS)
+ .addNext(1,
+ VibrationEffect.createWaveform(
+ new long[] {TEST_TIMEOUT_MILLIS, TEST_TIMEOUT_MILLIS},
+ /* repeat= */ -1),
+ /* delay= */ TEST_TIMEOUT_MILLIS)
+ .addNext(2,
+ VibrationEffect.startComposition()
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1,
+ /* delay= */ TEST_TIMEOUT_MILLIS)
+ .compose(),
+ /* delay= */ TEST_TIMEOUT_MILLIS)
+ .combine();
+ HalVibration vibration = startThreadAndDispatcher(effect, /* isInSession= */ true);
+
+ // Should not timeout as delays will not affect in session playback time.
+ waitForCompletion();
+
+ // Vibrating state remains ON until session resets it.
+ verifyCallbacksTriggered(vibration, Status.FINISHED);
+ assertTrue(mControllers.get(1).isVibrating());
+ assertTrue(mControllers.get(2).isVibrating());
+ assertTrue(mControllers.get(3).isVibrating());
+
+ assertEquals(0, mVibratorProviders.get(1).getOffCount());
+ assertEquals(0, mVibratorProviders.get(2).getOffCount());
+ assertEquals(0, mVibratorProviders.get(3).getOffCount());
+ assertEquals(Arrays.asList(expectedOneShot(TEST_TIMEOUT_MILLIS)),
+ mVibratorProviders.get(1).getEffectSegments(vibration.id));
+ assertEquals(expectedAmplitudes(255), mVibratorProviders.get(1).getAmplitudes());
+ assertEquals(Arrays.asList(expectedPrimitive(
+ VibrationEffect.Composition.PRIMITIVE_CLICK, 1, TEST_TIMEOUT_MILLIS)),
+ mVibratorProviders.get(2).getEffectSegments(vibration.id));
+ assertEquals(Arrays.asList(expectedPrebaked(VibrationEffect.EFFECT_CLICK)),
+ mVibratorProviders.get(3).getEffectSegments(vibration.id));
+ }
+
private void mockVibrators(int... vibratorIds) {
for (int vibratorId : vibratorIds) {
mVibratorProviders.put(vibratorId,
@@ -1935,8 +2003,14 @@
return startThreadAndDispatcher(createVibration(effect));
}
+ private HalVibration startThreadAndDispatcher(CombinedVibration effect, boolean isInSession) {
+ return startThreadAndDispatcher(createVibration(effect), isInSession,
+ /* requestVibrationParamsFuture= */ null);
+ }
+
private HalVibration startThreadAndDispatcher(HalVibration vib) {
- return startThreadAndDispatcher(vib, /* requestVibrationParamsFuture= */ null);
+ return startThreadAndDispatcher(vib, /* isInSession= */ false,
+ /* requestVibrationParamsFuture= */ null);
}
private HalVibration startThreadAndDispatcher(VibrationEffect effect,
@@ -1947,15 +2021,17 @@
HalVibration vib = new HalVibration(
new CallerInfo(attrs, UID, DEVICE_ID, PACKAGE_NAME, "reason"),
CombinedVibration.createParallel(effect));
- return startThreadAndDispatcher(vib, requestVibrationParamsFuture);
+ return startThreadAndDispatcher(vib, /* isInSession= */ false,
+ requestVibrationParamsFuture);
}
- private HalVibration startThreadAndDispatcher(HalVibration vib,
+ private HalVibration startThreadAndDispatcher(HalVibration vib, boolean isInSession,
CompletableFuture<Void> requestVibrationParamsFuture) {
mControllers = createVibratorControllers();
DeviceAdapter deviceAdapter = new DeviceAdapter(mVibrationSettings, mControllers);
- mVibrationConductor = new VibrationStepConductor(vib, mVibrationSettings, deviceAdapter,
- mVibrationScaler, mStatsLoggerMock, requestVibrationParamsFuture, mManagerHooks);
+ mVibrationConductor = new VibrationStepConductor(vib, isInSession, mVibrationSettings,
+ deviceAdapter, mVibrationScaler, mStatsLoggerMock, requestVibrationParamsFuture,
+ mManagerHooks);
assertTrue(mThread.runVibrationOnVibrationThread(mVibrationConductor));
return mVibrationConductor.getVibration();
}
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java
index 5f76d68..ec83e99 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java
@@ -2865,7 +2865,7 @@
mTestLooper.dispatchAll();
assertThat(session.getStatus()).isEqualTo(Status.IGNORED_UNSUPPORTED);
- verify(mNativeWrapperMock).startSession(eq(session.getSessionId()), eq(new int[] {1, 3}));
+ verify(mNativeWrapperMock, never()).startSession(anyLong(), any(int[].class));
verify(callback, never()).onStarted(any(IVibrationSession.class));
verify(callback, never()).onFinishing();
verify(callback)
@@ -2889,6 +2889,7 @@
verify(callback).onStarted(captor.capture());
captor.getValue().finishSession();
+ mTestLooper.dispatchAll();
// Session not ended until HAL callback.
assertThat(session.getStatus()).isEqualTo(Status.RUNNING);
@@ -3139,6 +3140,224 @@
}
@Test
+ @EnableFlags(android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS)
+ public void vibrateInSession_afterCancel_vibrationIgnored() throws Exception {
+ mockCapabilities(IVibratorManager.CAP_START_SESSIONS);
+ mockVibrators(1, 2);
+ FakeVibratorControllerProvider fakeVibrator1 = mVibratorProviders.get(1);
+ fakeVibrator1.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
+ VibratorManagerService service = createSystemReadyService();
+ int sessionFinishDelayMs = 200;
+ IVibrationSessionCallback callback = mockSessionCallbacks(sessionFinishDelayMs);
+
+ VendorVibrationSession session = startSession(service, RINGTONE_ATTRS, callback, 1, 2);
+ mTestLooper.dispatchAll();
+
+ verify(mNativeWrapperMock).startSession(eq(session.getSessionId()), eq(new int[] {1, 2}));
+ ArgumentCaptor<IVibrationSession> captor = ArgumentCaptor.forClass(IVibrationSession.class);
+ verify(callback).onStarted(captor.capture());
+
+ IVibrationSession startedSession = captor.getValue();
+ startedSession.cancelSession();
+ startedSession.vibrate(
+ CombinedVibration.createParallel(VibrationEffect.createOneShot(10, 255)),
+ "reason");
+
+ // VibrationThread will never start this vibration.
+ assertFalse(waitUntil(s -> !fakeVibrator1.getAmplitudes().isEmpty(), service,
+ TEST_TIMEOUT_MILLIS));
+
+ // Dispatch HAL callbacks.
+ mTestLooper.moveTimeForward(sessionFinishDelayMs);
+ mTestLooper.dispatchAll();
+
+ assertThat(session.getStatus()).isEqualTo(Status.CANCELLED_BY_USER);
+ verify(callback).onFinishing();
+ verify(callback).onFinished(eq(android.os.vibrator.VendorVibrationSession.STATUS_CANCELED));
+ }
+
+ @Test
+ @EnableFlags(android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS)
+ public void vibrateInSession_afterFinish_vibrationIgnored() throws Exception {
+ mockCapabilities(IVibratorManager.CAP_START_SESSIONS);
+ mockVibrators(1, 2);
+ FakeVibratorControllerProvider fakeVibrator1 = mVibratorProviders.get(1);
+ fakeVibrator1.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
+ VibratorManagerService service = createSystemReadyService();
+ int sessionFinishDelayMs = 200;
+ IVibrationSessionCallback callback = mockSessionCallbacks(sessionFinishDelayMs);
+
+ VendorVibrationSession session = startSession(service, RINGTONE_ATTRS, callback, 1, 2);
+ mTestLooper.dispatchAll();
+
+ verify(mNativeWrapperMock).startSession(eq(session.getSessionId()), eq(new int[] {1, 2}));
+ ArgumentCaptor<IVibrationSession> captor = ArgumentCaptor.forClass(IVibrationSession.class);
+ verify(callback).onStarted(captor.capture());
+
+ IVibrationSession startedSession = captor.getValue();
+ startedSession.finishSession();
+ mTestLooper.dispatchAll();
+
+ startedSession.vibrate(
+ CombinedVibration.createParallel(VibrationEffect.createOneShot(10, 255)),
+ "reason");
+
+ // Session not ended until HAL callback.
+ assertThat(session.getStatus()).isEqualTo(Status.RUNNING);
+
+ // VibrationThread will never start this vibration.
+ assertFalse(waitUntil(s -> !fakeVibrator1.getAmplitudes().isEmpty(), service,
+ TEST_TIMEOUT_MILLIS));
+
+ // Dispatch HAL callbacks.
+ mTestLooper.moveTimeForward(sessionFinishDelayMs);
+ mTestLooper.dispatchAll();
+
+ assertThat(session.getStatus()).isEqualTo(Status.FINISHED);
+ verify(callback).onFinishing();
+ verify(callback).onFinished(eq(android.os.vibrator.VendorVibrationSession.STATUS_SUCCESS));
+ }
+
+ @Test
+ @EnableFlags(android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS)
+ public void vibrateInSession_repeatingVibration_vibrationIgnored() throws Exception {
+ mockCapabilities(IVibratorManager.CAP_START_SESSIONS);
+ mockVibrators(1, 2);
+ FakeVibratorControllerProvider fakeVibrator1 = mVibratorProviders.get(1);
+ fakeVibrator1.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
+ VibratorManagerService service = createSystemReadyService();
+ int sessionFinishDelayMs = 200;
+ IVibrationSessionCallback callback = mockSessionCallbacks(sessionFinishDelayMs);
+
+ VendorVibrationSession session = startSession(service, RINGTONE_ATTRS, callback, 1, 2);
+ mTestLooper.dispatchAll();
+
+ verify(mNativeWrapperMock).startSession(eq(session.getSessionId()), eq(new int[] {1, 2}));
+ ArgumentCaptor<IVibrationSession> captor = ArgumentCaptor.forClass(IVibrationSession.class);
+ verify(callback).onStarted(captor.capture());
+
+ IVibrationSession startedSession = captor.getValue();
+ startedSession.vibrate(
+ CombinedVibration.createParallel(
+ VibrationEffect.createWaveform(new long[]{ 10, 10, 10, 10}, 0)),
+ "reason");
+
+ // VibrationThread will never start this vibration.
+ assertFalse(waitUntil(s -> !fakeVibrator1.getAmplitudes().isEmpty(), service,
+ TEST_TIMEOUT_MILLIS));
+
+ startedSession.finishSession();
+ mTestLooper.dispatchAll();
+
+ // Dispatch HAL callbacks.
+ mTestLooper.moveTimeForward(sessionFinishDelayMs);
+ mTestLooper.dispatchAll();
+
+ assertThat(session.getStatus()).isEqualTo(Status.FINISHED);
+ assertThat(service.isVibrating(1)).isFalse();
+ assertThat(service.isVibrating(2)).isFalse();
+ verify(callback).onFinishing();
+ verify(callback).onFinished(eq(android.os.vibrator.VendorVibrationSession.STATUS_SUCCESS));
+ }
+
+ @Test
+ @EnableFlags(android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS)
+ public void vibrateInSession_singleVibration_playsAllVibrateCommands() throws Exception {
+ mockCapabilities(IVibratorManager.CAP_START_SESSIONS);
+ mockVibrators(1, 2);
+ FakeVibratorControllerProvider fakeVibrator1 = mVibratorProviders.get(1);
+ fakeVibrator1.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
+ FakeVibratorControllerProvider fakeVibrator2 = mVibratorProviders.get(1);
+ fakeVibrator2.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
+ VibratorManagerService service = createSystemReadyService();
+ int sessionFinishDelayMs = 200;
+ IVibrationSessionCallback callback = mockSessionCallbacks(sessionFinishDelayMs);
+
+ VendorVibrationSession session = startSession(service, RINGTONE_ATTRS, callback, 1, 2);
+ mTestLooper.dispatchAll();
+
+ verify(mNativeWrapperMock).startSession(eq(session.getSessionId()), eq(new int[] {1, 2}));
+ ArgumentCaptor<IVibrationSession> captor = ArgumentCaptor.forClass(IVibrationSession.class);
+ verify(callback).onStarted(captor.capture());
+
+ IVibrationSession startedSession = captor.getValue();
+ startedSession.vibrate(
+ CombinedVibration.createParallel(
+ VibrationEffect.createWaveform(new long[]{ 10, 10, 10, 10}, -1)),
+ "reason");
+
+ // VibrationThread will start this vibration async, so wait until vibration is triggered.
+ // Vibrators will receive 2 requests for the waveform playback
+ assertTrue(waitUntil(s -> fakeVibrator1.getAmplitudes().size() == 2, service,
+ TEST_TIMEOUT_MILLIS));
+ assertTrue(waitUntil(s -> fakeVibrator2.getAmplitudes().size() == 2, service,
+ TEST_TIMEOUT_MILLIS));
+
+ startedSession.finishSession();
+ mTestLooper.dispatchAll();
+
+ // Dispatch HAL callbacks.
+ mTestLooper.moveTimeForward(sessionFinishDelayMs);
+ mTestLooper.dispatchAll();
+
+ assertThat(session.getStatus()).isEqualTo(Status.FINISHED);
+ assertThat(service.isVibrating(1)).isFalse();
+ assertThat(service.isVibrating(2)).isFalse();
+ verify(callback).onFinishing();
+ verify(callback).onFinished(eq(android.os.vibrator.VendorVibrationSession.STATUS_SUCCESS));
+ }
+
+ @Test
+ @EnableFlags(android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS)
+ public void vibrateInSession_multipleVibrations_playsAllVibrations() throws Exception {
+ mockCapabilities(IVibratorManager.CAP_START_SESSIONS);
+ mockVibrators(1, 2);
+ FakeVibratorControllerProvider fakeVibrator1 = mVibratorProviders.get(1);
+ fakeVibrator1.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
+ VibratorManagerService service = createSystemReadyService();
+ int sessionFinishDelayMs = 200;
+ IVibrationSessionCallback callback = mockSessionCallbacks(sessionFinishDelayMs);
+
+ VendorVibrationSession session = startSession(service, RINGTONE_ATTRS, callback, 1, 2);
+ mTestLooper.dispatchAll();
+
+ verify(mNativeWrapperMock).startSession(eq(session.getSessionId()), eq(new int[] {1, 2}));
+ ArgumentCaptor<IVibrationSession> captor = ArgumentCaptor.forClass(IVibrationSession.class);
+ verify(callback).onStarted(captor.capture());
+
+ IVibrationSession startedSession = captor.getValue();
+ startedSession.vibrate(
+ CombinedVibration.createParallel(VibrationEffect.createOneShot(10, 255)),
+ "reason");
+
+ // VibrationThread will start this vibration async, so wait until vibration is completed.
+ assertTrue(waitUntil(s -> fakeVibrator1.getAmplitudes().size() == 1, service,
+ TEST_TIMEOUT_MILLIS));
+ assertTrue(waitUntil(s -> !session.getVibrations().isEmpty(), service,
+ TEST_TIMEOUT_MILLIS));
+
+ startedSession.vibrate(
+ CombinedVibration.createParallel(VibrationEffect.createOneShot(20, 255)),
+ "reason");
+
+ assertTrue(waitUntil(s -> fakeVibrator1.getAmplitudes().size() == 2, service,
+ TEST_TIMEOUT_MILLIS));
+
+ startedSession.finishSession();
+ mTestLooper.dispatchAll();
+
+ // Dispatch HAL callbacks.
+ mTestLooper.moveTimeForward(sessionFinishDelayMs);
+ mTestLooper.dispatchAll();
+
+ assertThat(session.getStatus()).isEqualTo(Status.FINISHED);
+ assertThat(service.isVibrating(1)).isFalse();
+ assertThat(service.isVibrating(2)).isFalse();
+ verify(callback).onFinishing();
+ verify(callback).onFinished(eq(android.os.vibrator.VendorVibrationSession.STATUS_SUCCESS));
+ }
+
+ @Test
public void frameworkStats_externalVibration_reportsAllMetrics() throws Exception {
mockVibrators(1);
mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_EXTERNAL_CONTROL);
diff --git a/services/tests/wmtests/AndroidManifest.xml b/services/tests/wmtests/AndroidManifest.xml
index 6e6b70d..5f2f3ed 100644
--- a/services/tests/wmtests/AndroidManifest.xml
+++ b/services/tests/wmtests/AndroidManifest.xml
@@ -17,10 +17,10 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.frameworks.wmtests">
- <!-- Uses API introduced in P (28) -->
+ <!-- Uses API introduced in S (31). Using SDK 31+ avoids Google Play Protect popups. -->
<uses-sdk
android:minSdkVersion="1"
- android:targetSdkVersion="28" />
+ android:targetSdkVersion="31" />
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
diff --git a/services/tests/wmtests/res/xml/bookmarks.xml b/services/tests/wmtests/res/xml/bookmarks.xml
index 197b366..787f4e8 100644
--- a/services/tests/wmtests/res/xml/bookmarks.xml
+++ b/services/tests/wmtests/res/xml/bookmarks.xml
@@ -13,60 +13,70 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<bookmarks>
+<bookmarks xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
<!-- the key combinations for the following shortcuts must be in sync
with the key combinations sent by the test in ModifierShortcutTests.java -->
<bookmark
role="android.app.role.BROWSER"
- shortcut="b" />
+ androidprv:keycode="KEYCODE_B"
+ androidprv:modifierState="META" />
<bookmark
category="android.intent.category.APP_CONTACTS"
- shortcut="c" />
+ androidprv:keycode="KEYCODE_C"
+ androidprv:modifierState="META" />
<bookmark
category="android.intent.category.APP_EMAIL"
- shortcut="e" />
+ androidprv:keycode="KEYCODE_E"
+ androidprv:modifierState="META" />
<bookmark
category="android.intent.category.APP_CALENDAR"
- shortcut="k" />
+ androidprv:keycode="KEYCODE_K"
+ androidprv:modifierState="META" />
<bookmark
category="android.intent.category.APP_MAPS"
- shortcut="m" />
+ androidprv:keycode="KEYCODE_M"
+ androidprv:modifierState="META" />
<bookmark
category="android.intent.category.APP_MUSIC"
- shortcut="p" />
+ androidprv:keycode="KEYCODE_P"
+ androidprv:modifierState="META" />
<bookmark
role="android.app.role.SMS"
- shortcut="s" />
+ androidprv:keycode="KEYCODE_S"
+ androidprv:modifierState="META" />
<bookmark
category="android.intent.category.APP_CALCULATOR"
- shortcut="u" />
+ androidprv:keycode="KEYCODE_U"
+ androidprv:modifierState="META" />
<bookmark
role="android.app.role.BROWSER"
- shortcut="b"
- shift="true" />
+ androidprv:keycode="KEYCODE_B"
+ androidprv:modifierState="META|SHIFT" />
<bookmark
category="android.intent.category.APP_CONTACTS"
- shortcut="c"
- shift="true" />
+ androidprv:keycode="KEYCODE_C"
+ androidprv:modifierState="META|SHIFT" />
<bookmark
package="com.test"
class="com.test.BookmarkTest"
- shortcut="j"
- shift="true" />
+ androidprv:keycode="KEYCODE_J"
+ androidprv:modifierState="META|SHIFT" />
<!-- The following shortcuts will not be invoked by tests but are here to
provide test coverage of parsing the different types of shortcut. -->
<bookmark
package="com.test"
class="com.test.BookmarkTest"
- shortcut="j" />
+ androidprv:keycode="KEYCODE_J"
+ androidprv:modifierState="META" />
<bookmark
package="com.test2"
class="com.test.BookmarkTest"
- shortcut="d" />
+ androidprv:keycode="KEYCODE_D"
+ androidprv:modifierState="META" />
<!-- It's intended that this package/class will NOT resolve so we test the resolution
@@ -74,6 +84,7 @@
<bookmark
package="com.test3"
class="com.test.BookmarkTest"
- shortcut="f" />
+ androidprv:keycode="KEYCODE_F"
+ androidprv:modifierState="META" />
</bookmarks>
diff --git a/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutManagerTests.java b/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutManagerTests.java
index 0575d98..82a5add 100644
--- a/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutManagerTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutManagerTests.java
@@ -116,6 +116,7 @@
mModifierShortcutManager = new ModifierShortcutManager(
mContext, mHandler, UserHandle.SYSTEM);
+ mModifierShortcutManager.onSystemReady();
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
index a51ce995..bc03c23 100644
--- a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
+++ b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
@@ -88,6 +88,7 @@
import android.telecom.TelecomManager;
import android.view.Display;
import android.view.InputEvent;
+import android.view.KeyCharacterMap;
import android.view.KeyEvent;
import android.view.accessibility.AccessibilityManager;
import android.view.autofill.AutofillManagerInternal;
@@ -270,11 +271,15 @@
// Return mocked services: LocalServices.getService
mMockitoSession = mockitoSession()
.mockStatic(LocalServices.class, spyStubOnly)
+ .mockStatic(KeyCharacterMap.class)
.strictness(Strictness.LENIENT)
.startMocking();
mPhoneWindowManager = spy(new PhoneWindowManager());
+ KeyCharacterMap virtualKcm = mContext.getSystemService(InputManager.class)
+ .getInputDevice(KeyCharacterMap.VIRTUAL_KEYBOARD).getKeyCharacterMap();
+ doReturn(virtualKcm).when(() -> KeyCharacterMap.load(anyInt()));
doReturn(mWindowManagerInternal).when(
() -> LocalServices.getService(eq(WindowManagerInternal.class)));
doReturn(mActivityManagerInternal).when(
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java
index 7f260f8..70f57eb 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java
@@ -357,6 +357,25 @@
assertEquals(activity1.app, mAtm.mTopApp);
}
+ @Test
+ public void testTopResumedActivity_deferResume() {
+ final ActivityRecord activity1 = new ActivityBuilder(mAtm).setCreateTask(true).build();
+ final ActivityRecord activity2 = new ActivityBuilder(mAtm).setCreateTask(true).build();
+ activity2.setState(ActivityRecord.State.RESUMED, "test");
+ assertEquals(activity2.app, mAtm.mTopApp);
+ reset(activity2);
+
+ // Verify that no top-resumed activity changes to the client while defer-resume enabled.
+ mSupervisor.beginDeferResume();
+ activity1.getTask().moveToFront("test");
+ activity1.setState(ActivityRecord.State.RESUMED, "test");
+ verify(activity2, never()).scheduleTopResumedActivityChanged(eq(false));
+
+ // Verify that the change is scheduled to the client after defer-resumed disabled
+ mSupervisor.endDeferResume();
+ verify(activity2).scheduleTopResumedActivityChanged(eq(false));
+ }
+
/**
* We need to launch home again after user unlocked for those displays that do not have
* encryption aware home app.
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatCameraOverridesTest.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatCameraOverridesTest.java
index b91a5b7..d5ed048 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppCompatCameraOverridesTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatCameraOverridesTest.java
@@ -17,8 +17,8 @@
package com.android.server.wm;
import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_FORCE_ROTATION;
-import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_FREEFORM_WINDOWING_TREATMENT;
import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_REFRESH;
+import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT;
import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE;
import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_ONLY_FOR_CAMERA;
import static android.content.pm.ActivityInfo.OVERRIDE_ORIENTATION_ONLY_FOR_CAMERA;
@@ -228,9 +228,8 @@
}
@Test
- @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_DISABLE_FREEFORM_WINDOWING_TREATMENT})
@EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
- public void testShouldApplyCameraCompatFreeformTreatment_overrideEnabled_returnsFalse() {
+ public void testShouldApplyCameraCompatFreeformTreatment_notEnabledByOverride_returnsFalse() {
runTestScenario((robot) -> {
robot.activity().createActivityWithComponentInNewTask();
@@ -239,19 +238,9 @@
}
@Test
- @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_DISABLE_FREEFORM_WINDOWING_TREATMENT})
+ @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT})
@EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
- public void testShouldApplyCameraCompatFreeformTreatment_disabledByOverride_returnsFalse() {
- runTestScenario((robot) -> {
- robot.activity().createActivityWithComponentInNewTask();
-
- robot.checkShouldApplyFreeformTreatmentForCameraCompat(false);
- });
- }
-
- @Test
- @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
- public void testShouldApplyCameraCompatFreeformTreatment_notDisabledByOverride_returnsTrue() {
+ public void testShouldApplyCameraCompatFreeformTreatment_overrideAndFlagEnabled_returnsTrue() {
runTestScenario((robot) -> {
robot.activity().createActivityWithComponentInNewTask();
@@ -261,8 +250,9 @@
@Test
@EnableCompatChanges({OVERRIDE_ORIENTATION_ONLY_FOR_CAMERA,
- OVERRIDE_MIN_ASPECT_RATIO_ONLY_FOR_CAMERA})
- public void testShouldRecomputeConfigurationForCameraCompat() {
+ OVERRIDE_MIN_ASPECT_RATIO_ONLY_FOR_CAMERA,
+ OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT})
+ public void testShouldRecomputeConfigurationForFreeformTreatment() {
runTestScenario((robot) -> {
robot.conf().enableCameraCompatSplitScreenAspectRatio(true);
robot.applyOnActivity((a) -> {
diff --git a/services/tests/wmtests/src/com/android/server/wm/CameraCompatFreeformPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/CameraCompatFreeformPolicyTests.java
index e447565..c427583 100644
--- a/services/tests/wmtests/src/com/android/server/wm/CameraCompatFreeformPolicyTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/CameraCompatFreeformPolicyTests.java
@@ -25,7 +25,7 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.servertransaction.ActivityLifecycleItem.ON_PAUSE;
import static android.app.servertransaction.ActivityLifecycleItem.ON_STOP;
-import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_FREEFORM_WINDOWING_TREATMENT;
+import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_FULL_USER;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
@@ -96,61 +96,55 @@
private static final String TEST_PACKAGE_1 = "com.android.frameworks.wmtests";
private static final String TEST_PACKAGE_2 = "com.test.package.two";
private static final String CAMERA_ID_1 = "camera-1";
- private static final String CAMERA_ID_2 = "camera-2";
- private CameraManager mMockCameraManager;
- private Handler mMockHandler;
private AppCompatConfiguration mAppCompatConfiguration;
private CameraManager.AvailabilityCallback mCameraAvailabilityCallback;
private CameraCompatFreeformPolicy mCameraCompatFreeformPolicy;
private ActivityRecord mActivity;
- private Task mTask;
private ActivityRefresher mActivityRefresher;
@Before
public void setUp() throws Exception {
mAppCompatConfiguration = mDisplayContent.mWmService.mAppCompatConfiguration;
spyOn(mAppCompatConfiguration);
- when(mAppCompatConfiguration.isCameraCompatTreatmentEnabled())
- .thenReturn(true);
- when(mAppCompatConfiguration.isCameraCompatRefreshEnabled())
- .thenReturn(true);
+ when(mAppCompatConfiguration.isCameraCompatTreatmentEnabled()).thenReturn(true);
+ when(mAppCompatConfiguration.isCameraCompatRefreshEnabled()).thenReturn(true);
when(mAppCompatConfiguration.isCameraCompatRefreshCycleThroughStopEnabled())
.thenReturn(true);
- mMockCameraManager = mock(CameraManager.class);
+ final CameraManager mockCameraManager = mock(CameraManager.class);
doAnswer(invocation -> {
mCameraAvailabilityCallback = invocation.getArgument(1);
return null;
- }).when(mMockCameraManager).registerAvailabilityCallback(
+ }).when(mockCameraManager).registerAvailabilityCallback(
any(Executor.class), any(CameraManager.AvailabilityCallback.class));
- when(mContext.getSystemService(CameraManager.class)).thenReturn(mMockCameraManager);
+ when(mContext.getSystemService(CameraManager.class)).thenReturn(mockCameraManager);
mDisplayContent.setIgnoreOrientationRequest(true);
- mMockHandler = mock(Handler.class);
+ final Handler mockHandler = mock(Handler.class);
- when(mMockHandler.postDelayed(any(Runnable.class), anyLong())).thenAnswer(
+ when(mockHandler.postDelayed(any(Runnable.class), anyLong())).thenAnswer(
invocation -> {
((Runnable) invocation.getArgument(0)).run();
return null;
});
- mActivityRefresher = new ActivityRefresher(mDisplayContent.mWmService, mMockHandler);
- CameraStateMonitor cameraStateMonitor =
- new CameraStateMonitor(mDisplayContent, mMockHandler);
- mCameraCompatFreeformPolicy =
- new CameraCompatFreeformPolicy(mDisplayContent, cameraStateMonitor,
- mActivityRefresher);
+ mActivityRefresher = new ActivityRefresher(mDisplayContent.mWmService, mockHandler);
+ final CameraStateMonitor cameraStateMonitor = new CameraStateMonitor(mDisplayContent,
+ mockHandler);
+ mCameraCompatFreeformPolicy = new CameraCompatFreeformPolicy(mDisplayContent,
+ cameraStateMonitor, mActivityRefresher);
setDisplayRotation(Surface.ROTATION_90);
mCameraCompatFreeformPolicy.start();
cameraStateMonitor.startListeningToCameraState();
}
- @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
@Test
+ @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
+ @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT})
public void testFullscreen_doesNotActivateCameraCompatMode() {
configureActivity(SCREEN_ORIENTATION_PORTRAIT, WINDOWING_MODE_FULLSCREEN);
doReturn(false).when(mActivity).inFreeformWindowingMode();
@@ -160,23 +154,26 @@
assertNotInCameraCompatMode();
}
- @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
@Test
+ @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
+ @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT})
public void testOrientationUnspecified_doesNotActivateCameraCompatMode() {
configureActivity(SCREEN_ORIENTATION_UNSPECIFIED);
assertNotInCameraCompatMode();
}
- @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
@Test
+ @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
+ @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT})
public void testNoCameraConnection_doesNotActivateCameraCompatMode() {
configureActivity(SCREEN_ORIENTATION_PORTRAIT);
assertNotInCameraCompatMode();
}
- @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
@Test
+ @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
+ @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT})
public void testCameraConnected_deviceInPortrait_portraitCameraCompatMode() throws Exception {
configureActivity(SCREEN_ORIENTATION_PORTRAIT);
setDisplayRotation(Surface.ROTATION_0);
@@ -187,6 +184,8 @@
}
@Test
+ @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
+ @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT})
public void testCameraConnected_deviceInLandscape_portraitCameraCompatMode() throws Exception {
configureActivity(SCREEN_ORIENTATION_PORTRAIT);
setDisplayRotation(Surface.ROTATION_270);
@@ -197,6 +196,8 @@
}
@Test
+ @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
+ @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT})
public void testCameraConnected_deviceInPortrait_landscapeCameraCompatMode() throws Exception {
configureActivity(SCREEN_ORIENTATION_LANDSCAPE);
setDisplayRotation(Surface.ROTATION_0);
@@ -207,6 +208,8 @@
}
@Test
+ @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
+ @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT})
public void testCameraConnected_deviceInLandscape_landscapeCameraCompatMode() throws Exception {
configureActivity(SCREEN_ORIENTATION_LANDSCAPE);
setDisplayRotation(Surface.ROTATION_270);
@@ -216,8 +219,9 @@
assertActivityRefreshRequested(/* refreshRequested */ false);
}
- @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
@Test
+ @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
+ @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT})
public void testCameraReconnected_cameraCompatModeAndRefresh() throws Exception {
configureActivity(SCREEN_ORIENTATION_PORTRAIT);
setDisplayRotation(Surface.ROTATION_270);
@@ -236,6 +240,8 @@
}
@Test
+ @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
+ @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT})
public void testCameraOpenedForDifferentPackage_notInCameraCompatMode() {
configureActivity(SCREEN_ORIENTATION_PORTRAIT);
@@ -246,27 +252,32 @@
@Test
@EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
- @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_DISABLE_FREEFORM_WINDOWING_TREATMENT})
- public void testShouldApplyCameraCompatFreeformTreatment_overrideEnabled_returnsFalse() {
+ public void testShouldApplyCameraCompatFreeformTreatment_overrideNotEnabled_returnsFalse() {
configureActivity(SCREEN_ORIENTATION_PORTRAIT);
+ mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
+
+ assertFalse(mCameraCompatFreeformPolicy.isTreatmentEnabledForActivity(mActivity,
+ /* checkOrientation */ true));
+ }
+
+ @Test
+ @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
+ @EnableCompatChanges(OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT)
+ public void testShouldApplyCameraCompatFreeformTreatment_enabledByOverride_returnsTrue() {
+ configureActivity(SCREEN_ORIENTATION_PORTRAIT);
+
+ mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
+
assertTrue(mActivity.info
- .isChangeEnabled(OVERRIDE_CAMERA_COMPAT_DISABLE_FREEFORM_WINDOWING_TREATMENT));
- assertFalse(mCameraCompatFreeformPolicy.isCameraCompatForFreeformEnabledForActivity(
- mActivity));
+ .isChangeEnabled(OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT));
+ assertTrue(mCameraCompatFreeformPolicy.isTreatmentEnabledForActivity(mActivity,
+ /* checkOrientation */ true));
}
@Test
@EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
- public void testShouldApplyCameraCompatFreeformTreatment_notDisabledByOverride_returnsTrue() {
- configureActivity(SCREEN_ORIENTATION_PORTRAIT);
-
- assertTrue(mCameraCompatFreeformPolicy.isCameraCompatForFreeformEnabledForActivity(
- mActivity));
- }
-
- @Test
- @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
+ @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT})
public void testShouldRefreshActivity_appBoundsChanged_returnsTrue() {
configureActivity(SCREEN_ORIENTATION_PORTRAIT);
Configuration oldConfiguration = createConfiguration(/* letterbox= */ false);
@@ -279,6 +290,7 @@
@Test
@EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
+ @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT})
public void testShouldRefreshActivity_displayRotationChanged_returnsTrue() {
configureActivity(SCREEN_ORIENTATION_PORTRAIT);
Configuration oldConfiguration = createConfiguration(/* letterbox= */ true);
@@ -294,6 +306,7 @@
@Test
@EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
+ @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT})
public void testShouldRefreshActivity_appBoundsNorDisplayChanged_returnsFalse() {
configureActivity(SCREEN_ORIENTATION_PORTRAIT);
Configuration oldConfiguration = createConfiguration(/* letterbox= */ true);
@@ -309,6 +322,7 @@
@Test
@EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
+ @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT})
public void testOnActivityConfigurationChanging_refreshDisabledViaFlag_noRefresh()
throws Exception {
configureActivity(SCREEN_ORIENTATION_PORTRAIT);
@@ -324,6 +338,7 @@
@Test
@EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
+ @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT})
public void testOnActivityConfigurationChanging_cycleThroughStopDisabled() throws Exception {
when(mAppCompatConfiguration.isCameraCompatRefreshCycleThroughStopEnabled())
.thenReturn(false);
@@ -338,6 +353,7 @@
@Test
@EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
+ @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT})
public void testOnActivityConfigurationChanging_cycleThroughStopDisabledForApp()
throws Exception {
configureActivity(SCREEN_ORIENTATION_PORTRAIT);
@@ -352,6 +368,7 @@
@Test
@EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
+ @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT})
public void testGetCameraCompatAspectRatio_activityNotInCameraCompat_returnsDefaultAspRatio() {
configureActivity(SCREEN_ORIENTATION_FULL_USER);
@@ -365,6 +382,7 @@
@Test
@EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
+ @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT})
public void testGetCameraCompatAspectRatio_activityInCameraCompat_returnsConfigAspectRatio() {
configureActivity(SCREEN_ORIENTATION_PORTRAIT);
final float configAspectRatio = 1.5f;
@@ -380,6 +398,7 @@
@Test
@EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
+ @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT})
public void testGetCameraCompatAspectRatio_inCameraCompatPerAppOverride_returnDefAspectRatio() {
configureActivity(SCREEN_ORIENTATION_PORTRAIT);
final float configAspectRatio = 1.5f;
@@ -406,7 +425,7 @@
private void configureActivityAndDisplay(@ScreenOrientation int activityOrientation,
@Orientation int naturalOrientation, @WindowingMode int windowingMode) {
- mTask = new TaskBuilder(mSupervisor)
+ final Task task = new TaskBuilder(mSupervisor)
.setDisplay(mDisplayContent)
.setWindowingMode(windowingMode)
.build();
@@ -416,7 +435,7 @@
.setComponent(ComponentName.createRelative(mContext,
com.android.server.wm.CameraCompatFreeformPolicyTests.class.getName()))
.setScreenOrientation(activityOrientation)
- .setTask(mTask)
+ .setTask(task)
.build();
spyOn(mActivity.mAppCompatController.getAppCompatCameraOverrides());
@@ -429,13 +448,11 @@
}
private void assertInCameraCompatMode(@CameraCompatTaskInfo.FreeformCameraCompatMode int mode) {
- assertEquals(mode, mActivity.mAppCompatController.getAppCompatCameraOverrides()
- .getFreeformCameraCompatMode());
+ assertEquals(mode, mCameraCompatFreeformPolicy.getCameraCompatMode(mActivity));
}
private void assertNotInCameraCompatMode() {
- assertEquals(CAMERA_COMPAT_FREEFORM_NONE, mActivity.mAppCompatController
- .getAppCompatCameraOverrides().getFreeformCameraCompatMode());
+ assertInCameraCompatMode(CAMERA_COMPAT_FREEFORM_NONE);
}
private void assertActivityRefreshRequested(boolean refreshRequested) throws Exception {
@@ -471,7 +488,8 @@
private Configuration createConfiguration(boolean letterbox) {
final Configuration configuration = new Configuration();
- Rect bounds = letterbox ? new Rect(300, 0, 700, 600) : new Rect(0, 0, 1000, 600);
+ Rect bounds = letterbox ? new Rect(/*left*/ 300, /*top*/ 0, /*right*/ 700, /*bottom*/ 600)
+ : new Rect(/*left*/ 0, /*top*/ 0, /*right*/ 1000, /*bottom*/ 600);
configuration.windowConfiguration.setAppBounds(bounds);
return configuration;
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
index df17cd1..7ed8283 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
@@ -496,24 +496,6 @@
}
@Test
- public void testAppendOrganizedChildTaskInfo() {
- final Task root = createTaskBuilder(".CreatedByOrganizerRoot").build();
- root.mCreatedByOrganizer = true;
- // Add organized and non-organized child.
- final Task child1 = createTaskBuilder(".Task1").setParentTask(root).build();
- final Task child2 = createTaskBuilder(".Task2").setParentTask(root).build();
- doReturn(true).when(child1).isOrganized();
- doReturn(false).when(child2).isOrganized();
- mRecentTasks.add(root);
-
- // Make sure only organized child will be appended.
- final List<RecentTaskInfo> infos = getRecentTasks(0 /* flags */);
- final List<RecentTaskInfo> childrenTaskInfos = infos.get(0).childrenTaskInfos;
- assertEquals(childrenTaskInfos.size(), 1);
- assertEquals(childrenTaskInfos.get(0).taskId, child1.mTaskId);
- }
-
- @Test
public void testAddTasksHomeClearUntrackedTasks_expectFinish() {
// There may be multiple tasks with the same base intent by flags (FLAG_ACTIVITY_NEW_TASK |
// FLAG_ACTIVITY_MULTIPLE_TASK). If the previous task is still active, it should be removed
@@ -1420,46 +1402,6 @@
}
@Test
- public void testLastSnapshotData_snapshotSaved() {
- final TaskSnapshot snapshot = createSnapshot(new Point(100, 100), new Point(80, 80));
- final Task task1 = createTaskBuilder(".Task").build();
- task1.onSnapshotChanged(snapshot);
-
- mRecentTasks.add(task1);
- final List<RecentTaskInfo> infos = getRecentTasks(0 /* flags */);
- final RecentTaskInfo.PersistedTaskSnapshotData lastSnapshotData =
- infos.get(0).lastSnapshotData;
- assertTrue(lastSnapshotData.taskSize.equals(100, 100));
- assertTrue(lastSnapshotData.bufferSize.equals(80, 80));
- }
-
- @Test
- public void testLastSnapshotData_noBuffer() {
- final Task task1 = createTaskBuilder(".Task").build();
- final TaskSnapshot snapshot = createSnapshot(new Point(100, 100), null);
- task1.onSnapshotChanged(snapshot);
-
- mRecentTasks.add(task1);
- final List<RecentTaskInfo> infos = getRecentTasks(0 /* flags */);
- final RecentTaskInfo.PersistedTaskSnapshotData lastSnapshotData =
- infos.get(0).lastSnapshotData;
- assertTrue(lastSnapshotData.taskSize.equals(100, 100));
- assertNull(lastSnapshotData.bufferSize);
- }
-
- @Test
- public void testLastSnapshotData_notSet() {
- final Task task1 = createTaskBuilder(".Task").build();
-
- mRecentTasks.add(task1);
- final List<RecentTaskInfo> infos = getRecentTasks(0 /* flags */);
- final RecentTaskInfo.PersistedTaskSnapshotData lastSnapshotData =
- infos.get(0).lastSnapshotData;
- assertNull(lastSnapshotData.taskSize);
- assertNull(lastSnapshotData.bufferSize);
- }
-
- @Test
public void testCreateRecentTaskInfo_detachedTask() {
final Task task = createTaskBuilder(".Task").build();
final ComponentName componentName = getUniqueComponentName();
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 42e31de..817c368 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
@@ -82,6 +82,7 @@
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
+import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.Presubmit;
import android.util.ArrayMap;
@@ -122,6 +123,8 @@
* Build/Install/Run:
* atest WmTests:WindowOrganizerTests
*/
+
+// TODO revert parts of this set to set the flag to test the behavior
@SmallTest
@Presubmit
@RunWith(WindowTestRunner.class)
@@ -1299,6 +1302,7 @@
}
@Test
+ @DisableFlags(com.android.wm.shell.Flags.FLAG_ENABLE_PIP2)
public void testEnterPipParams() {
final StubOrganizer o = new StubOrganizer();
mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(o);
@@ -1314,6 +1318,7 @@
}
@Test
+ @DisableFlags(com.android.wm.shell.Flags.FLAG_ENABLE_PIP2)
public void testChangePipParams() {
class ChangeSavingOrganizer extends StubOrganizer {
RunningTaskInfo mChangedInfo;
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
index b737d35..50e0e18 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -71,6 +71,7 @@
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -84,12 +85,14 @@
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.when;
+import android.content.ContentResolver;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.graphics.Matrix;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.Region;
+import android.os.Build;
import android.os.IBinder;
import android.os.InputConfig;
import android.os.RemoteException;
@@ -97,6 +100,7 @@
import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.Presubmit;
import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.provider.Settings;
import android.util.ArraySet;
import android.util.MergedConfiguration;
import android.view.Gravity;
@@ -1557,6 +1561,57 @@
}
@Test
+ public void testIsSecureLocked_flagSecureSet() {
+ WindowState window = createWindow(null /* parent */, TYPE_APPLICATION, "test-window",
+ 1 /* ownerId */);
+ window.mAttrs.flags |= WindowManager.LayoutParams.FLAG_SECURE;
+
+ assertTrue(window.isSecureLocked());
+ }
+
+ @Test
+ public void testIsSecureLocked_flagSecureNotSet() {
+ WindowState window = createWindow(null /* parent */, TYPE_APPLICATION, "test-window",
+ 1 /* ownerId */);
+
+ assertFalse(window.isSecureLocked());
+ }
+
+ @Test
+ public void testIsSecureLocked_disableSecureWindows() {
+ assumeTrue(Build.IS_DEBUGGABLE);
+
+ WindowState window = createWindow(null /* parent */, TYPE_APPLICATION, "test-window",
+ 1 /* ownerId */);
+ window.mAttrs.flags |= WindowManager.LayoutParams.FLAG_SECURE;
+ ContentResolver cr = useFakeSettingsProvider();
+
+ // isSecureLocked should return false when DISABLE_SECURE_WINDOWS is set to 1
+ Settings.Secure.putString(cr, Settings.Secure.DISABLE_SECURE_WINDOWS, "1");
+ mWm.mSettingsObserver.onChange(false /* selfChange */,
+ Settings.Secure.getUriFor(Settings.Secure.DISABLE_SECURE_WINDOWS));
+ assertFalse(window.isSecureLocked());
+
+ // isSecureLocked should return true if DISABLE_SECURE_WINDOWS is set to 0.
+ Settings.Secure.putString(cr, Settings.Secure.DISABLE_SECURE_WINDOWS, "0");
+ mWm.mSettingsObserver.onChange(false /* selfChange */,
+ Settings.Secure.getUriFor(Settings.Secure.DISABLE_SECURE_WINDOWS));
+ assertTrue(window.isSecureLocked());
+
+ // Disable secure windows again.
+ Settings.Secure.putString(cr, Settings.Secure.DISABLE_SECURE_WINDOWS, "1");
+ mWm.mSettingsObserver.onChange(false /* selfChange */,
+ Settings.Secure.getUriFor(Settings.Secure.DISABLE_SECURE_WINDOWS));
+ assertFalse(window.isSecureLocked());
+
+ // isSecureLocked should return true if DISABLE_SECURE_WINDOWS is deleted.
+ Settings.Secure.putString(cr, Settings.Secure.DISABLE_SECURE_WINDOWS, null);
+ mWm.mSettingsObserver.onChange(false /* selfChange */,
+ Settings.Secure.getUriFor(Settings.Secure.DISABLE_SECURE_WINDOWS));
+ assertTrue(window.isSecureLocked());
+ }
+
+ @Test
@RequiresFlagsEnabled(FLAG_SENSITIVE_NOTIFICATION_APP_PROTECTION)
public void testIsSecureLocked_sensitiveContentProtectionManagerEnabled() {
String testPackage = "test";
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index 1294945..15c8b13 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -185,6 +185,7 @@
private static final int MSG_INCREASE_SENDSTRING_COUNT = 21;
private static final int MSG_UPDATE_USB_SPEED = 22;
private static final int MSG_UPDATE_HAL_VERSION = 23;
+ private static final int MSG_USER_UNLOCKED_AFTER_BOOT = 24;
// Delay for debouncing USB disconnects.
// We often get rapid connect/disconnect events when enabling USB functions,
@@ -414,6 +415,17 @@
}
};
+ if (Flags.checkUserActionUnlocked()) {
+ BroadcastReceiver userUnlockedAfterBootReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ mHandler.sendEmptyMessage(MSG_USER_UNLOCKED_AFTER_BOOT);
+ }
+ };
+ mContext.registerReceiver(userUnlockedAfterBootReceiver,
+ new IntentFilter(Intent.ACTION_USER_UNLOCKED));
+ }
+
mContext.registerReceiver(portReceiver,
new IntentFilter(UsbManager.ACTION_USB_PORT_CHANGED));
mContext.registerReceiver(chargingReceiver,
@@ -474,6 +486,7 @@
mHandler.sendEmptyMessage(MSG_SYSTEM_READY);
}
+ // Same as ACTION_LOCKED_BOOT_COMPLETED.
public void bootCompleted() {
if (DEBUG) Slog.d(TAG, "boot completed");
mHandler.sendEmptyMessage(MSG_BOOT_COMPLETED);
@@ -632,7 +645,7 @@
protected int mUsbSpeed;
protected int mCurrentGadgetHalVersion;
protected boolean mPendingBootAccessoryHandshakeBroadcast;
-
+ protected boolean mUserUnlockedAfterBoot;
/**
* The persistent property which stores whether adb is enabled or not.
* May also contain vendor-specific default functions for testing purposes.
@@ -837,6 +850,12 @@
return !userManager.hasUserRestriction(UserManager.DISALLOW_USB_FILE_TRANSFER);
}
+ private void attachAccessory() {
+ mUsbDeviceManager.getCurrentSettings().accessoryAttached(mCurrentAccessory);
+ removeMessages(MSG_ACCESSORY_HANDSHAKE_TIMEOUT);
+ broadcastUsbAccessoryHandshake();
+ }
+
private void updateCurrentAccessory() {
// We are entering accessory mode if we have received a request from the host
// and the request has not timed out yet.
@@ -863,10 +882,13 @@
Slog.d(TAG, "entering USB accessory mode: " + mCurrentAccessory);
// defer accessoryAttached if system is not ready
- if (mBootCompleted) {
- mUsbDeviceManager.getCurrentSettings().accessoryAttached(mCurrentAccessory);
- removeMessages(MSG_ACCESSORY_HANDSHAKE_TIMEOUT);
- broadcastUsbAccessoryHandshake();
+ if (!Flags.checkUserActionUnlocked() && mBootCompleted) {
+ attachAccessory();
+ }
+ // Defer accessoryAttached till user unlocks after boot.
+ // When no pin pattern is set, ACTION_USER_UNLOCKED would fire anyways
+ if (Flags.checkUserActionUnlocked() && mUserUnlockedAfterBoot) {
+ attachAccessory();
} // else handle in boot completed
} else {
Slog.e(TAG, "nativeGetAccessoryStrings failed");
@@ -887,7 +909,10 @@
setEnabledFunctions(UsbManager.FUNCTION_NONE, false, operationId);
if (mCurrentAccessory != null) {
- if (mBootCompleted) {
+ if (!Flags.checkUserActionUnlocked() && mBootCompleted) {
+ mPermissionManager.usbAccessoryRemoved(mCurrentAccessory);
+ }
+ if (Flags.checkUserActionUnlocked() && mUserUnlockedAfterBoot) {
mPermissionManager.usbAccessoryRemoved(mCurrentAccessory);
}
mCurrentAccessory = null;
@@ -1377,6 +1402,7 @@
case MSG_BOOT_COMPLETED:
operationId = sUsbOperationCount.incrementAndGet();
mBootCompleted = true;
+ if (DEBUG) Slog.d(TAG, "MSG_BOOT_COMPLETED");
finishBoot(operationId);
break;
case MSG_USER_SWITCHED: {
@@ -1423,14 +1449,38 @@
}
case MSG_INCREASE_SENDSTRING_COUNT: {
mSendStringCount = mSendStringCount + 1;
+ break;
+ }
+ case MSG_USER_UNLOCKED_AFTER_BOOT: {
+ if (DEBUG) Slog.d(TAG, "MSG_USER_UNLOCKED_AFTER_BOOT");
+ if (mUserUnlockedAfterBoot) {
+ break;
+ }
+ mUserUnlockedAfterBoot = true;
+ if (mCurrentUsbFunctionsReceived && mUserUnlockedAfterBoot) {
+ attachAccessoryAfterBoot();
+ }
+ break;
}
}
}
+ private void attachAccessoryAfterBoot() {
+ if (mCurrentAccessory != null) {
+ Slog.i(TAG, "AccessoryAttached");
+ mUsbDeviceManager.getCurrentSettings().accessoryAttached(mCurrentAccessory);
+ broadcastUsbAccessoryHandshake();
+ } else if (mPendingBootAccessoryHandshakeBroadcast) {
+ broadcastUsbAccessoryHandshake();
+ }
+ mPendingBootAccessoryHandshakeBroadcast = false;
+ }
+
public abstract void handlerInitDone(int operationId);
protected void finishBoot(int operationId) {
if (mBootCompleted && mCurrentUsbFunctionsReceived && mSystemReady) {
+ if (DEBUG) Slog.d(TAG, "finishBoot all flags true");
if (mPendingBootBroadcast) {
updateUsbStateBroadcastIfNeeded(getAppliedFunctions(mCurrentFunctions));
mPendingBootBroadcast = false;
@@ -1441,14 +1491,12 @@
} else {
setEnabledFunctions(UsbManager.FUNCTION_NONE, false, operationId);
}
- if (mCurrentAccessory != null) {
- mUsbDeviceManager.getCurrentSettings().accessoryAttached(mCurrentAccessory);
- broadcastUsbAccessoryHandshake();
- } else if (mPendingBootAccessoryHandshakeBroadcast) {
- broadcastUsbAccessoryHandshake();
+ if (!Flags.checkUserActionUnlocked()) {
+ attachAccessoryAfterBoot();
}
-
- mPendingBootAccessoryHandshakeBroadcast = false;
+ if (Flags.checkUserActionUnlocked() && mUserUnlockedAfterBoot) {
+ attachAccessoryAfterBoot();
+ }
updateUsbNotification(false);
updateAdbNotification(false);
updateUsbFunctions();
diff --git a/services/usb/java/com/android/server/usb/flags/usb_flags.aconfig b/services/usb/java/com/android/server/usb/flags/usb_flags.aconfig
index cd96d76..a2d0efd 100644
--- a/services/usb/java/com/android/server/usb/flags/usb_flags.aconfig
+++ b/services/usb/java/com/android/server/usb/flags/usb_flags.aconfig
@@ -14,3 +14,10 @@
description: "This flag enables binding to MtpService when in mtp/ptp modes"
bug: "332256525"
}
+
+flag {
+ name: "check_user_action_unlocked"
+ namespace: "usb"
+ description: "This flag checks if phone is unlocked after boot"
+ bug: "73654179"
+}
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 6490cbe..7cfdec6 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -9734,6 +9734,35 @@
"carrier_supported_satellite_services_per_provider_bundle";
/**
+ * A PersistableBundle that contains a list of key-value pairs, where the values are integer
+ * arrays.
+ * <p>
+ * Keys are the IDs of regional satellite configs as strings and values are
+ * integer arrays of earfcns in the corresponding regions.
+ *
+ * An example config for two regions "1" and "2":
+ * <pre>{@code
+ * <carrier_config>
+ * <pbundle_as_map name="regional_satellite_earfcn_bundle">
+ * <int-array name = "1" num = "2">
+ * <item value = "100"/>
+ * <item value = "200"/>
+ * </int-array>
+ * <int-array name = "2" num = "1">
+ * <item value = "200"/>
+ * </int-array>
+ * </pbundle_as_map>
+ * </carrier_config>
+ * }</pre>
+ * <p>
+ * This config is empty by default.
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
+ public static final String KEY_REGIONAL_SATELLITE_EARFCN_BUNDLE =
+ "regional_satellite_earfcn_bundle";
+
+ /**
* This config enables modem to scan satellite PLMNs specified as per
* {@link #KEY_CARRIER_SUPPORTED_SATELLITE_SERVICES_PER_PROVIDER_BUNDLE} and attach to same
* in case cellular networks are not enabled. This will need specific agreement between
@@ -11264,6 +11293,9 @@
sDefaults.putPersistableBundle(
KEY_CARRIER_SUPPORTED_SATELLITE_SERVICES_PER_PROVIDER_BUNDLE,
PersistableBundle.EMPTY);
+ sDefaults.putPersistableBundle(
+ KEY_REGIONAL_SATELLITE_EARFCN_BUNDLE,
+ PersistableBundle.EMPTY);
sDefaults.putBoolean(KEY_SATELLITE_ATTACH_SUPPORTED_BOOL, false);
sDefaults.putInt(KEY_SATELLITE_CONNECTION_HYSTERESIS_SEC_INT, 180);
sDefaults.putIntArray(KEY_NTN_LTE_RSRP_THRESHOLDS_INT_ARRAY,
diff --git a/core/java/android/text/ClientFlags.java b/telephony/java/android/telephony/CellularIdentifierDisclosure.aidl
similarity index 65%
rename from core/java/android/text/ClientFlags.java
rename to telephony/java/android/telephony/CellularIdentifierDisclosure.aidl
index ca88764..1e41d6e 100644
--- a/core/java/android/text/ClientFlags.java
+++ b/telephony/java/android/telephony/CellularIdentifierDisclosure.aidl
@@ -14,16 +14,7 @@
* limitations under the License.
*/
-package android.text;
+/** @hide */
+package android.telephony;
-/**
- * An aconfig feature flags that can be accessible from application process without
- * ContentProvider IPCs.
- *
- * When you add new flags, you have to add flag string to {@link TextFlags#TEXT_ACONFIGS_FLAGS}.
- *
- * TODO(nona): Remove this class.
- * @hide
- */
-public class ClientFlags {
-}
+parcelable CellularIdentifierDisclosure;
diff --git a/telephony/java/android/telephony/CellularIdentifierDisclosure.java b/telephony/java/android/telephony/CellularIdentifierDisclosure.java
index 7b2db6d..0b6a70f 100644
--- a/telephony/java/android/telephony/CellularIdentifierDisclosure.java
+++ b/telephony/java/android/telephony/CellularIdentifierDisclosure.java
@@ -16,11 +16,16 @@
package android.telephony;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.os.Parcel;
import android.os.Parcelable;
+import com.android.internal.telephony.flags.Flags;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Objects;
@@ -31,16 +36,88 @@
*
* @hide
*/
+@SystemApi
+@FlaggedApi(Flags.FLAG_CELLULAR_IDENTIFIER_DISCLOSURE_INDICATIONS)
public final class CellularIdentifierDisclosure implements Parcelable {
private static final String TAG = "CellularIdentifierDisclosure";
+ /* Non-access stratum protocol messages */
+ /** Unknown */
+ public static final int NAS_PROTOCOL_MESSAGE_UNKNOWN = 0;
+ /** ATTACH REQUESTS. Sample reference: TS 24.301 8.2.4 Applies to 2g, 3g, and 4g networks */
+ public static final int NAS_PROTOCOL_MESSAGE_ATTACH_REQUEST = 1;
+ /** IDENTITY RESPONSE. Sample Reference: TS 24.301 8.2.19.
+ * Applies to 2g, 3g, 4g, and 5g networks */
+ public static final int NAS_PROTOCOL_MESSAGE_IDENTITY_RESPONSE = 2;
+ /** DETACH_REQUEST. Sample Reference: TS 24.301 8.2.11. Applies to 2g, 3g, and 4g networks */
+ public static final int NAS_PROTOCOL_MESSAGE_DETACH_REQUEST = 3;
+ /** TRACKING AREA UPDATE (TAU) REQUEST. Sample Reference: 3GPP TS 24.301 8.2.29.
+ * Note: that per the spec, only temporary IDs should be sent in the TAU Request, but since the
+ * EPS Mobile Identity field supports IMSIs, this is included as an extra safety measure to
+ * combat implementation bugs. Applies to 4g and 5g networks. */
+ public static final int NAS_PROTOCOL_MESSAGE_TRACKING_AREA_UPDATE_REQUEST = 4;
+ /** LOCATION UPDATE REQUEST. Sample Reference: TS 24.008 4.4.3. Applies to 2g and 3g networks */
+ public static final int NAS_PROTOCOL_MESSAGE_LOCATION_UPDATE_REQUEST = 5;
+ /** AUTHENTICATION AND CIPHERING RESPONSE. Reference: 3GPP TS 24.008 4.7.7.1.
+ * Applies to 2g and 3g networks */
+ public static final int NAS_PROTOCOL_MESSAGE_AUTHENTICATION_AND_CIPHERING_RESPONSE = 6;
+ /** REGISTRATION REQUEST. Reference: 3GPP TS 24.501 8.2.6. Applies to 5g networks */
+ public static final int NAS_PROTOCOL_MESSAGE_REGISTRATION_REQUEST = 7;
+ /** DEREGISTRATION REQUEST. Reference: 3GPP TS 24.501 8.2.12. Applies to 5g networks */
+ public static final int NAS_PROTOCOL_MESSAGE_DEREGISTRATION_REQUEST = 8;
+ /** CONNECTION MANAGEMENT REESTABLISHMENT REQUEST. Reference: 3GPP TS 24.008 9.2.4.
+ * Applies to 2g and 3g networks */
+ public static final int NAS_PROTOCOL_MESSAGE_CM_REESTABLISHMENT_REQUEST = 9;
+ /** CONNECTION MANAGEMENT SERVICE REQUEST. Reference: 3GPP TS 24.008 9.2.9.
+ * Applies to 2g and 3g networks */
+ public static final int NAS_PROTOCOL_MESSAGE_CM_SERVICE_REQUEST = 10;
+ /** IMEI DETATCH INDICATION. Reference: 3GPP TS 24.008 9.2.14.
+ * Applies to 2g and 3g networks. Used for circuit-switched detach. */
+ public static final int NAS_PROTOCOL_MESSAGE_IMSI_DETACH_INDICATION = 11;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"NAS_PROTOCOL_MESSAGE_"}, value = {NAS_PROTOCOL_MESSAGE_UNKNOWN,
+ NAS_PROTOCOL_MESSAGE_ATTACH_REQUEST, NAS_PROTOCOL_MESSAGE_IDENTITY_RESPONSE,
+ NAS_PROTOCOL_MESSAGE_DETACH_REQUEST, NAS_PROTOCOL_MESSAGE_TRACKING_AREA_UPDATE_REQUEST,
+ NAS_PROTOCOL_MESSAGE_LOCATION_UPDATE_REQUEST,
+ NAS_PROTOCOL_MESSAGE_AUTHENTICATION_AND_CIPHERING_RESPONSE,
+ NAS_PROTOCOL_MESSAGE_REGISTRATION_REQUEST, NAS_PROTOCOL_MESSAGE_DEREGISTRATION_REQUEST,
+ NAS_PROTOCOL_MESSAGE_CM_REESTABLISHMENT_REQUEST,
+ NAS_PROTOCOL_MESSAGE_CM_SERVICE_REQUEST, NAS_PROTOCOL_MESSAGE_IMSI_DETACH_INDICATION})
+ public @interface NasProtocolMessage {
+ }
+
+ /* Cellular identifiers */
+ /** Unknown */
+ public static final int CELLULAR_IDENTIFIER_UNKNOWN = 0;
+ /** IMSI (International Mobile Subscriber Identity) */
+ public static final int CELLULAR_IDENTIFIER_IMSI = 1;
+ /** IMEI (International Mobile Equipment Identity) */
+ public static final int CELLULAR_IDENTIFIER_IMEI = 2;
+ /** 5G-specific SUCI (Subscription Concealed Identifier) */
+ public static final int CELLULAR_IDENTIFIER_SUCI = 3;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"CELLULAR_IDENTIFIER_"}, value = {CELLULAR_IDENTIFIER_UNKNOWN,
+ CELLULAR_IDENTIFIER_IMSI, CELLULAR_IDENTIFIER_IMEI, CELLULAR_IDENTIFIER_SUCI})
+ public @interface CellularIdentifier {
+ }
+
private @NasProtocolMessage int mNasProtocolMessage;
private @CellularIdentifier int mCellularIdentifier;
private String mPlmn;
private boolean mIsEmergency;
+ /**
+ * Constructor for new CellularIdentifierDisclosure instances.
+ *
+ * @hide
+ */
+ @TestApi
public CellularIdentifierDisclosure(@NasProtocolMessage int nasProtocolMessage,
- @CellularIdentifier int cellularIdentifier, String plmn, boolean isEmergency) {
+ @CellularIdentifier int cellularIdentifier, @NonNull String plmn, boolean isEmergency) {
mNasProtocolMessage = nasProtocolMessage;
mCellularIdentifier = cellularIdentifier;
mPlmn = plmn;
@@ -51,18 +128,30 @@
readFromParcel(in);
}
+ /**
+ * @return the NAS protocol message associated with the disclosed identifier.
+ */
public @NasProtocolMessage int getNasProtocolMessage() {
return mNasProtocolMessage;
}
+ /**
+ * @return the identifier disclosed.
+ */
public @CellularIdentifier int getCellularIdentifier() {
return mCellularIdentifier;
}
- public String getPlmn() {
+ /**
+ * @return the PLMN associated with the disclosure.
+ */
+ @NonNull public String getPlmn() {
return mPlmn;
}
+ /**
+ * @return if the disclosure is associated with an emergency call.
+ */
public boolean isEmergency() {
return mIsEmergency;
}
@@ -73,14 +162,14 @@
}
@Override
- public void writeToParcel(Parcel out, int flags) {
+ public void writeToParcel(@NonNull Parcel out, int flags) {
out.writeInt(mNasProtocolMessage);
out.writeInt(mCellularIdentifier);
out.writeBoolean(mIsEmergency);
out.writeString8(mPlmn);
}
- public static final Parcelable.Creator<CellularIdentifierDisclosure> CREATOR =
+ public static final @NonNull Parcelable.Creator<CellularIdentifierDisclosure> CREATOR =
new Parcelable.Creator<CellularIdentifierDisclosure>() {
public CellularIdentifierDisclosure createFromParcel(Parcel in) {
return new CellularIdentifierDisclosure(in);
@@ -120,42 +209,4 @@
mIsEmergency = in.readBoolean();
mPlmn = in.readString8();
}
-
- public static final int NAS_PROTOCOL_MESSAGE_UNKNOWN = 0;
- public static final int NAS_PROTOCOL_MESSAGE_ATTACH_REQUEST = 1;
- public static final int NAS_PROTOCOL_MESSAGE_IDENTITY_RESPONSE = 2;
- public static final int NAS_PROTOCOL_MESSAGE_DETACH_REQUEST = 3;
- public static final int NAS_PROTOCOL_MESSAGE_TRACKING_AREA_UPDATE_REQUEST = 4;
- public static final int NAS_PROTOCOL_MESSAGE_LOCATION_UPDATE_REQUEST = 5;
- public static final int NAS_PROTOCOL_MESSAGE_AUTHENTICATION_AND_CIPHERING_RESPONSE = 6;
- public static final int NAS_PROTOCOL_MESSAGE_REGISTRATION_REQUEST = 7;
- public static final int NAS_PROTOCOL_MESSAGE_DEREGISTRATION_REQUEST = 8;
- public static final int NAS_PROTOCOL_MESSAGE_CM_REESTABLISHMENT_REQUEST = 9;
- public static final int NAS_PROTOCOL_MESSAGE_CM_SERVICE_REQUEST = 10;
- public static final int NAS_PROTOCOL_MESSAGE_IMSI_DETACH_INDICATION = 11;
-
- /** @hide */
- @Retention(RetentionPolicy.SOURCE)
- @IntDef(prefix = {"NAS_PROTOCOL_MESSAGE_"}, value = {NAS_PROTOCOL_MESSAGE_UNKNOWN,
- NAS_PROTOCOL_MESSAGE_ATTACH_REQUEST, NAS_PROTOCOL_MESSAGE_IDENTITY_RESPONSE,
- NAS_PROTOCOL_MESSAGE_DETACH_REQUEST, NAS_PROTOCOL_MESSAGE_TRACKING_AREA_UPDATE_REQUEST,
- NAS_PROTOCOL_MESSAGE_LOCATION_UPDATE_REQUEST,
- NAS_PROTOCOL_MESSAGE_AUTHENTICATION_AND_CIPHERING_RESPONSE,
- NAS_PROTOCOL_MESSAGE_REGISTRATION_REQUEST, NAS_PROTOCOL_MESSAGE_DEREGISTRATION_REQUEST,
- NAS_PROTOCOL_MESSAGE_CM_REESTABLISHMENT_REQUEST,
- NAS_PROTOCOL_MESSAGE_CM_SERVICE_REQUEST, NAS_PROTOCOL_MESSAGE_IMSI_DETACH_INDICATION})
- public @interface NasProtocolMessage {
- }
-
- public static final int CELLULAR_IDENTIFIER_UNKNOWN = 0;
- public static final int CELLULAR_IDENTIFIER_IMSI = 1;
- public static final int CELLULAR_IDENTIFIER_IMEI = 2;
- public static final int CELLULAR_IDENTIFIER_SUCI = 3;
-
- /** @hide */
- @Retention(RetentionPolicy.SOURCE)
- @IntDef(prefix = {"CELLULAR_IDENTIFIER_"}, value = {CELLULAR_IDENTIFIER_UNKNOWN,
- CELLULAR_IDENTIFIER_IMSI, CELLULAR_IDENTIFIER_IMEI, CELLULAR_IDENTIFIER_SUCI})
- public @interface CellularIdentifier {
- }
}
diff --git a/core/java/android/text/ClientFlags.java b/telephony/java/android/telephony/SecurityAlgorithmUpdate.aidl
similarity index 60%
copy from core/java/android/text/ClientFlags.java
copy to telephony/java/android/telephony/SecurityAlgorithmUpdate.aidl
index ca88764..bee30bd 100644
--- a/core/java/android/text/ClientFlags.java
+++ b/telephony/java/android/telephony/SecurityAlgorithmUpdate.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,16 +14,7 @@
* limitations under the License.
*/
-package android.text;
+/** @hide */
+package android.telephony;
-/**
- * An aconfig feature flags that can be accessible from application process without
- * ContentProvider IPCs.
- *
- * When you add new flags, you have to add flag string to {@link TextFlags#TEXT_ACONFIGS_FLAGS}.
- *
- * TODO(nona): Remove this class.
- * @hide
- */
-public class ClientFlags {
-}
+parcelable SecurityAlgorithmUpdate;
diff --git a/telephony/java/android/telephony/SecurityAlgorithmUpdate.java b/telephony/java/android/telephony/SecurityAlgorithmUpdate.java
index 57209eb..d635b55 100644
--- a/telephony/java/android/telephony/SecurityAlgorithmUpdate.java
+++ b/telephony/java/android/telephony/SecurityAlgorithmUpdate.java
@@ -16,11 +16,16 @@
package android.telephony;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.os.Parcel;
import android.os.Parcelable;
+import com.android.internal.telephony.flags.Flags;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Objects;
@@ -31,14 +36,189 @@
*
* @hide
*/
+@SystemApi
+@FlaggedApi(Flags.FLAG_SECURITY_ALGORITHMS_UPDATE_INDICATIONS)
public final class SecurityAlgorithmUpdate implements Parcelable {
private static final String TAG = "SecurityAlgorithmUpdate";
+ /** 2G GSM circuit switched */
+ public static final int CONNECTION_EVENT_CS_SIGNALLING_GSM = 0;
+ /** 2G GPRS packet services */
+ public static final int CONNECTION_EVENT_PS_SIGNALLING_GPRS = 1;
+ /** 3G circuit switched*/
+ public static final int CONNECTION_EVENT_CS_SIGNALLING_3G = 2;
+ /** 3G packet switched*/
+ public static final int CONNECTION_EVENT_PS_SIGNALLING_3G = 3;
+ /** 4G Non-access stratum */
+ public static final int CONNECTION_EVENT_NAS_SIGNALLING_LTE = 4;
+ /** 4G Access-stratum */
+ public static final int CONNECTION_EVENT_AS_SIGNALLING_LTE = 5;
+ /** VOLTE SIP */
+ public static final int CONNECTION_EVENT_VOLTE_SIP = 6;
+ /** VOLTE SIP SOS (emergency) */
+ public static final int CONNECTION_EVENT_VOLTE_SIP_SOS = 7;
+ /** VOLTE RTP */
+ public static final int CONNECTION_EVENT_VOLTE_RTP = 8;
+ /** VOLTE RTP SOS (emergency) */
+ public static final int CONNECTION_EVENT_VOLTE_RTP_SOS = 9;
+ /** 5G Non-access stratum */
+ public static final int CONNECTION_EVENT_NAS_SIGNALLING_5G = 10;
+ /** 5G Access stratum */
+ public static final int CONNECTION_EVENT_AS_SIGNALLING_5G = 11;
+ /** VoNR SIP */
+ public static final int CONNECTION_EVENT_VONR_SIP = 12;
+ /** VoNR SIP SOS (emergency) */
+ public static final int CONNECTION_EVENT_VONR_SIP_SOS = 13;
+ /** VoNR RTP */
+ public static final int CONNECTION_EVENT_VONR_RTP = 14;
+ /** VoNR RTP SOS (emergency) */
+ public static final int CONNECTION_EVENT_VONR_RTP_SOS = 15;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"CONNECTION_EVENT_"}, value = {CONNECTION_EVENT_CS_SIGNALLING_GSM,
+ CONNECTION_EVENT_PS_SIGNALLING_GPRS, CONNECTION_EVENT_CS_SIGNALLING_3G,
+ CONNECTION_EVENT_PS_SIGNALLING_3G, CONNECTION_EVENT_NAS_SIGNALLING_LTE,
+ CONNECTION_EVENT_AS_SIGNALLING_LTE, CONNECTION_EVENT_VOLTE_SIP,
+ CONNECTION_EVENT_VOLTE_SIP_SOS, CONNECTION_EVENT_VOLTE_RTP,
+ CONNECTION_EVENT_VOLTE_RTP_SOS, CONNECTION_EVENT_NAS_SIGNALLING_5G,
+ CONNECTION_EVENT_AS_SIGNALLING_5G, CONNECTION_EVENT_VONR_SIP,
+ CONNECTION_EVENT_VONR_SIP_SOS, CONNECTION_EVENT_VONR_RTP,
+ CONNECTION_EVENT_VONR_RTP_SOS})
+ public @interface ConnectionEvent {
+ }
+
+ /* GSM CS services, see 3GPP TS 43.020 for details */
+ /** A5/0 - the null cipher */
+ public static final int SECURITY_ALGORITHM_A50 = 0;
+ /** A5/1 cipher */
+ public static final int SECURITY_ALGORITHM_A51 = 1;
+ /** A5/2 cipher */
+ public static final int SECURITY_ALGORITHM_A52 = 2;
+ /** A5/3 cipher */
+ public static final int SECURITY_ALGORITHM_A53 = 3;
+ /** A5/4 cipher */
+ public static final int SECURITY_ALGORITHM_A54 = 4;
+ /* GPRS PS services (3GPP TS 43.020) */
+ /** GEA0 - null cipher */
+ public static final int SECURITY_ALGORITHM_GEA0 = 14;
+ /** GEA1 cipher */
+ public static final int SECURITY_ALGORITHM_GEA1 = 15;
+ /** GEA2 cipher */
+ public static final int SECURITY_ALGORITHM_GEA2 = 16;
+ /** GEA3 cipher */
+ public static final int SECURITY_ALGORITHM_GEA3 = 17;
+ /** GEA4 cipher */
+ public static final int SECURITY_ALGORITHM_GEA4 = 18;
+ /** GEA5 cipher */
+ public static final int SECURITY_ALGORITHM_GEA5 = 19;
+ /* 3G PS/CS services (3GPP TS 33.102) */
+ /** UEA0 - null cipher */
+ public static final int SECURITY_ALGORITHM_UEA0 = 29;
+ /** UEA1 cipher */
+ public static final int SECURITY_ALGORITHM_UEA1 = 30;
+ /** UEA2 cipher */
+ public static final int SECURITY_ALGORITHM_UEA2 = 31;
+ /* 4G PS services & 5G NSA (3GPP TS 33.401) */
+ /** EEA0 - null cipher */
+ public static final int SECURITY_ALGORITHM_EEA0 = 41;
+ /** EEA1 */
+ public static final int SECURITY_ALGORITHM_EEA1 = 42;
+ /** EEA2 */
+ public static final int SECURITY_ALGORITHM_EEA2 = 43;
+ /** EEA3 */
+ public static final int SECURITY_ALGORITHM_EEA3 = 44;
+ /* 5G PS services (3GPP TS 33.401 for 5G NSA and 3GPP TS 33.501 for 5G SA) */
+ /** NEA0 - the null cipher */
+ public static final int SECURITY_ALGORITHM_NEA0 = 55;
+ /** NEA1 */
+ public static final int SECURITY_ALGORITHM_NEA1 = 56;
+ /** NEA2 */
+ public static final int SECURITY_ALGORITHM_NEA2 = 57;
+ /** NEA3 */
+ public static final int SECURITY_ALGORITHM_NEA3 = 58;
+ /* IMS and SIP layer security (See 3GPP TS 33.203) */
+ /** No IPsec config */
+ public static final int SECURITY_ALGORITHM_SIP_NO_IPSEC_CONFIG = 66;
+ /** No IMS security, recommended to use SIP_NO_IPSEC_CONFIG and SIP_NULL instead */
+ public static final int SECURITY_ALGORITHM_IMS_NULL = 67;
+ /* IPSEC is present */
+ /** SIP security is not enabled */
+ public static final int SECURITY_ALGORITHM_SIP_NULL = 68;
+ /** AES GCM mode */
+ public static final int SECURITY_ALGORITHM_AES_GCM = 69;
+ /** AES GMAC mode */
+ public static final int SECURITY_ALGORITHM_AES_GMAC = 70;
+ /** AES CBC mode */
+ public static final int SECURITY_ALGORITHM_AES_CBC = 71;
+ /** DES EDE3 CBC mode */
+ public static final int SECURITY_ALGORITHM_DES_EDE3_CBC = 72;
+ /** AES EDE3 CBC mode */
+ public static final int SECURITY_ALGORITHM_AES_EDE3_CBC = 73;
+ /** HMAC SHA1 96 */
+ public static final int SECURITY_ALGORITHM_HMAC_SHA1_96 = 74;
+ /** HMAC MD5 96 */
+ public static final int SECURITY_ALGORITHM_HMAC_MD5_96 = 75;
+ /* RTP and SRTP (see 3GPP TS 33.328) */
+ /** RTP only, SRTP is not being used */
+ public static final int SECURITY_ALGORITHM_RTP = 85;
+ /* When SRTP is available and used */
+ /** SRTP with null ciphering */
+ public static final int SECURITY_ALGORITHM_SRTP_NULL = 86;
+ /** SRTP with AES counter mode */
+ public static final int SECURITY_ALGORITHM_SRTP_AES_COUNTER = 87;
+ /** SRTP with AES F8 mode */
+ public static final int SECURITY_ALGORITHM_SRTP_AES_F8 = 88;
+ /** SRTP with HMAC SHA1 */
+ public static final int SECURITY_ALGORITHM_SRTP_HMAC_SHA1 = 89;
+ /* Ciphers for ePDG (3GPP TS 33.402) */
+ /** ePDG encryption - AES GCM mode */
+ public static final int SECURITY_ALGORITHM_ENCR_AES_GCM_16 = 99;
+ /** ePDG encryption - AES GCM CBC mode */
+ public static final int SECURITY_ALGORITHM_ENCR_AES_CBC = 100;
+ /** ePDG authentication - HMAC SHA1 256 128 */
+ public static final int SECURITY_ALGORITHM_AUTH_HMAC_SHA2_256_128 = 101;
+ /** Unknown */
+ public static final int SECURITY_ALGORITHM_UNKNOWN = 113;
+ /** Other */
+ public static final int SECURITY_ALGORITHM_OTHER = 114;
+ /** Proprietary algorithms */
+ public static final int SECURITY_ALGORITHM_ORYX = 124;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"CONNECTION_EVENT_"}, value = {SECURITY_ALGORITHM_A50, SECURITY_ALGORITHM_A51,
+ SECURITY_ALGORITHM_A52, SECURITY_ALGORITHM_A53,
+ SECURITY_ALGORITHM_A54, SECURITY_ALGORITHM_GEA0, SECURITY_ALGORITHM_GEA1,
+ SECURITY_ALGORITHM_GEA2, SECURITY_ALGORITHM_GEA3, SECURITY_ALGORITHM_GEA4,
+ SECURITY_ALGORITHM_GEA5, SECURITY_ALGORITHM_UEA0, SECURITY_ALGORITHM_UEA1,
+ SECURITY_ALGORITHM_UEA2, SECURITY_ALGORITHM_EEA0, SECURITY_ALGORITHM_EEA1,
+ SECURITY_ALGORITHM_EEA2, SECURITY_ALGORITHM_EEA3, SECURITY_ALGORITHM_NEA0,
+ SECURITY_ALGORITHM_NEA1, SECURITY_ALGORITHM_NEA2, SECURITY_ALGORITHM_NEA3,
+ SECURITY_ALGORITHM_SIP_NO_IPSEC_CONFIG, SECURITY_ALGORITHM_IMS_NULL,
+ SECURITY_ALGORITHM_SIP_NULL, SECURITY_ALGORITHM_AES_GCM,
+ SECURITY_ALGORITHM_AES_GMAC, SECURITY_ALGORITHM_AES_CBC,
+ SECURITY_ALGORITHM_DES_EDE3_CBC, SECURITY_ALGORITHM_AES_EDE3_CBC,
+ SECURITY_ALGORITHM_HMAC_SHA1_96, SECURITY_ALGORITHM_HMAC_MD5_96,
+ SECURITY_ALGORITHM_RTP, SECURITY_ALGORITHM_SRTP_NULL,
+ SECURITY_ALGORITHM_SRTP_AES_COUNTER, SECURITY_ALGORITHM_SRTP_AES_F8,
+ SECURITY_ALGORITHM_SRTP_HMAC_SHA1, SECURITY_ALGORITHM_ENCR_AES_GCM_16,
+ SECURITY_ALGORITHM_ENCR_AES_CBC, SECURITY_ALGORITHM_AUTH_HMAC_SHA2_256_128,
+ SECURITY_ALGORITHM_UNKNOWN, SECURITY_ALGORITHM_OTHER, SECURITY_ALGORITHM_ORYX})
+ public @interface SecurityAlgorithm {
+ }
+
private @ConnectionEvent int mConnectionEvent;
private @SecurityAlgorithm int mEncryption;
private @SecurityAlgorithm int mIntegrity;
private boolean mIsUnprotectedEmergency;
+ /**
+ * Constructor for new SecurityAlgorithmUpdate instances.
+ *
+ * @hide
+ */
+ @TestApi
public SecurityAlgorithmUpdate(@ConnectionEvent int connectionEvent,
@SecurityAlgorithm int encryption, @SecurityAlgorithm int integrity,
boolean isUnprotectedEmergency) {
@@ -52,18 +232,30 @@
readFromParcel(in);
}
+ /**
+ * @return the connection event.
+ */
public @ConnectionEvent int getConnectionEvent() {
return mConnectionEvent;
}
+ /**
+ * @return the encryption algorithm.
+ */
public @SecurityAlgorithm int getEncryption() {
return mEncryption;
}
+ /**
+ * @return the integrity algorithm.
+ */
public @SecurityAlgorithm int getIntegrity() {
return mIntegrity;
}
+ /**
+ * @return if the security algorithm update is associated with an unprotected emergency call.
+ */
public boolean isUnprotectedEmergency() {
return mIsUnprotectedEmergency;
}
@@ -74,7 +266,7 @@
}
@Override
- public void writeToParcel(Parcel out, int flags) {
+ public void writeToParcel(@NonNull Parcel out, int flags) {
out.writeInt(mConnectionEvent);
out.writeInt(mEncryption);
out.writeInt(mIntegrity);
@@ -88,7 +280,7 @@
mIsUnprotectedEmergency = in.readBoolean();
}
- public static final Parcelable.Creator<SecurityAlgorithmUpdate> CREATOR =
+ public static final @NonNull Parcelable.Creator<SecurityAlgorithmUpdate> CREATOR =
new Parcelable.Creator<SecurityAlgorithmUpdate>() {
public SecurityAlgorithmUpdate createFromParcel(Parcel in) {
return new SecurityAlgorithmUpdate(in);
@@ -121,103 +313,4 @@
public int hashCode() {
return Objects.hash(mConnectionEvent, mEncryption, mIntegrity, mIsUnprotectedEmergency);
}
-
- public static final int CONNECTION_EVENT_CS_SIGNALLING_GSM = 0;
- public static final int CONNECTION_EVENT_PS_SIGNALLING_GPRS = 1;
- public static final int CONNECTION_EVENT_CS_SIGNALLING_3G = 2;
- public static final int CONNECTION_EVENT_PS_SIGNALLING_3G = 3;
- public static final int CONNECTION_EVENT_NAS_SIGNALLING_LTE = 4;
- public static final int CONNECTION_EVENT_AS_SIGNALLING_LTE = 5;
- public static final int CONNECTION_EVENT_VOLTE_SIP = 6;
- public static final int CONNECTION_EVENT_VOLTE_SIP_SOS = 7;
- public static final int CONNECTION_EVENT_VOLTE_RTP = 8;
- public static final int CONNECTION_EVENT_VOLTE_RTP_SOS = 9;
- public static final int CONNECTION_EVENT_NAS_SIGNALLING_5G = 10;
- public static final int CONNECTION_EVENT_AS_SIGNALLING_5G = 11;
- public static final int CONNECTION_EVENT_VONR_SIP = 12;
- public static final int CONNECTION_EVENT_VONR_SIP_SOS = 13;
- public static final int CONNECTION_EVENT_VONR_RTP = 14;
- public static final int CONNECTION_EVENT_VONR_RTP_SOS = 15;
-
- /** @hide */
- @Retention(RetentionPolicy.SOURCE)
- @IntDef(prefix = {"CONNECTION_EVENT_"}, value = {CONNECTION_EVENT_CS_SIGNALLING_GSM,
- CONNECTION_EVENT_PS_SIGNALLING_GPRS, CONNECTION_EVENT_CS_SIGNALLING_3G,
- CONNECTION_EVENT_PS_SIGNALLING_3G, CONNECTION_EVENT_NAS_SIGNALLING_LTE,
- CONNECTION_EVENT_AS_SIGNALLING_LTE, CONNECTION_EVENT_VOLTE_SIP,
- CONNECTION_EVENT_VOLTE_SIP_SOS, CONNECTION_EVENT_VOLTE_RTP,
- CONNECTION_EVENT_VOLTE_RTP_SOS, CONNECTION_EVENT_NAS_SIGNALLING_5G,
- CONNECTION_EVENT_AS_SIGNALLING_5G, CONNECTION_EVENT_VONR_SIP,
- CONNECTION_EVENT_VONR_SIP_SOS, CONNECTION_EVENT_VONR_RTP,
- CONNECTION_EVENT_VONR_RTP_SOS})
- public @interface ConnectionEvent {
- }
-
- public static final int SECURITY_ALGORITHM_A50 = 0;
- public static final int SECURITY_ALGORITHM_A51 = 1;
- public static final int SECURITY_ALGORITHM_A52 = 2;
- public static final int SECURITY_ALGORITHM_A53 = 3;
- public static final int SECURITY_ALGORITHM_A54 = 4;
- public static final int SECURITY_ALGORITHM_GEA0 = 14;
- public static final int SECURITY_ALGORITHM_GEA1 = 15;
- public static final int SECURITY_ALGORITHM_GEA2 = 16;
- public static final int SECURITY_ALGORITHM_GEA3 = 17;
- public static final int SECURITY_ALGORITHM_GEA4 = 18;
- public static final int SECURITY_ALGORITHM_GEA5 = 19;
- public static final int SECURITY_ALGORITHM_UEA0 = 29;
- public static final int SECURITY_ALGORITHM_UEA1 = 30;
- public static final int SECURITY_ALGORITHM_UEA2 = 31;
- public static final int SECURITY_ALGORITHM_EEA0 = 41;
- public static final int SECURITY_ALGORITHM_EEA1 = 42;
- public static final int SECURITY_ALGORITHM_EEA2 = 43;
- public static final int SECURITY_ALGORITHM_EEA3 = 44;
- public static final int SECURITY_ALGORITHM_NEA0 = 55;
- public static final int SECURITY_ALGORITHM_NEA1 = 56;
- public static final int SECURITY_ALGORITHM_NEA2 = 57;
- public static final int SECURITY_ALGORITHM_NEA3 = 58;
- public static final int SECURITY_ALGORITHM_SIP_NO_IPSEC_CONFIG = 66;
- public static final int SECURITY_ALGORITHM_IMS_NULL = 67;
- public static final int SECURITY_ALGORITHM_SIP_NULL = 68;
- public static final int SECURITY_ALGORITHM_AES_GCM = 69;
- public static final int SECURITY_ALGORITHM_AES_GMAC = 70;
- public static final int SECURITY_ALGORITHM_AES_CBC = 71;
- public static final int SECURITY_ALGORITHM_DES_EDE3_CBC = 72;
- public static final int SECURITY_ALGORITHM_AES_EDE3_CBC = 73;
- public static final int SECURITY_ALGORITHM_HMAC_SHA1_96 = 74;
- public static final int SECURITY_ALGORITHM_HMAC_MD5_96 = 75;
- public static final int SECURITY_ALGORITHM_RTP = 85;
- public static final int SECURITY_ALGORITHM_SRTP_NULL = 86;
- public static final int SECURITY_ALGORITHM_SRTP_AES_COUNTER = 87;
- public static final int SECURITY_ALGORITHM_SRTP_AES_F8 = 88;
- public static final int SECURITY_ALGORITHM_SRTP_HMAC_SHA1 = 89;
- public static final int SECURITY_ALGORITHM_ENCR_AES_GCM_16 = 99;
- public static final int SECURITY_ALGORITHM_ENCR_AES_CBC = 100;
- public static final int SECURITY_ALGORITHM_AUTH_HMAC_SHA2_256_128 = 101;
- public static final int SECURITY_ALGORITHM_UNKNOWN = 113;
- public static final int SECURITY_ALGORITHM_OTHER = 114;
- public static final int SECURITY_ALGORITHM_ORYX = 124;
-
- /** @hide */
- @Retention(RetentionPolicy.SOURCE)
- @IntDef(prefix = {"CONNECTION_EVENT_"}, value = {SECURITY_ALGORITHM_A50, SECURITY_ALGORITHM_A51,
- SECURITY_ALGORITHM_A52, SECURITY_ALGORITHM_A53,
- SECURITY_ALGORITHM_A54, SECURITY_ALGORITHM_GEA0, SECURITY_ALGORITHM_GEA1,
- SECURITY_ALGORITHM_GEA2, SECURITY_ALGORITHM_GEA3, SECURITY_ALGORITHM_GEA4,
- SECURITY_ALGORITHM_GEA5, SECURITY_ALGORITHM_UEA0, SECURITY_ALGORITHM_UEA1,
- SECURITY_ALGORITHM_UEA2, SECURITY_ALGORITHM_EEA0, SECURITY_ALGORITHM_EEA1,
- SECURITY_ALGORITHM_EEA2, SECURITY_ALGORITHM_EEA3, SECURITY_ALGORITHM_NEA0,
- SECURITY_ALGORITHM_NEA1, SECURITY_ALGORITHM_NEA2, SECURITY_ALGORITHM_NEA3,
- SECURITY_ALGORITHM_SIP_NO_IPSEC_CONFIG, SECURITY_ALGORITHM_IMS_NULL,
- SECURITY_ALGORITHM_SIP_NULL, SECURITY_ALGORITHM_AES_GCM,
- SECURITY_ALGORITHM_AES_GMAC, SECURITY_ALGORITHM_AES_CBC,
- SECURITY_ALGORITHM_DES_EDE3_CBC, SECURITY_ALGORITHM_AES_EDE3_CBC,
- SECURITY_ALGORITHM_HMAC_SHA1_96, SECURITY_ALGORITHM_HMAC_MD5_96,
- SECURITY_ALGORITHM_RTP, SECURITY_ALGORITHM_SRTP_NULL,
- SECURITY_ALGORITHM_SRTP_AES_COUNTER, SECURITY_ALGORITHM_SRTP_AES_F8,
- SECURITY_ALGORITHM_SRTP_HMAC_SHA1, SECURITY_ALGORITHM_ENCR_AES_GCM_16,
- SECURITY_ALGORITHM_ENCR_AES_CBC, SECURITY_ALGORITHM_AUTH_HMAC_SHA2_256_128,
- SECURITY_ALGORITHM_UNKNOWN, SECURITY_ALGORITHM_OTHER, SECURITY_ALGORITHM_ORYX})
- public @interface SecurityAlgorithm {
- }
-
}
diff --git a/telephony/java/android/telephony/satellite/SatelliteManager.java b/telephony/java/android/telephony/satellite/SatelliteManager.java
index 887b798..23203ed 100644
--- a/telephony/java/android/telephony/satellite/SatelliteManager.java
+++ b/telephony/java/android/telephony/satellite/SatelliteManager.java
@@ -281,6 +281,14 @@
"satellite_access_configuration";
/**
+ * Bundle key to get the response from
+ * {@link #requestSelectedNbIotSatelliteSubscriptionId(Executor, OutcomeReceiver)}.
+ * @hide
+ */
+ public static final String KEY_SELECTED_NB_IOT_SATELLITE_SUBSCRIPTION_ID =
+ "selected_nb_iot_satellite_subscription_id";
+
+ /**
* The request was successfully processed.
* @hide
*/
@@ -531,6 +539,12 @@
@FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
public static final int SATELLITE_RESULT_ENABLE_IN_PROGRESS = 29;
+ /**
+ * There is no valid satellite subscription selected.
+ * @hide
+ */
+ public static final int SATELLITE_RESULT_NO_VALID_SATELLITE_SUBSCRIPTION = 30;
+
/** @hide */
@IntDef(prefix = {"SATELLITE_RESULT_"}, value = {
SATELLITE_RESULT_SUCCESS,
@@ -562,7 +576,8 @@
SATELLITE_RESULT_LOCATION_NOT_AVAILABLE,
SATELLITE_RESULT_EMERGENCY_CALL_IN_PROGRESS,
SATELLITE_RESULT_DISABLE_IN_PROGRESS,
- SATELLITE_RESULT_ENABLE_IN_PROGRESS
+ SATELLITE_RESULT_ENABLE_IN_PROGRESS,
+ SATELLITE_RESULT_NO_VALID_SATELLITE_SUBSCRIPTION
})
@Retention(RetentionPolicy.SOURCE)
public @interface SatelliteResult {}
@@ -2464,6 +2479,68 @@
}
/**
+ * Request to get the currently selected satellite subscription id as an {@link Integer}.
+ *
+ * @param executor The executor on which the callback will be called.
+ * @param callback The callback object to which the result will be delivered.
+ * If the request is successful, {@link OutcomeReceiver#onResult(Object)}
+ * will return the time after which the satellite will be visible.
+ * If the request is not successful, {@link OutcomeReceiver#onError(Throwable)}
+ * will return a {@link SatelliteException} with the {@link SatelliteResult}.
+ *
+ * @throws SecurityException if the caller doesn't have required permission.
+ *
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+ public void requestSelectedNbIotSatelliteSubscriptionId(
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull OutcomeReceiver<Integer, SatelliteException> callback) {
+ Objects.requireNonNull(executor);
+ Objects.requireNonNull(callback);
+
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ ResultReceiver receiver = new ResultReceiver(null) {
+ @Override
+ protected void onReceiveResult(int resultCode, Bundle resultData) {
+ if (resultCode == SATELLITE_RESULT_SUCCESS) {
+ if (resultData
+ .containsKey(KEY_SELECTED_NB_IOT_SATELLITE_SUBSCRIPTION_ID)) {
+ int selectedSatelliteSubscriptionId =
+ resultData
+ .getInt(KEY_SELECTED_NB_IOT_SATELLITE_SUBSCRIPTION_ID);
+ executor.execute(() -> Binder.withCleanCallingIdentity(() ->
+ callback.onResult(selectedSatelliteSubscriptionId)));
+ } else {
+ loge(
+ "KEY_SELECTED_NB_IOT_SATELLITE_SUBSCRIPTION_ID does not exist."
+ );
+ executor.execute(() -> Binder.withCleanCallingIdentity(() ->
+ callback.onError(new SatelliteException(
+ SATELLITE_RESULT_REQUEST_FAILED))));
+ }
+ } else {
+ executor.execute(() -> Binder.withCleanCallingIdentity(() ->
+ callback.onError(new SatelliteException(resultCode))));
+ }
+ }
+ };
+ telephony.requestSelectedNbIotSatelliteSubscriptionId(receiver);
+ } else {
+ loge("requestSelectedNbIotSatelliteSubscriptionId() invalid telephony");
+ executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError(
+ new SatelliteException(SATELLITE_RESULT_ILLEGAL_STATE))));
+ }
+ } catch (RemoteException ex) {
+ loge("requestSelectedNbIotSatelliteSubscriptionId() RemoteException: " + ex);
+ executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError(
+ new SatelliteException(SATELLITE_RESULT_ILLEGAL_STATE))));
+ }
+ }
+
+ /**
* Inform whether the device is aligned with the satellite in both real and demo mode.
*
* In demo mode, framework will send datagram to modem only when device is aligned with
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index a584273..d22e9fa 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -3018,6 +3018,17 @@
+ "android.Manifest.permission.SATELLITE_COMMUNICATION)")
void requestTimeForNextSatelliteVisibility(in ResultReceiver receiver);
+
+ /**
+ * Request to get the currently selected satellite subscription id.
+ *
+ * @param receiver Result receiver to get the error code of the request and the currently
+ * selected satellite subscription id.
+ */
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission("
+ + "android.Manifest.permission.SATELLITE_COMMUNICATION)")
+ void requestSelectedNbIotSatelliteSubscriptionId(in ResultReceiver receiver);
+
/**
* Inform whether the device is aligned with the satellite in both real and demo mode.
*
diff --git a/test-mock/src/android/test/mock/MockContentResolver.java b/test-mock/src/android/test/mock/MockContentResolver.java
index 8f4bccc..af6ee3d 100644
--- a/test-mock/src/android/test/mock/MockContentResolver.java
+++ b/test-mock/src/android/test/mock/MockContentResolver.java
@@ -24,6 +24,7 @@
import android.content.IContentProvider;
import android.database.ContentObserver;
import android.net.Uri;
+import android.util.Log;
import java.util.Collection;
import java.util.HashMap;
@@ -53,6 +54,7 @@
* </div>
*/
public class MockContentResolver extends ContentResolver {
+ private static final String TAG = "MockContentResolver";
Map<String, ContentProvider> mProviders;
/**
@@ -105,6 +107,7 @@
if (provider != null) {
return provider.getIContentProvider();
} else {
+ Log.w(TAG, "Provider does not exist: " + name);
return null;
}
}
diff --git a/tests/AppJankTest/src/android/app/jank/tests/JankDataProcessorTest.java b/tests/AppJankTest/src/android/app/jank/tests/JankDataProcessorTest.java
index 2cd625e..4d495ad 100644
--- a/tests/AppJankTest/src/android/app/jank/tests/JankDataProcessorTest.java
+++ b/tests/AppJankTest/src/android/app/jank/tests/JankDataProcessorTest.java
@@ -18,7 +18,9 @@
import static org.junit.Assert.assertEquals;
+import android.app.jank.AppJankStats;
import android.app.jank.Flags;
+import android.app.jank.FrameOverrunHistogram;
import android.app.jank.JankDataProcessor;
import android.app.jank.StateTracker;
import android.platform.test.annotations.RequiresFlagsEnabled;
@@ -39,6 +41,7 @@
import org.junit.runner.RunWith;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
@RunWith(AndroidJUnit4.class)
@@ -154,6 +157,73 @@
assertEquals(totalFrames, histogramFrames);
}
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_DETAILED_APP_JANK_METRICS_API)
+ public void mergeAppJankStats_confirmStatAddedToPendingStats() {
+ HashMap<String, JankDataProcessor.PendingJankStat> pendingStats =
+ mJankDataProcessor.getPendingJankStats();
+
+ assertEquals(pendingStats.size(), 0);
+
+ AppJankStats jankStats = getAppJankStats();
+ mJankDataProcessor.mergeJankStats(jankStats, sActivityName);
+
+ pendingStats = mJankDataProcessor.getPendingJankStats();
+
+ assertEquals(pendingStats.size(), 1);
+ }
+
+ /**
+ * This test confirms matching states are combined into one pending stat. When JankStats are
+ * merged from outside the platform they will contain widget category, widget id and widget
+ * state. If an incoming JankStats matches a pending stat on all those fields the incoming
+ * JankStat will be merged into the existing stat.
+ */
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_DETAILED_APP_JANK_METRICS_API)
+ public void mergeAppJankStats_confirmStatsWithMatchingStatesAreCombinedIntoOnePendingStat() {
+ AppJankStats jankStats = getAppJankStats();
+ mJankDataProcessor.mergeJankStats(jankStats, sActivityName);
+
+ HashMap<String, JankDataProcessor.PendingJankStat> pendingStats =
+ mJankDataProcessor.getPendingJankStats();
+ assertEquals(pendingStats.size(), 1);
+
+ AppJankStats secondJankStat = getAppJankStats();
+ mJankDataProcessor.mergeJankStats(secondJankStat, sActivityName);
+
+ pendingStats = mJankDataProcessor.getPendingJankStats();
+
+ assertEquals(pendingStats.size(), 1);
+ }
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_DETAILED_APP_JANK_METRICS_API)
+ public void mergeAppJankStats_whenStatsWithMatchingStatesMerge_confirmFrameCountsAdded() {
+ AppJankStats jankStats = getAppJankStats();
+ mJankDataProcessor.mergeJankStats(jankStats, sActivityName);
+ mJankDataProcessor.mergeJankStats(jankStats, sActivityName);
+
+ HashMap<String, JankDataProcessor.PendingJankStat> pendingStats =
+ mJankDataProcessor.getPendingJankStats();
+
+ String statKey = pendingStats.keySet().iterator().next();
+ JankDataProcessor.PendingJankStat pendingStat = pendingStats.get(statKey);
+
+ assertEquals(pendingStats.size(), 1);
+ // The same jankStats objects are merged twice, this should result in the frame counts being
+ // doubled.
+ assertEquals(jankStats.getJankyFrameCount() * 2, pendingStat.getJankyFrames());
+ assertEquals(jankStats.getTotalFrameCount() * 2, pendingStat.getTotalFrames());
+
+ int[] originalHistogramBuckets = jankStats.getFrameOverrunHistogram().getBucketCounters();
+ int[] frameOverrunBuckets = pendingStat.getFrameOverrunBuckets();
+
+ for (int i = 0; i < frameOverrunBuckets.length; i++) {
+ assertEquals(originalHistogramBuckets[i] * 2, frameOverrunBuckets[i]);
+ }
+ }
+
// TODO b/375005277 add tests that cover logging and releasing resources back to pool.
private long getTotalFramesCounted() {
@@ -276,4 +346,26 @@
return mockData;
}
+ private AppJankStats getAppJankStats() {
+ AppJankStats jankStats = new AppJankStats(
+ /*App Uid*/APP_ID,
+ /*Widget Id*/"test widget id",
+ /*Widget Category*/AppJankStats.SCROLL,
+ /*Widget State*/AppJankStats.SCROLLING,
+ /*Total Frames*/100,
+ /*Janky Frames*/25,
+ getOverrunHistogram()
+ );
+ return jankStats;
+ }
+
+ private FrameOverrunHistogram getOverrunHistogram() {
+ FrameOverrunHistogram overrunHistogram = new FrameOverrunHistogram();
+ overrunHistogram.addFrameOverrunMillis(-2);
+ overrunHistogram.addFrameOverrunMillis(1);
+ overrunHistogram.addFrameOverrunMillis(5);
+ overrunHistogram.addFrameOverrunMillis(25);
+ return overrunHistogram;
+ }
+
}
diff --git a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/DesktopModeAppHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/DesktopModeAppHelper.kt
index 9f5e6d1..6432827 100644
--- a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/DesktopModeAppHelper.kt
+++ b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/DesktopModeAppHelper.kt
@@ -16,6 +16,7 @@
package com.android.server.wm.flicker.helpers
+import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
import android.content.Context
import android.graphics.Insets
import android.graphics.Rect
@@ -74,13 +75,28 @@
.waitForAndVerify()
}
+ /** Launch an app and ensure it's moved to Desktop if it has not. */
+ fun enterDesktopMode(
+ wmHelper: WindowManagerStateHelper,
+ device: UiDevice,
+ motionEventHelper: MotionEventHelper = MotionEventHelper(getInstrumentation(), TOUCH),
+ ) {
+ innerHelper.launchViaIntent(wmHelper)
+ if (!isInDesktopWindowingMode(wmHelper)) {
+ enterDesktopModeWithDrag(
+ wmHelper = wmHelper,
+ device = device,
+ motionEventHelper = motionEventHelper
+ )
+ }
+ }
+
/** Move an app to Desktop by dragging the app handle at the top. */
- fun enterDesktopWithDrag(
+ private fun enterDesktopModeWithDrag(
wmHelper: WindowManagerStateHelper,
device: UiDevice,
motionEventHelper: MotionEventHelper = MotionEventHelper(getInstrumentation(), TOUCH)
) {
- innerHelper.launchViaIntent(wmHelper)
dragToDesktop(
wmHelper = wmHelper,
device = device,
@@ -139,14 +155,35 @@
?: error("Unable to find resource $MINIMIZE_BUTTON_VIEW\n")
}
- fun minimizeDesktopApp(wmHelper: WindowManagerStateHelper, device: UiDevice) {
+ fun minimizeDesktopApp(wmHelper: WindowManagerStateHelper, device: UiDevice, isPip: Boolean = false) {
val caption = getCaptionForTheApp(wmHelper, device)
val minimizeButton = getMinimizeButtonForTheApp(caption)
minimizeButton.click()
wmHelper
.StateSyncBuilder()
.withAppTransitionIdle()
- .withWindowSurfaceDisappeared(innerHelper)
+ .apply {
+ if (isPip) withPipShown() else withWindowSurfaceDisappeared(innerHelper)
+ }
+ .waitForAndVerify()
+ }
+
+ private fun getHeaderEmptyView(caption: UiObject2?): UiObject2 {
+ return caption
+ ?.children
+ ?.find { it.resourceName.endsWith(HEADER_EMPTY_VIEW) }
+ ?: error("Unable to find resource $HEADER_EMPTY_VIEW\n")
+ }
+
+ /** Click on an existing window's header to bring it to the front. */
+ fun bringToFront(wmHelper: WindowManagerStateHelper, device: UiDevice) {
+ val caption = getCaptionForTheApp(wmHelper, device)
+ val openHeaderView = getHeaderEmptyView(caption)
+ openHeaderView.click()
+ wmHelper
+ .StateSyncBuilder()
+ .withAppTransitionIdle()
+ .withTopVisibleApp(innerHelper)
.waitForAndVerify()
}
@@ -415,6 +452,10 @@
return metricInsets.getInsetsIgnoringVisibility(typeMask)
}
+ // Requirement of DesktopWindowingMode is having a minimum of 1 app in WINDOWING_MODE_FREEFORM.
+ private fun isInDesktopWindowingMode(wmHelper: WindowManagerStateHelper) =
+ wmHelper.getWindow(innerHelper)?.windowingMode == WINDOWING_MODE_FREEFORM
+
private companion object {
val TIMEOUT: Duration = Duration.ofSeconds(3)
const val SNAP_RESIZE_DRAG_INSET: Int = 5 // inset to avoid dragging to display edge
@@ -427,6 +468,7 @@
const val SNAP_LEFT_BUTTON: String = "maximize_menu_snap_left_button"
const val SNAP_RIGHT_BUTTON: String = "maximize_menu_snap_right_button"
const val MINIMIZE_BUTTON_VIEW: String = "minimize_window"
+ const val HEADER_EMPTY_VIEW: String = "caption_handle"
val caption: BySelector
get() = By.res(SYSTEMUI_PACKAGE, CAPTION)
}
diff --git a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/StartMediaProjectionAppHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/StartMediaProjectionAppHelper.kt
index 69fde01..9e48848 100644
--- a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/StartMediaProjectionAppHelper.kt
+++ b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/StartMediaProjectionAppHelper.kt
@@ -65,10 +65,45 @@
.waitForAndVerify()
}
+ fun startSingleAppMediaProjectionWithExtraIntent(
+ wmHelper: WindowManagerStateHelper,
+ targetApp: StandardAppHelper
+ ) {
+ clickStartMediaProjectionWithExtraIntentButton()
+ chooseSingleAppOption()
+ startScreenSharing()
+ selectTargetApp(targetApp.appName)
+ wmHelper
+ .StateSyncBuilder()
+ .withAppTransitionIdle()
+ .withHomeActivityVisible()
+ .waitForAndVerify()
+ }
+
+ fun startSingleAppMediaProjectionFromRecents(
+ wmHelper: WindowManagerStateHelper,
+ targetApp: StandardAppHelper,
+ recentTasksIndex: Int = 0,
+ ) {
+ clickStartMediaProjectionButton()
+ chooseSingleAppOption()
+ startScreenSharing()
+ selectTargetAppRecent(recentTasksIndex)
+ wmHelper
+ .StateSyncBuilder()
+ .withAppTransitionIdle()
+ .withWindowSurfaceAppeared(targetApp)
+ .waitForAndVerify()
+ }
+
private fun clickStartMediaProjectionButton() {
findObject(By.res(packageName, START_MEDIA_PROJECTION_BUTTON_ID)).also { it.click() }
}
+ private fun clickStartMediaProjectionWithExtraIntentButton() {
+ findObject(By.res(packageName, START_MEDIA_PROJECTION_NEW_INTENT_BUTTON_ID)).also { it.click() }
+ }
+
private fun chooseEntireScreenOption() {
findObject(By.res(SCREEN_SHARE_OPTIONS_PATTERN)).also { it.click() }
@@ -92,6 +127,13 @@
findObject(By.text(targetAppName)).also { it.click() }
}
+ private fun selectTargetAppRecent(recentTasksIndex: Int) {
+ // Scroll to to find target app to launch then click app icon it to start capture
+ val recentsTasksRecycler =
+ findObject(By.res(SYSTEMUI_PACKAGE, MEDIA_PROJECTION_RECENT_TASKS))
+ recentsTasksRecycler.children[recentTasksIndex].also{ it.click() }
+ }
+
private fun chooseSingleAppOption() {
findObject(By.res(SCREEN_SHARE_OPTIONS_PATTERN)).also { it.click() }
@@ -116,8 +158,10 @@
const val TIMEOUT: Long = 5000L
const val ACCEPT_RESOURCE_ID: String = "android:id/button1"
const val START_MEDIA_PROJECTION_BUTTON_ID: String = "button_start_mp"
+ const val START_MEDIA_PROJECTION_NEW_INTENT_BUTTON_ID: String = "button_start_mp_new_intent"
val SCREEN_SHARE_OPTIONS_PATTERN: Pattern =
Pattern.compile("$SYSTEMUI_PACKAGE:id/screen_share_mode_(options|spinner)")
+ const val MEDIA_PROJECTION_RECENT_TASKS: String = "media_projection_recent_tasks_recycler"
const val ENTIRE_SCREEN_STRING_RES_NAME: String =
"screen_share_permission_dialog_option_entire_screen"
const val SINGLE_APP_STRING_RES_NAME: String =
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_start_media_projection.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_start_media_projection.xml
index 46f01e6..c34d200 100644
--- a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_start_media_projection.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_start_media_projection.xml
@@ -16,17 +16,27 @@
-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:gravity="center"
android:orientation="vertical"
android:background="@android:color/holo_orange_light">
<Button
android:id="@+id/button_start_mp"
- android:layout_width="500dp"
- android:layout_height="500dp"
+ android:layout_margin="16dp"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
android:gravity="center_vertical|center_horizontal"
android:text="Start Media Projection"
android:textAppearance="?android:attr/textAppearanceLarge"/>
+ <Button
+ android:id="@+id/button_start_mp_new_intent"
+ android:layout_margin="16dp"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="center_vertical|center_horizontal"
+ android:text="Start Media Projection with extra intent"
+ android:textAppearance="?android:attr/textAppearanceLarge"/>
</LinearLayout>
\ No newline at end of file
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/StartMediaProjectionActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/StartMediaProjectionActivity.java
index a24a482..b29b874 100644
--- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/StartMediaProjectionActivity.java
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/StartMediaProjectionActivity.java
@@ -19,7 +19,8 @@
import static com.android.wm.shell.flicker.utils.MediaProjectionUtils.EXTRA_MESSENGER;
import static com.android.wm.shell.flicker.utils.MediaProjectionUtils.MSG_SERVICE_DESTROYED;
import static com.android.wm.shell.flicker.utils.MediaProjectionUtils.MSG_START_FOREGROUND_DONE;
-import static com.android.wm.shell.flicker.utils.MediaProjectionUtils.REQUEST_CODE;
+import static com.android.wm.shell.flicker.utils.MediaProjectionUtils.REQUEST_CODE_NORMAL;
+import static com.android.wm.shell.flicker.utils.MediaProjectionUtils.REQUEST_CODE_EXTRA_INTENT;
import android.app.Activity;
import android.content.ComponentName;
@@ -71,13 +72,17 @@
setContentView(R.layout.activity_start_media_projection);
Button startMediaProjectionButton = findViewById(R.id.button_start_mp);
+ Button startMediaProjectionButton2 = findViewById(R.id.button_start_mp_new_intent);
startMediaProjectionButton.setOnClickListener(v ->
- startActivityForResult(mService.createScreenCaptureIntent(), REQUEST_CODE));
+ startActivityForResult(mService.createScreenCaptureIntent(), REQUEST_CODE_NORMAL));
+ startMediaProjectionButton2.setOnClickListener(v ->
+ startActivityForResult(mService.createScreenCaptureIntent(),
+ REQUEST_CODE_EXTRA_INTENT));
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- if (requestCode != REQUEST_CODE) {
+ if (requestCode != REQUEST_CODE_NORMAL && requestCode != REQUEST_CODE_EXTRA_INTENT) {
throw new IllegalStateException("Unknown request code: " + requestCode);
}
if (resultCode != RESULT_OK) {
@@ -85,6 +90,11 @@
}
Log.d(TAG, "onActivityResult");
startMediaProjectionService(resultCode, data);
+ if (requestCode == REQUEST_CODE_EXTRA_INTENT) {
+ Intent startMain = new Intent(Intent.ACTION_MAIN);
+ startMain.addCategory(Intent.CATEGORY_HOME);
+ startActivity(startMain);
+ }
}
private void startMediaProjectionService(int resultCode, Intent resultData) {
@@ -122,7 +132,7 @@
displayBounds.width(), displayBounds.height(), PixelFormat.RGBA_8888, 1);
mVirtualDisplay = mMediaProjection.createVirtualDisplay(
- "DanielDisplay",
+ "TestDisplay",
displayBounds.width(),
displayBounds.height(),
DisplayMetrics.DENSITY_HIGH,
diff --git a/tests/Input/res/xml/bookmarks.xml b/tests/Input/res/xml/bookmarks.xml
index ba3f187..a4c898d 100644
--- a/tests/Input/res/xml/bookmarks.xml
+++ b/tests/Input/res/xml/bookmarks.xml
@@ -14,47 +14,55 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<bookmarks>
+<bookmarks xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
<!-- the key combinations for the following shortcuts must be in sync
with the key combinations sent by the test in KeyGestureControllerTests.java -->
<bookmark
role="android.app.role.BROWSER"
- shortcut="b" />
+ androidprv:keycode="KEYCODE_B"
+ androidprv:modifierState="META" />
<bookmark
category="android.intent.category.APP_CONTACTS"
- shortcut="c" />
+ androidprv:keycode="KEYCODE_C"
+ androidprv:modifierState="META" />
<bookmark
category="android.intent.category.APP_EMAIL"
- shortcut="e" />
+ androidprv:keycode="KEYCODE_E"
+ androidprv:modifierState="META" />
<bookmark
category="android.intent.category.APP_CALENDAR"
- shortcut="k" />
+ androidprv:keycode="KEYCODE_K"
+ androidprv:modifierState="META" />
<bookmark
category="android.intent.category.APP_MAPS"
- shortcut="m" />
+ androidprv:keycode="KEYCODE_M"
+ androidprv:modifierState="META" />
<bookmark
category="android.intent.category.APP_MUSIC"
- shortcut="p" />
+ androidprv:keycode="KEYCODE_P"
+ androidprv:modifierState="META" />
<bookmark
role="android.app.role.SMS"
- shortcut="s" />
+ androidprv:keycode="KEYCODE_S"
+ androidprv:modifierState="META" />
<bookmark
category="android.intent.category.APP_CALCULATOR"
- shortcut="u" />
+ androidprv:keycode="KEYCODE_U"
+ androidprv:modifierState="META" />
<bookmark
role="android.app.role.BROWSER"
- shortcut="b"
- shift="true" />
+ androidprv:keycode="KEYCODE_B"
+ androidprv:modifierState="META|SHIFT" />
<bookmark
category="android.intent.category.APP_CONTACTS"
- shortcut="c"
+ androidprv:keycode="KEYCODE_C"
shift="true" />
<bookmark
package="com.test"
class="com.test.BookmarkTest"
- shortcut="j"
+ androidprv:keycode="KEYCODE_J"
shift="true" />
</bookmarks>
\ No newline at end of file
diff --git a/tests/Input/res/xml/bookmarks_legacy.xml b/tests/Input/res/xml/bookmarks_legacy.xml
new file mode 100644
index 0000000..8bacf49
--- /dev/null
+++ b/tests/Input/res/xml/bookmarks_legacy.xml
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright 2024 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<bookmarks>
+ <!-- The key combinations for the following shortcuts are legacy way defining bookmarks and we
+ should prefer new way of defining bookmarks as shown in {@link bookmarks.xml} -->
+ <bookmark
+ role="android.app.role.BROWSER"
+ shortcut="b" />
+ <bookmark
+ category="android.intent.category.APP_CONTACTS"
+ shortcut="c" />
+ <bookmark
+ category="android.intent.category.APP_EMAIL"
+ shortcut="e" />
+ <bookmark
+ category="android.intent.category.APP_CALENDAR"
+ shortcut="k" />
+ <bookmark
+ category="android.intent.category.APP_MAPS"
+ shortcut="m" />
+ <bookmark
+ category="android.intent.category.APP_MUSIC"
+ shortcut="p" />
+ <bookmark
+ role="android.app.role.SMS"
+ shortcut="s" />
+ <bookmark
+ category="android.intent.category.APP_CALCULATOR"
+ shortcut="u" />
+
+ <bookmark
+ role="android.app.role.BROWSER"
+ shortcut="b"
+ shift="true" />
+
+ <bookmark
+ category="android.intent.category.APP_CONTACTS"
+ shortcut="c"
+ shift="true" />
+
+ <bookmark
+ package="com.test"
+ class="com.test.BookmarkTest"
+ shortcut="j"
+ shift="true" />
+</bookmarks>
\ No newline at end of file
diff --git a/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt b/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt
index 09a686c..34350ab 100644
--- a/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt
+++ b/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt
@@ -115,13 +115,11 @@
private lateinit var iInputManager: IInputManager
@Mock
- private lateinit var resources: Resources
-
- @Mock
private lateinit var packageManager: PackageManager
private var currentPid = 0
private lateinit var context: Context
+ private lateinit var resources: Resources
private lateinit var keyGestureController: KeyGestureController
private lateinit var inputManagerGlobalSession: InputManagerGlobal.TestSession
private lateinit var testLooper: TestLooper
@@ -130,6 +128,7 @@
@Before
fun setup() {
context = Mockito.spy(ContextWrapper(ApplicationProvider.getApplicationContext()))
+ resources = Mockito.spy(context.resources)
setupInputDevices()
setupBehaviors()
testLooper = TestLooper()
@@ -146,10 +145,6 @@
private fun setupBehaviors() {
Mockito.`when`(SystemProperties.get("ro.debuggable")).thenReturn("1")
Mockito.`when`(resources.getBoolean(R.bool.config_enableScreenshotChord)).thenReturn(true)
- val testBookmarks: XmlResourceParser = context.resources.getXml(
- com.android.test.input.R.xml.bookmarks
- )
- Mockito.`when`(resources.getXml(R.xml.bookmarks)).thenReturn(testBookmarks)
Mockito.`when`(context.resources).thenReturn(resources)
Mockito.`when`(packageManager.hasSystemFeature(PackageManager.FEATURE_WATCH))
.thenReturn(true)
@@ -158,6 +153,11 @@
Mockito.`when`(context.packageManager).thenReturn(packageManager)
}
+ private fun setupBookmarks(bookmarkRes: Int) {
+ val testBookmarks: XmlResourceParser = context.resources.getXml(bookmarkRes)
+ Mockito.`when`(resources.getXml(R.xml.bookmarks)).thenReturn(testBookmarks)
+ }
+
private fun setupInputDevices() {
val correctIm = context.getSystemService(InputManager::class.java)!!
val virtualDevice = correctIm.getInputDevice(KeyCharacterMap.VIRTUAL_KEYBOARD)!!
@@ -604,45 +604,6 @@
AppLaunchData.createLaunchDataForCategory(Intent.CATEGORY_APP_CALCULATOR)
),
TestData(
- "META + SHIFT + B -> Launch Default Browser",
- intArrayOf(
- KeyEvent.KEYCODE_META_LEFT,
- KeyEvent.KEYCODE_SHIFT_LEFT,
- KeyEvent.KEYCODE_B
- ),
- KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION,
- intArrayOf(KeyEvent.KEYCODE_B),
- KeyEvent.META_META_ON or KeyEvent.META_SHIFT_ON,
- intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
- AppLaunchData.createLaunchDataForRole(RoleManager.ROLE_BROWSER)
- ),
- TestData(
- "META + SHIFT + C -> Launch Default Contacts",
- intArrayOf(
- KeyEvent.KEYCODE_META_LEFT,
- KeyEvent.KEYCODE_SHIFT_LEFT,
- KeyEvent.KEYCODE_C
- ),
- KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION,
- intArrayOf(KeyEvent.KEYCODE_C),
- KeyEvent.META_META_ON or KeyEvent.META_SHIFT_ON,
- intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
- AppLaunchData.createLaunchDataForCategory(Intent.CATEGORY_APP_CONTACTS)
- ),
- TestData(
- "META + SHIFT + J -> Launch Target Activity",
- intArrayOf(
- KeyEvent.KEYCODE_META_LEFT,
- KeyEvent.KEYCODE_SHIFT_LEFT,
- KeyEvent.KEYCODE_J
- ),
- KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION,
- intArrayOf(KeyEvent.KEYCODE_J),
- KeyEvent.META_META_ON or KeyEvent.META_SHIFT_ON,
- intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
- AppLaunchData.createLaunchDataForComponent("com.test", "com.test.BookmarkTest")
- ),
- TestData(
"META + CTRL + DEL -> Trigger Bug Report",
intArrayOf(
KeyEvent.KEYCODE_META_LEFT,
@@ -715,47 +676,47 @@
intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
),
TestData(
- "ALT + [ -> Resizes a task to fit the left half of the screen",
+ "META + [ -> Resizes a task to fit the left half of the screen",
intArrayOf(
- KeyEvent.KEYCODE_ALT_LEFT,
+ KeyEvent.KEYCODE_META_LEFT,
KeyEvent.KEYCODE_LEFT_BRACKET
),
KeyGestureEvent.KEY_GESTURE_TYPE_SNAP_LEFT_FREEFORM_WINDOW,
intArrayOf(KeyEvent.KEYCODE_LEFT_BRACKET),
- KeyEvent.META_ALT_ON,
+ KeyEvent.META_META_ON,
intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
),
TestData(
- "ALT + ] -> Resizes a task to fit the right half of the screen",
+ "META + ] -> Resizes a task to fit the right half of the screen",
intArrayOf(
- KeyEvent.KEYCODE_ALT_LEFT,
+ KeyEvent.KEYCODE_META_LEFT,
KeyEvent.KEYCODE_RIGHT_BRACKET
),
KeyGestureEvent.KEY_GESTURE_TYPE_SNAP_RIGHT_FREEFORM_WINDOW,
intArrayOf(KeyEvent.KEYCODE_RIGHT_BRACKET),
- KeyEvent.META_ALT_ON,
+ KeyEvent.META_META_ON,
intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
),
TestData(
- "ALT + '=' -> Maximizes a task to fit the screen",
+ "META + '=' -> Toggles maximization of a task to maximized and restore its bounds",
intArrayOf(
- KeyEvent.KEYCODE_ALT_LEFT,
+ KeyEvent.KEYCODE_META_LEFT,
KeyEvent.KEYCODE_EQUALS
),
- KeyGestureEvent.KEY_GESTURE_TYPE_MAXIMIZE_FREEFORM_WINDOW,
+ KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_MAXIMIZE_FREEFORM_WINDOW,
intArrayOf(KeyEvent.KEYCODE_EQUALS),
- KeyEvent.META_ALT_ON,
+ KeyEvent.META_META_ON,
intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
),
TestData(
- "ALT + '-' -> Restores a task size to its previous bounds",
+ "META + '-' -> Minimizes a freeform task",
intArrayOf(
- KeyEvent.KEYCODE_ALT_LEFT,
+ KeyEvent.KEYCODE_META_LEFT,
KeyEvent.KEYCODE_MINUS
),
- KeyGestureEvent.KEY_GESTURE_TYPE_RESTORE_FREEFORM_WINDOW_SIZE,
+ KeyGestureEvent.KEY_GESTURE_TYPE_MINIMIZE_FREEFORM_WINDOW,
intArrayOf(KeyEvent.KEYCODE_MINUS),
- KeyEvent.META_ALT_ON,
+ KeyEvent.META_META_ON,
intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
),
TestData(
@@ -866,6 +827,139 @@
}
@Keep
+ private fun bookmarkArguments(): Array<TestData> {
+ return arrayOf(
+ TestData(
+ "META + B -> Launch Default Browser",
+ intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_B),
+ KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION,
+ intArrayOf(KeyEvent.KEYCODE_B),
+ KeyEvent.META_META_ON,
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
+ AppLaunchData.createLaunchDataForRole(RoleManager.ROLE_BROWSER)
+ ),
+ TestData(
+ "META + C -> Launch Default Contacts",
+ intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_C),
+ KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION,
+ intArrayOf(KeyEvent.KEYCODE_C),
+ KeyEvent.META_META_ON,
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
+ AppLaunchData.createLaunchDataForCategory(Intent.CATEGORY_APP_CONTACTS)
+ ),
+ TestData(
+ "META + E -> Launch Default Email",
+ intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_E),
+ KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION,
+ intArrayOf(KeyEvent.KEYCODE_E),
+ KeyEvent.META_META_ON,
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
+ AppLaunchData.createLaunchDataForCategory(Intent.CATEGORY_APP_EMAIL)
+ ),
+ TestData(
+ "META + K -> Launch Default Calendar",
+ intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_K),
+ KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION,
+ intArrayOf(KeyEvent.KEYCODE_K),
+ KeyEvent.META_META_ON,
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
+ AppLaunchData.createLaunchDataForCategory(Intent.CATEGORY_APP_CALENDAR)
+ ),
+ TestData(
+ "META + M -> Launch Default Maps",
+ intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_M),
+ KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION,
+ intArrayOf(KeyEvent.KEYCODE_M),
+ KeyEvent.META_META_ON,
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
+ AppLaunchData.createLaunchDataForCategory(Intent.CATEGORY_APP_MAPS)
+ ),
+ TestData(
+ "META + P -> Launch Default Music",
+ intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_P),
+ KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION,
+ intArrayOf(KeyEvent.KEYCODE_P),
+ KeyEvent.META_META_ON,
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
+ AppLaunchData.createLaunchDataForCategory(Intent.CATEGORY_APP_MUSIC)
+ ),
+ TestData(
+ "META + S -> Launch Default SMS",
+ intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_S),
+ KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION,
+ intArrayOf(KeyEvent.KEYCODE_S),
+ KeyEvent.META_META_ON,
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
+ AppLaunchData.createLaunchDataForRole(RoleManager.ROLE_SMS)
+ ),
+ TestData(
+ "META + U -> Launch Default Calculator",
+ intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_U),
+ KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION,
+ intArrayOf(KeyEvent.KEYCODE_U),
+ KeyEvent.META_META_ON,
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
+ AppLaunchData.createLaunchDataForCategory(Intent.CATEGORY_APP_CALCULATOR)
+ ),
+ TestData(
+ "META + SHIFT + B -> Launch Default Browser",
+ intArrayOf(
+ KeyEvent.KEYCODE_META_LEFT,
+ KeyEvent.KEYCODE_SHIFT_LEFT,
+ KeyEvent.KEYCODE_B
+ ),
+ KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION,
+ intArrayOf(KeyEvent.KEYCODE_B),
+ KeyEvent.META_META_ON or KeyEvent.META_SHIFT_ON,
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
+ AppLaunchData.createLaunchDataForRole(RoleManager.ROLE_BROWSER)
+ ),
+ TestData(
+ "META + SHIFT + C -> Launch Default Contacts",
+ intArrayOf(
+ KeyEvent.KEYCODE_META_LEFT,
+ KeyEvent.KEYCODE_SHIFT_LEFT,
+ KeyEvent.KEYCODE_C
+ ),
+ KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION,
+ intArrayOf(KeyEvent.KEYCODE_C),
+ KeyEvent.META_META_ON or KeyEvent.META_SHIFT_ON,
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
+ AppLaunchData.createLaunchDataForCategory(Intent.CATEGORY_APP_CONTACTS)
+ ),
+ TestData(
+ "META + SHIFT + J -> Launch Target Activity",
+ intArrayOf(
+ KeyEvent.KEYCODE_META_LEFT,
+ KeyEvent.KEYCODE_SHIFT_LEFT,
+ KeyEvent.KEYCODE_J
+ ),
+ KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION,
+ intArrayOf(KeyEvent.KEYCODE_J),
+ KeyEvent.META_META_ON or KeyEvent.META_SHIFT_ON,
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
+ AppLaunchData.createLaunchDataForComponent("com.test", "com.test.BookmarkTest")
+ )
+ )
+ }
+
+ @Test
+ @Parameters(method = "bookmarkArguments")
+ fun testBookmarks(test: TestData) {
+ setupBookmarks(com.android.test.input.R.xml.bookmarks)
+ setupKeyGestureController()
+ testKeyGestureInternal(test)
+ }
+
+ @Test
+ @Parameters(method = "bookmarkArguments")
+ fun testBookmarksLegacy(test: TestData) {
+ setupBookmarks(com.android.test.input.R.xml.bookmarks_legacy)
+ setupKeyGestureController()
+ testKeyGestureInternal(test)
+ }
+
+ @Keep
private fun systemKeysTestArguments(): Array<TestData> {
return arrayOf(
TestData(
diff --git a/tests/PackageWatchdog/Android.bp b/tests/PackageWatchdog/Android.bp
index 91483eb..8be74eacc 100644
--- a/tests/PackageWatchdog/Android.bp
+++ b/tests/PackageWatchdog/Android.bp
@@ -37,7 +37,7 @@
"truth",
] + select(soong_config_variable("ANDROID", "release_crashrecovery_module"), {
"true": [
- "service-crashrecovery.impl",
+ "service-crashrecovery-pre-jarjar",
"framework-crashrecovery.impl",
],
default: [],
diff --git a/tests/Tracing/src/com/android/internal/protolog/ProcessedPerfettoProtoLogImplTest.java b/tests/Tracing/src/com/android/internal/protolog/ProcessedPerfettoProtoLogImplTest.java
index 2692e12..44641f7 100644
--- a/tests/Tracing/src/com/android/internal/protolog/ProcessedPerfettoProtoLogImplTest.java
+++ b/tests/Tracing/src/com/android/internal/protolog/ProcessedPerfettoProtoLogImplTest.java
@@ -24,6 +24,7 @@
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.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -57,6 +58,7 @@
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import org.mockito.Mockito;
+import org.mockito.stubbing.Answer;
import perfetto.protos.Protolog;
import perfetto.protos.ProtologCommon;
@@ -858,6 +860,39 @@
.isEqualTo("This message should also be logged 567");
}
+ @Test
+ public void enablesLogGroupAfterLoadingConfig() {
+ sProtoLog.stopLoggingToLogcat(
+ new String[] { TestProtoLogGroup.TEST_GROUP.name() }, (msg) -> {});
+ Truth.assertThat(TestProtoLogGroup.TEST_GROUP.isLogToLogcat()).isFalse();
+
+ doAnswer((Answer<Void>) invocation -> {
+ // logToLogcat is still false before we laod the viewer config
+ Truth.assertThat(TestProtoLogGroup.TEST_GROUP.isLogToLogcat()).isFalse();
+ return null;
+ }).when(sReader).unloadViewerConfig(any(), any());
+
+ sProtoLog.startLoggingToLogcat(
+ new String[] { TestProtoLogGroup.TEST_GROUP.name() }, (msg) -> {});
+ Truth.assertThat(TestProtoLogGroup.TEST_GROUP.isLogToLogcat()).isTrue();
+ }
+
+ @Test
+ public void disablesLogGroupBeforeUnloadingConfig() {
+ sProtoLog.startLoggingToLogcat(
+ new String[] { TestProtoLogGroup.TEST_GROUP.name() }, (msg) -> {});
+ Truth.assertThat(TestProtoLogGroup.TEST_GROUP.isLogToLogcat()).isTrue();
+
+ doAnswer((Answer<Void>) invocation -> {
+ // Already set logToLogcat to false by the time we unload the config
+ Truth.assertThat(TestProtoLogGroup.TEST_GROUP.isLogToLogcat()).isFalse();
+ return null;
+ }).when(sReader).unloadViewerConfig(any(), any());
+ sProtoLog.stopLoggingToLogcat(
+ new String[] { TestProtoLogGroup.TEST_GROUP.name() }, (msg) -> {});
+ Truth.assertThat(TestProtoLogGroup.TEST_GROUP.isLogToLogcat()).isFalse();
+ }
+
private enum TestProtoLogGroup implements IProtoLogGroup {
TEST_GROUP(true, true, false, "TEST_TAG");
diff --git a/tools/aapt2/link/TableMerger.cpp b/tools/aapt2/link/TableMerger.cpp
index 1bef5f8..1d4adc4 100644
--- a/tools/aapt2/link/TableMerger.cpp
+++ b/tools/aapt2/link/TableMerger.cpp
@@ -207,14 +207,13 @@
Value* dst_value = dst_config_value->value.get();
Value* src_value = src_config_value->value.get();
- CollisionResult collision_result;
- if (overlay) {
- collision_result =
- ResolveMergeCollision(override_styles_instead_of_overlaying, dst_value, src_value, pool);
- } else {
- collision_result =
- ResourceTable::ResolveFlagCollision(dst_value->GetFlagStatus(), src_value->GetFlagStatus());
- if (collision_result == CollisionResult::kConflict) {
+ CollisionResult collision_result =
+ ResourceTable::ResolveFlagCollision(dst_value->GetFlagStatus(), src_value->GetFlagStatus());
+ if (collision_result == CollisionResult::kConflict) {
+ if (overlay) {
+ collision_result =
+ ResolveMergeCollision(override_styles_instead_of_overlaying, dst_value, src_value, pool);
+ } else {
collision_result = ResourceTable::ResolveValueCollision(dst_value, src_value);
}
}
diff --git a/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManager.java b/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManager.java
index f68ae2c..74a907f 100644
--- a/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManager.java
+++ b/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManager.java
@@ -309,7 +309,7 @@
*/
@TestApi
@NonNull
- @FlaggedApi("com.android.wifi.flags.shared_connectivity_broadcast_receiver_test_api")
+ @SuppressLint("UnflaggedApi") // Exempt: Test API for already shipped feature
public BroadcastReceiver getBroadcastReceiver() {
return mBroadcastReceiver;
}