summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/api/current.txt2
-rw-r--r--core/java/android/content/pm/multiuser.aconfig10
-rw-r--r--core/java/android/hardware/input/input_framework.aconfig10
-rw-r--r--core/java/android/os/BaseBundle.java11
-rw-r--r--core/java/android/os/Bundle.java21
-rw-r--r--core/java/android/os/Parcel.java58
-rw-r--r--core/java/android/provider/Settings.java10
-rw-r--r--core/java/android/text/style/TtsSpan.java22
-rw-r--r--core/java/android/view/InsetsSourceControl.java2
-rw-r--r--core/java/android/window/DesktopModeFlags.java98
-rw-r--r--core/java/android/window/flags/lse_desktop_experience.aconfig9
-rw-r--r--core/res/res/values/public-staging.xml8
-rw-r--r--core/tests/coretests/src/android/window/DesktopModeFlagsTest.java284
-rw-r--r--libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatus.java9
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java6
-rw-r--r--media/java/android/media/flags/projection.aconfig10
-rw-r--r--media/java/android/media/projection/IMediaProjectionWatcherCallback.aidl16
-rw-r--r--media/java/android/media/projection/MediaProjectionEvent.aidl3
-rw-r--r--media/java/android/media/projection/MediaProjectionEvent.java103
-rw-r--r--media/java/android/media/projection/MediaProjectionManager.java21
-rw-r--r--media/java/android/media/quality/MediaQualityManager.java11
-rw-r--r--packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java1
-rw-r--r--packages/SystemUI/accessibility/accessibilitymenu/aconfig/Android.bp2
-rw-r--r--packages/SystemUI/accessibility/accessibilitymenu/aconfig/accessibility.aconfig2
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ResponsiveLazyHorizontalGrid.kt3
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardFingerprintListenModel.kt4
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java32
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java56
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiControlService.java4
-rw-r--r--services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java102
-rw-r--r--services/core/java/com/android/server/media/projection/MediaProjectionStopController.java5
-rw-r--r--services/core/java/com/android/server/pm/UserManagerService.java11
-rw-r--r--services/core/java/com/android/server/policy/SingleKeyGestureDetector.java15
-rw-r--r--services/core/java/com/android/server/wm/ScreenRecordingCallbackController.java7
-rw-r--r--services/core/java/com/android/server/wm/WallpaperWindowToken.java5
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java20
-rw-r--r--services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionStopControllerTest.java16
-rw-r--r--services/tests/wmtests/src/com/android/server/policy/SingleKeyGestureTests.java46
-rw-r--r--telephony/java/android/telephony/CarrierConfigManager.java1
39 files changed, 797 insertions, 259 deletions
diff --git a/core/api/current.txt b/core/api/current.txt
index e0fc9590f9f7..6964866db7f3 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -45145,7 +45145,7 @@ package android.telephony {
field public static final String KEY_SHOW_ICCID_IN_SIM_STATUS_BOOL = "show_iccid_in_sim_status_bool";
field public static final String KEY_SHOW_IMS_REGISTRATION_STATUS_BOOL = "show_ims_registration_status_bool";
field public static final String KEY_SHOW_ONSCREEN_DIAL_BUTTON_BOOL = "show_onscreen_dial_button_bool";
- field @FlaggedApi("com.android.internal.telephony.flags.hide_roaming_icon") public static final String KEY_SHOW_ROAMING_INDICATOR_BOOL = "show_roaming_indicator_bool";
+ field public static final String KEY_SHOW_ROAMING_INDICATOR_BOOL = "show_roaming_indicator_bool";
field public static final String KEY_SHOW_SIGNAL_STRENGTH_IN_SIM_STATUS_BOOL = "show_signal_strength_in_sim_status_bool";
field public static final String KEY_SHOW_VIDEO_CALL_CHARGES_ALERT_DIALOG_BOOL = "show_video_call_charges_alert_dialog_bool";
field public static final String KEY_SHOW_WFC_LOCATION_PRIVACY_POLICY_BOOL = "show_wfc_location_privacy_policy_bool";
diff --git a/core/java/android/content/pm/multiuser.aconfig b/core/java/android/content/pm/multiuser.aconfig
index 4a579a4c0e85..4e6fb8d3a8e7 100644
--- a/core/java/android/content/pm/multiuser.aconfig
+++ b/core/java/android/content/pm/multiuser.aconfig
@@ -500,6 +500,16 @@ flag {
}
flag {
+ name: "get_user_switchability_permission"
+ namespace: "multiuser"
+ description: "Update permissions for getUserSwitchability"
+ bug: "390458180"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "restrict_quiet_mode_credential_bug_fix_to_managed_profiles"
namespace: "profile_experiences"
description: "Use user states to check the state of quiet mode for managed profiles only"
diff --git a/core/java/android/hardware/input/input_framework.aconfig b/core/java/android/hardware/input/input_framework.aconfig
index 62126963cba4..79323bf2f2f7 100644
--- a/core/java/android/hardware/input/input_framework.aconfig
+++ b/core/java/android/hardware/input/input_framework.aconfig
@@ -225,3 +225,13 @@ flag {
description: "Removes modifiers from the original key event that activated the fallback, ensuring that only the intended fallback event is sent."
bug: "382545048"
}
+
+flag {
+ name: "abort_slow_multi_press"
+ namespace: "wear_frameworks"
+ description: "If a press that's a part of a multipress takes too long, the multipress gesture will be cancelled."
+ bug: "370095426"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/core/java/android/os/BaseBundle.java b/core/java/android/os/BaseBundle.java
index 1041041b2a27..1cf293d46350 100644
--- a/core/java/android/os/BaseBundle.java
+++ b/core/java/android/os/BaseBundle.java
@@ -45,8 +45,7 @@ import java.util.function.BiFunction;
* {@link PersistableBundle} subclass.
*/
@android.ravenwood.annotation.RavenwoodKeepWholeClass
-@SuppressWarnings("HiddenSuperclass")
-public class BaseBundle implements Parcel.ClassLoaderProvider {
+public class BaseBundle {
/** @hide */
protected static final String TAG = "Bundle";
static final boolean DEBUG = false;
@@ -300,9 +299,8 @@ public class BaseBundle implements Parcel.ClassLoaderProvider {
/**
* Return the ClassLoader currently associated with this Bundle.
- * @hide
*/
- public ClassLoader getClassLoader() {
+ ClassLoader getClassLoader() {
return mClassLoader;
}
@@ -416,9 +414,6 @@ public class BaseBundle implements Parcel.ClassLoaderProvider {
if ((mFlags & Bundle.FLAG_VERIFY_TOKENS_PRESENT) != 0) {
Intent.maybeMarkAsMissingCreatorToken(object);
}
- } else if (object instanceof Bundle) {
- Bundle bundle = (Bundle) object;
- bundle.setClassLoaderSameAsContainerBundleWhenRetrievedFirstTime(this);
}
return (clazz != null) ? clazz.cast(object) : (T) object;
}
@@ -492,7 +487,7 @@ public class BaseBundle implements Parcel.ClassLoaderProvider {
int[] numLazyValues = new int[]{0};
try {
parcelledData.readArrayMap(map, count, !parcelledByNative,
- /* lazy */ ownsParcel, this, numLazyValues);
+ /* lazy */ ownsParcel, mClassLoader, numLazyValues);
} catch (BadParcelableException e) {
if (sShouldDefuse) {
Log.w(TAG, "Failed to parse Bundle, but defusing quietly", e);
diff --git a/core/java/android/os/Bundle.java b/core/java/android/os/Bundle.java
index 55bfd451d97a..819d58d9f059 100644
--- a/core/java/android/os/Bundle.java
+++ b/core/java/android/os/Bundle.java
@@ -141,8 +141,6 @@ public final class Bundle extends BaseBundle implements Cloneable, Parcelable {
STRIPPED.putInt("STRIPPED", 1);
}
- private boolean isFirstRetrievedFromABundle = false;
-
/**
* Constructs a new, empty Bundle.
*/
@@ -1022,9 +1020,7 @@ public final class Bundle extends BaseBundle implements Cloneable, Parcelable {
return null;
}
try {
- Bundle bundle = (Bundle) o;
- bundle.setClassLoaderSameAsContainerBundleWhenRetrievedFirstTime(this);
- return bundle;
+ return (Bundle) o;
} catch (ClassCastException e) {
typeWarning(key, o, "Bundle", e);
return null;
@@ -1032,21 +1028,6 @@ public final class Bundle extends BaseBundle implements Cloneable, Parcelable {
}
/**
- * Set the ClassLoader of a bundle to its container bundle. This is necessary so that when a
- * bundle's ClassLoader is changed, it can be propagated to its children. Do this only when it
- * is retrieved from the container bundle first time though. Once it is accessed outside of its
- * container, its ClassLoader should no longer be changed by its container anymore.
- *
- * @param containerBundle the bundle this bundle is retrieved from.
- */
- void setClassLoaderSameAsContainerBundleWhenRetrievedFirstTime(BaseBundle containerBundle) {
- if (!isFirstRetrievedFromABundle) {
- setClassLoader(containerBundle.getClassLoader());
- isFirstRetrievedFromABundle = true;
- }
- }
-
- /**
* Returns the value associated with the given key, or {@code null} if
* no mapping of the desired type exists for the given key or a {@code null}
* value is explicitly associated with the key.
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 3c4139d39762..e58934746c14 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -4661,7 +4661,7 @@ public final class Parcel {
* @hide
*/
@Nullable
- private Object readLazyValue(@Nullable ClassLoaderProvider loaderProvider) {
+ public Object readLazyValue(@Nullable ClassLoader loader) {
int start = dataPosition();
int type = readInt();
if (isLengthPrefixed(type)) {
@@ -4672,17 +4672,12 @@ public final class Parcel {
int end = MathUtils.addOrThrow(dataPosition(), objectLength);
int valueLength = end - start;
setDataPosition(end);
- return new LazyValue(this, start, valueLength, type, loaderProvider);
+ return new LazyValue(this, start, valueLength, type, loader);
} else {
- return readValue(type, getClassLoader(loaderProvider), /* clazz */ null);
+ return readValue(type, loader, /* clazz */ null);
}
}
- @Nullable
- private static ClassLoader getClassLoader(@Nullable ClassLoaderProvider loaderProvider) {
- return loaderProvider == null ? null : loaderProvider.getClassLoader();
- }
-
private static final class LazyValue implements BiFunction<Class<?>, Class<?>[], Object> {
/**
@@ -4696,12 +4691,7 @@ public final class Parcel {
private final int mPosition;
private final int mLength;
private final int mType;
- // this member is set when a bundle that includes a LazyValue is unparceled. But it is used
- // when apply method is called. Between these 2 events, the bundle's ClassLoader could have
- // changed. Let the bundle be a ClassLoaderProvider allows the bundle provides its current
- // ClassLoader at the time apply method is called.
- @NonNull
- private final ClassLoaderProvider mLoaderProvider;
+ @Nullable private final ClassLoader mLoader;
@Nullable private Object mObject;
/**
@@ -4712,13 +4702,12 @@ public final class Parcel {
*/
@Nullable private volatile Parcel mSource;
- LazyValue(Parcel source, int position, int length, int type,
- @NonNull ClassLoaderProvider loaderProvider) {
+ LazyValue(Parcel source, int position, int length, int type, @Nullable ClassLoader loader) {
mSource = requireNonNull(source);
mPosition = position;
mLength = length;
mType = type;
- mLoaderProvider = loaderProvider;
+ mLoader = loader;
}
@Override
@@ -4731,8 +4720,7 @@ public final class Parcel {
int restore = source.dataPosition();
try {
source.setDataPosition(mPosition);
- mObject = source.readValue(mLoaderProvider.getClassLoader(), clazz,
- itemTypes);
+ mObject = source.readValue(mLoader, clazz, itemTypes);
} finally {
source.setDataPosition(restore);
}
@@ -4805,8 +4793,7 @@ public final class Parcel {
return Objects.equals(mObject, value.mObject);
}
// Better safely fail here since this could mean we get different objects.
- if (!Objects.equals(mLoaderProvider.getClassLoader(),
- value.mLoaderProvider.getClassLoader())) {
+ if (!Objects.equals(mLoader, value.mLoader)) {
return false;
}
// Otherwise compare metadata prior to comparing payload.
@@ -4820,24 +4807,10 @@ public final class Parcel {
@Override
public int hashCode() {
// Accessing mSource first to provide memory barrier for mObject
- return Objects.hash(mSource == null, mObject, mLoaderProvider.getClassLoader(), mType,
- mLength);
+ return Objects.hash(mSource == null, mObject, mLoader, mType, mLength);
}
}
- /**
- * Provides a ClassLoader.
- * @hide
- */
- public interface ClassLoaderProvider {
- /**
- * Returns a ClassLoader.
- *
- * @return ClassLoader
- */
- ClassLoader getClassLoader();
- }
-
/** Same as {@link #readValue(ClassLoader, Class, Class[])} without any item types. */
private <T> T readValue(int type, @Nullable ClassLoader loader, @Nullable Class<T> clazz) {
// Avoids allocating Class[0] array
@@ -5578,8 +5551,8 @@ public final class Parcel {
}
private void readArrayMapInternal(@NonNull ArrayMap<? super String, Object> outVal,
- int size, @Nullable ClassLoaderProvider loaderProvider) {
- readArrayMap(outVal, size, /* sorted */ true, /* lazy */ false, loaderProvider, null);
+ int size, @Nullable ClassLoader loader) {
+ readArrayMap(outVal, size, /* sorted */ true, /* lazy */ false, loader, null);
}
/**
@@ -5593,12 +5566,11 @@ public final class Parcel {
* @hide
*/
void readArrayMap(ArrayMap<? super String, Object> map, int size, boolean sorted,
- boolean lazy, @Nullable ClassLoaderProvider loaderProvider, int[] lazyValueCount) {
+ boolean lazy, @Nullable ClassLoader loader, int[] lazyValueCount) {
ensureWithinMemoryLimit(SIZE_COMPLEX_TYPE, size);
while (size > 0) {
String key = readString();
- Object value = (lazy) ? readLazyValue(loaderProvider) : readValue(
- getClassLoader(loaderProvider));
+ Object value = (lazy) ? readLazyValue(loader) : readValue(loader);
if (value instanceof LazyValue) {
lazyValueCount[0]++;
}
@@ -5619,12 +5591,12 @@ public final class Parcel {
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public void readArrayMap(@NonNull ArrayMap<? super String, Object> outVal,
- @Nullable ClassLoaderProvider loaderProvider) {
+ @Nullable ClassLoader loader) {
final int N = readInt();
if (N < 0) {
return;
}
- readArrayMapInternal(outVal, N, loaderProvider);
+ readArrayMapInternal(outVal, N, loader);
}
/**
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 2e231e3957c6..f8a90bbc6994 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -13788,6 +13788,16 @@ public final class Settings {
= "enable_freeform_support";
/**
+ * Whether to override the availability of the desktop experiences features on the
+ * device. With desktop experiences enabled, secondary displays can be used to run
+ * apps, in desktop mode by default. Otherwise they can only be used for mirroring.
+ * @hide
+ */
+ @Readable
+ public static final String DEVELOPMENT_OVERRIDE_DESKTOP_EXPERIENCE_FEATURES =
+ "override_desktop_experience_features";
+
+ /**
* Whether to override the availability of the desktop mode on the main display of the
* device. If on, users can make move an app to the desktop, allowing a freeform windowing
* experience.
diff --git a/core/java/android/text/style/TtsSpan.java b/core/java/android/text/style/TtsSpan.java
index a337ba2a57fb..e0d4ec1ca826 100644
--- a/core/java/android/text/style/TtsSpan.java
+++ b/core/java/android/text/style/TtsSpan.java
@@ -108,11 +108,13 @@ public class TtsSpan implements ParcelableSpan {
/**
* The text associated with this span is a time, consisting of a number of
- * hours and minutes, specified with {@link #ARG_HOURS} and
- * {@link #ARG_MINUTES}.
+ * hours, minutes, and seconds specified with {@link #ARG_HOURS}, {@link #ARG_MINUTES}, and
+ * {@link #ARG_SECONDS}.
* Also accepts the arguments {@link #ARG_GENDER},
* {@link #ARG_ANIMACY}, {@link #ARG_MULTIPLICITY} and
- * {@link #ARG_CASE}.
+ * {@link #ARG_CASE}. This is different from {@link #TYPE_DURATION}. This should be used to
+ * convey a particular moment in time, such as a clock time, while {@link #TYPE_DURATION} should
+ * be used to convey an interval of time.
*/
public static final String TYPE_TIME = "android.type.time";
@@ -310,16 +312,18 @@ public class TtsSpan implements ParcelableSpan {
public static final String ARG_UNIT = "android.arg.unit";
/**
- * Argument used to specify the hours of a time. The hours should be
- * provided as an integer in the range from 0 up to and including 24.
- * Can be used with {@link #TYPE_TIME}.
+ * Argument used to specify the hours of a time or duration. The hours should be
+ * provided as an integer in the range from 0 up to and including 24 for
+ * {@link #TYPE_TIME}.
+ * Can be used with {@link #TYPE_TIME} or {@link #TYPE_DURATION}.
*/
public static final String ARG_HOURS = "android.arg.hours";
/**
- * Argument used to specify the minutes of a time. The minutes should be
- * provided as an integer in the range from 0 up to and including 59.
- * Can be used with {@link #TYPE_TIME}.
+ * Argument used to specify the minutes of a time or duration. The minutes should be
+ * provided as an integer in the range from 0 up to and including 59 for
+ * {@link #TYPE_TIME}.
+ * Can be used with {@link #TYPE_TIME} or {@link #TYPE_DURATION}.
*/
public static final String ARG_MINUTES = "android.arg.minutes";
diff --git a/core/java/android/view/InsetsSourceControl.java b/core/java/android/view/InsetsSourceControl.java
index 7f2f0e8863df..cfb4835a13f7 100644
--- a/core/java/android/view/InsetsSourceControl.java
+++ b/core/java/android/view/InsetsSourceControl.java
@@ -194,7 +194,7 @@ public class InsetsSourceControl implements Parcelable {
}
public void release(Consumer<SurfaceControl> surfaceReleaseConsumer) {
- if (mLeash != null) {
+ if (mLeash != null && mLeash.isValid()) {
surfaceReleaseConsumer.accept(mLeash);
}
}
diff --git a/core/java/android/window/DesktopModeFlags.java b/core/java/android/window/DesktopModeFlags.java
index be69d3da3874..d44b941082b5 100644
--- a/core/java/android/window/DesktopModeFlags.java
+++ b/core/java/android/window/DesktopModeFlags.java
@@ -20,12 +20,13 @@ import android.annotation.Nullable;
import android.app.ActivityThread;
import android.app.Application;
import android.content.ContentResolver;
+import android.os.SystemProperties;
import android.provider.Settings;
import android.util.Log;
import com.android.window.flags.Flags;
-import java.util.function.Supplier;
+import java.util.function.BooleanSupplier;
/**
* Checks desktop mode flag state.
@@ -90,9 +91,35 @@ public enum DesktopModeFlags {
INCLUDE_TOP_TRANSPARENT_FULLSCREEN_TASK_IN_DESKTOP_HEURISTIC(
Flags::includeTopTransparentFullscreenTaskInDesktopHeuristic, true);
- private static final String TAG = "DesktopModeFlagsUtil";
+ /**
+ * Flag class, to be used in case the enum cannot be used because the flag is not accessible.
+ *
+ * <p> This class will still use the process-wide cache.
+ */
+ public static class DesktopModeFlag {
+ // Function called to obtain aconfig flag value.
+ private final BooleanSupplier mFlagFunction;
+ // Whether the flag state should be affected by developer option.
+ private final boolean mShouldOverrideByDevOption;
+
+ public DesktopModeFlag(BooleanSupplier flagFunction, boolean shouldOverrideByDevOption) {
+ this.mFlagFunction = flagFunction;
+ this.mShouldOverrideByDevOption = shouldOverrideByDevOption;
+ }
+
+ /**
+ * Determines state of flag based on the actual flag and desktop mode developer option
+ * overrides.
+ */
+ public boolean isTrue() {
+ return isFlagTrue(mFlagFunction, mShouldOverrideByDevOption);
+ }
+
+ }
+
+ private static final String TAG = "DesktopModeFlags";
// Function called to obtain aconfig flag value.
- private final Supplier<Boolean> mFlagFunction;
+ private final BooleanSupplier mFlagFunction;
// Whether the flag state should be affected by developer option.
private final boolean mShouldOverrideByDevOption;
@@ -100,7 +127,9 @@ public enum DesktopModeFlags {
// be refreshed only on reboots as overridden state is expected to take effect on reboots.
private static ToggleOverride sCachedToggleOverride;
- DesktopModeFlags(Supplier<Boolean> flagFunction, boolean shouldOverrideByDevOption) {
+ public static final String SYSTEM_PROPERTY_NAME = "persist.wm.debug.desktop_experience_devopts";
+
+ DesktopModeFlags(BooleanSupplier flagFunction, boolean shouldOverrideByDevOption) {
this.mFlagFunction = flagFunction;
this.mShouldOverrideByDevOption = shouldOverrideByDevOption;
}
@@ -110,24 +139,42 @@ public enum DesktopModeFlags {
* overrides.
*/
public boolean isTrue() {
- Application application = ActivityThread.currentApplication();
- if (!Flags.showDesktopWindowingDevOption()
- || !mShouldOverrideByDevOption
- || application == null) {
- return mFlagFunction.get();
- } else {
+ return isFlagTrue(mFlagFunction, mShouldOverrideByDevOption);
+ }
+
+ private static boolean isFlagTrue(BooleanSupplier flagFunction,
+ boolean shouldOverrideByDevOption) {
+ if (!shouldOverrideByDevOption) return flagFunction.getAsBoolean();
+ if (Flags.showDesktopExperienceDevOption()) {
+ return switch (getToggleOverride(null)) {
+ case OVERRIDE_UNSET, OVERRIDE_OFF -> flagFunction.getAsBoolean();
+ case OVERRIDE_ON -> true;
+ };
+ }
+ if (Flags.showDesktopWindowingDevOption()) {
+ Application application = ActivityThread.currentApplication();
+ if (application == null) {
+ Log.w(TAG, "Could not get the current application.");
+ return flagFunction.getAsBoolean();
+ }
+ ContentResolver contentResolver = application.getContentResolver();
+ if (contentResolver == null) {
+ Log.w(TAG, "Could not get the content resolver for the application.");
+ return flagFunction.getAsBoolean();
+ }
boolean shouldToggleBeEnabledByDefault = Flags.enableDesktopWindowingMode();
- return switch (getToggleOverride(application.getContentResolver())) {
- case OVERRIDE_UNSET -> mFlagFunction.get();
+ return switch (getToggleOverride(contentResolver)) {
+ case OVERRIDE_UNSET -> flagFunction.getAsBoolean();
// When toggle override matches its default state, don't override flags. This
// helps users reset their feature overrides.
- case OVERRIDE_OFF -> !shouldToggleBeEnabledByDefault && mFlagFunction.get();
- case OVERRIDE_ON -> shouldToggleBeEnabledByDefault ? mFlagFunction.get() : true;
+ case OVERRIDE_OFF -> !shouldToggleBeEnabledByDefault && flagFunction.getAsBoolean();
+ case OVERRIDE_ON -> !shouldToggleBeEnabledByDefault || flagFunction.getAsBoolean();
};
}
+ return flagFunction.getAsBoolean();
}
- private ToggleOverride getToggleOverride(ContentResolver contentResolver) {
+ private static ToggleOverride getToggleOverride(@Nullable ContentResolver contentResolver) {
// If cached, return it
if (sCachedToggleOverride != null) {
return sCachedToggleOverride;
@@ -143,12 +190,21 @@ public enum DesktopModeFlags {
/**
* Returns {@link ToggleOverride} from Settings.Global set by toggle.
*/
- private ToggleOverride getToggleOverrideFromSystem(ContentResolver contentResolver) {
- int settingValue = Settings.Global.getInt(
- contentResolver,
- Settings.Global.DEVELOPMENT_OVERRIDE_DESKTOP_MODE_FEATURES,
- ToggleOverride.OVERRIDE_UNSET.getSetting()
- );
+ private static ToggleOverride getToggleOverrideFromSystem(
+ @Nullable ContentResolver contentResolver) {
+ int settingValue;
+ if (Flags.showDesktopExperienceDevOption()) {
+ settingValue = SystemProperties.getInt(
+ SYSTEM_PROPERTY_NAME,
+ ToggleOverride.OVERRIDE_UNSET.getSetting()
+ );
+ } else {
+ settingValue = Settings.Global.getInt(
+ contentResolver,
+ Settings.Global.DEVELOPMENT_OVERRIDE_DESKTOP_MODE_FEATURES,
+ ToggleOverride.OVERRIDE_UNSET.getSetting()
+ );
+ }
return ToggleOverride.fromSetting(settingValue, ToggleOverride.OVERRIDE_UNSET);
}
diff --git a/core/java/android/window/flags/lse_desktop_experience.aconfig b/core/java/android/window/flags/lse_desktop_experience.aconfig
index b4e7675402b9..73ebcdd8a07b 100644
--- a/core/java/android/window/flags/lse_desktop_experience.aconfig
+++ b/core/java/android/window/flags/lse_desktop_experience.aconfig
@@ -555,4 +555,11 @@ flag {
metadata {
purpose: PURPOSE_BUGFIX
}
-} \ No newline at end of file
+}
+
+flag {
+ name: "show_desktop_experience_dev_option"
+ namespace: "lse_desktop_experience"
+ description: "Replace the freeform windowing dev options with a desktop experience one."
+ bug: "389092752"
+}
diff --git a/core/res/res/values/public-staging.xml b/core/res/res/values/public-staging.xml
index 7baaa6d590f2..a0c4c13a8702 100644
--- a/core/res/res/values/public-staging.xml
+++ b/core/res/res/values/public-staging.xml
@@ -137,6 +137,14 @@
<public name="wantsRoleHolderPriority"/>
<!-- @FlaggedApi(android.sdk.Flags.FLAG_MAJOR_MINOR_VERSIONING_SCHEME) -->
<public name="minSdkVersionFull"/>
+ <!-- @hide Only for device overlay to use this. -->
+ <public name="pointerIconVectorFill"/>
+ <!-- @hide Only for device overlay to use this. -->
+ <public name="pointerIconVectorFillInverse"/>
+ <!-- @hide Only for device overlay to use this. -->
+ <public name="pointerIconVectorStroke"/>
+ <!-- @hide Only for device overlay to use this. -->
+ <public name="pointerIconVectorStrokeInverse"/>
</staging-public-group>
<staging-public-group type="id" first-id="0x01b60000">
diff --git a/core/tests/coretests/src/android/window/DesktopModeFlagsTest.java b/core/tests/coretests/src/android/window/DesktopModeFlagsTest.java
index b28e2b04b342..49927be65ae5 100644
--- a/core/tests/coretests/src/android/window/DesktopModeFlagsTest.java
+++ b/core/tests/coretests/src/android/window/DesktopModeFlagsTest.java
@@ -21,33 +21,42 @@ import static android.window.DesktopModeFlags.ToggleOverride.OVERRIDE_OFF;
import static android.window.DesktopModeFlags.ToggleOverride.OVERRIDE_ON;
import static android.window.DesktopModeFlags.ToggleOverride.OVERRIDE_UNSET;
import static android.window.DesktopModeFlags.ToggleOverride.fromSetting;
-import static android.window.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_TRANSITIONS;
import static com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE;
-import static com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_TRANSITIONS;
+import static com.android.window.flags.Flags.FLAG_SHOW_DESKTOP_EXPERIENCE_DEV_OPTION;
import static com.android.window.flags.Flags.FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION;
import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assume.assumeTrue;
+
import android.content.ContentResolver;
import android.content.Context;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.Presubmit;
+import android.platform.test.flag.junit.FlagsParameterization;
import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
+import android.support.test.uiautomator.UiDevice;
+import android.window.DesktopModeFlags.DesktopModeFlag;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
+import com.android.window.flags.Flags;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
+import platform.test.runner.parameterized.Parameters;
+
import java.lang.reflect.Field;
+import java.util.List;
/**
* Test class for {@link android.window.DesktopModeFlags}
@@ -57,21 +66,39 @@ import java.lang.reflect.Field;
*/
@SmallTest
@Presubmit
-@RunWith(AndroidJUnit4.class)
+@RunWith(ParameterizedAndroidJunit4.class)
public class DesktopModeFlagsTest {
+ @Parameters(name = "{0}")
+ public static List<FlagsParameterization> getParams() {
+ return FlagsParameterization.allCombinationsOf(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION,
+ FLAG_SHOW_DESKTOP_EXPERIENCE_DEV_OPTION);
+ }
+
@Rule
- public SetFlagsRule setFlagsRule = new SetFlagsRule();
+ public SetFlagsRule mSetFlagsRule;
+ private UiDevice mUiDevice;
private Context mContext;
+ private boolean mLocalFlagValue = false;
+ private final DesktopModeFlag mOverriddenLocalFlag = new DesktopModeFlag(
+ () -> mLocalFlagValue, true);
+ private final DesktopModeFlag mNotOverriddenLocalFlag = new DesktopModeFlag(
+ () -> mLocalFlagValue, false);
private static final int OVERRIDE_OFF_SETTING = 0;
private static final int OVERRIDE_ON_SETTING = 1;
private static final int OVERRIDE_UNSET_SETTING = -1;
+ public DesktopModeFlagsTest(FlagsParameterization flags) {
+ mSetFlagsRule = new SetFlagsRule(flags);
+ }
+
@Before
- public void setUp() {
+ public void setUp() throws Exception {
mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+ mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+ setOverride(null);
}
@After
@@ -80,26 +107,35 @@ public class DesktopModeFlagsTest {
}
@Test
- @DisableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION)
@EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
- public void isTrue_devOptionFlagDisabled_overrideOff_featureFlagOn_returnsTrue() {
+ public void isTrue_overrideOff_featureFlagOn() throws Exception {
setOverride(OVERRIDE_OFF_SETTING);
- // In absence of dev options, follow flag
- assertThat(ENABLE_DESKTOP_WINDOWING_MODE.isTrue()).isTrue();
+
+ if (showDesktopWindowingDevOpts()) {
+ // DW Dev Opts turns off flags when ON
+ assertThat(ENABLE_DESKTOP_WINDOWING_MODE.isTrue()).isFalse();
+ } else {
+ // DE Dev Opts doesn't turn flags OFF
+ assertThat(ENABLE_DESKTOP_WINDOWING_MODE.isTrue()).isTrue();
+ }
}
@Test
- @DisableFlags({FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE})
- public void isTrue_devOptionFlagDisabled_overrideOn_featureFlagOff_returnsFalse() {
+ @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
+ public void isTrue_overrideOn_featureFlagOff() throws Exception {
setOverride(OVERRIDE_ON_SETTING);
- assertThat(ENABLE_DESKTOP_WINDOWING_MODE.isTrue()).isFalse();
+ if (showAnyDevOpts()) {
+ assertThat(ENABLE_DESKTOP_WINDOWING_MODE.isTrue()).isTrue();
+ } else {
+ assertThat(ENABLE_DESKTOP_WINDOWING_MODE.isTrue()).isFalse();
+ }
}
@Test
- @EnableFlags({FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE})
- public void isTrue_overrideUnset_featureFlagOn_returnsTrue() {
+ @EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
+ public void isTrue_overrideUnset_featureFlagOn() throws Exception {
setOverride(OVERRIDE_UNSET_SETTING);
// For overridableFlag, for unset overrides, follow flag
@@ -107,9 +143,8 @@ public class DesktopModeFlagsTest {
}
@Test
- @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION)
@DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
- public void isTrue_overrideUnset_featureFlagOff_returnsFalse() {
+ public void isTrue_overrideUnset_featureFlagOff() throws Exception {
setOverride(OVERRIDE_UNSET_SETTING);
// For overridableFlag, for unset overrides, follow flag
@@ -117,8 +152,8 @@ public class DesktopModeFlagsTest {
}
@Test
- @EnableFlags({FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE})
- public void isTrue_noOverride_featureFlagOn_returnsTrue() {
+ @EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
+ public void isTrue_noOverride_featureFlagOn_returnsTrue() throws Exception {
setOverride(null);
// For overridableFlag, in absence of overrides, follow flag
@@ -126,9 +161,8 @@ public class DesktopModeFlagsTest {
}
@Test
- @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION)
@DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
- public void isTrue_noOverride_featureFlagOff_returnsFalse() {
+ public void isTrue_noOverride_featureFlagOff_returnsFalse() throws Exception {
setOverride(null);
// For overridableFlag, in absence of overrides, follow flag
@@ -136,8 +170,8 @@ public class DesktopModeFlagsTest {
}
@Test
- @EnableFlags({FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE})
- public void isTrue_unrecognizableOverride_featureFlagOn_returnsTrue() {
+ @EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
+ public void isTrue_unrecognizableOverride_featureFlagOn_returnsTrue() throws Exception {
setOverride(-2);
// For overridableFlag, for unrecognized overrides, follow flag
@@ -145,9 +179,8 @@ public class DesktopModeFlagsTest {
}
@Test
- @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION)
@DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
- public void isTrue_unrecognizableOverride_featureFlagOff_returnsFalse() {
+ public void isTrue_unrecognizableOverride_featureFlagOff_returnsFalse() throws Exception {
setOverride(-2);
// For overridableFlag, for unrecognizable overrides, follow flag
@@ -155,27 +188,10 @@ public class DesktopModeFlagsTest {
}
@Test
- @EnableFlags({FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE})
- public void isTrue_overrideOff_featureFlagOn_returnsFalse() {
- setOverride(OVERRIDE_OFF_SETTING);
-
- // For overridableFlag, follow override if they exist
- assertThat(ENABLE_DESKTOP_WINDOWING_MODE.isTrue()).isFalse();
- }
-
- @Test
- @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION)
- @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
- public void isTrue_overrideOn_featureFlagOff_returnsTrue() {
- setOverride(OVERRIDE_ON_SETTING);
-
- // For overridableFlag, follow override if they exist
- assertThat(ENABLE_DESKTOP_WINDOWING_MODE.isTrue()).isTrue();
- }
+ @EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
+ public void isTrue_overrideOffThenOn_featureFlagOn_returnsFalseAndFalse() throws Exception {
+ assumeTrue(showDesktopWindowingDevOpts());
- @Test
- @EnableFlags({FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE})
- public void isTrue_overrideOffThenOn_featureFlagOn_returnsFalseAndFalse() {
setOverride(OVERRIDE_OFF_SETTING);
// For overridableFlag, follow override if they exist
@@ -188,9 +204,9 @@ public class DesktopModeFlagsTest {
}
@Test
- @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION)
@DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
- public void isTrue_overrideOnThenOff_featureFlagOff_returnsTrueAndTrue() {
+ public void isTrue_overrideOnThenOff_featureFlagOff_returnsTrueAndTrue() throws Exception {
+ assumeTrue(showAnyDevOpts());
setOverride(OVERRIDE_ON_SETTING);
// For overridableFlag, follow override if they exist
@@ -203,146 +219,144 @@ public class DesktopModeFlagsTest {
}
@Test
- @EnableFlags({FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE,
- FLAG_ENABLE_DESKTOP_WINDOWING_TRANSITIONS})
- public void isTrue_dwFlagOn_overrideUnset_featureFlagOn_returnsTrue() {
+ @EnableFlags({FLAG_ENABLE_DESKTOP_WINDOWING_MODE})
+ public void isTrue_dwFlagOn_overrideUnset_featureFlagOn() throws Exception {
+ mLocalFlagValue = true;
setOverride(OVERRIDE_UNSET_SETTING);
// For unset overrides, follow flag
- assertThat(ENABLE_DESKTOP_WINDOWING_TRANSITIONS.isTrue()).isTrue();
+ assertThat(mOverriddenLocalFlag.isTrue()).isTrue();
+ assertThat(mNotOverriddenLocalFlag.isTrue()).isTrue();
}
@Test
- @EnableFlags({FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE})
- @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_TRANSITIONS)
- public void isTrue_dwFlagOn_overrideUnset_featureFlagOff_returnsFalse() {
+ @EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
+ public void isTrue_dwFlagOn_overrideUnset_featureFlagOff() throws Exception {
+ mLocalFlagValue = false;
setOverride(OVERRIDE_UNSET_SETTING);
// For unset overrides, follow flag
- assertThat(ENABLE_DESKTOP_WINDOWING_TRANSITIONS.isTrue()).isFalse();
+ assertThat(mOverriddenLocalFlag.isTrue()).isFalse();
+ assertThat(mNotOverriddenLocalFlag.isTrue()).isFalse();
}
@Test
- @EnableFlags({
- FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION,
- FLAG_ENABLE_DESKTOP_WINDOWING_MODE,
- FLAG_ENABLE_DESKTOP_WINDOWING_TRANSITIONS
- })
- public void isTrue_dwFlagOn_overrideOn_featureFlagOn_returnsTrue() {
+ @EnableFlags({FLAG_ENABLE_DESKTOP_WINDOWING_MODE})
+ public void isTrue_dwFlagOn_overrideOn_featureFlagOn() throws Exception {
+ mLocalFlagValue = true;
setOverride(OVERRIDE_ON_SETTING);
// When toggle override matches its default state (dw flag), don't override flags
- assertThat(ENABLE_DESKTOP_WINDOWING_TRANSITIONS.isTrue()).isTrue();
+ assertThat(mOverriddenLocalFlag.isTrue()).isTrue();
+ assertThat(mNotOverriddenLocalFlag.isTrue()).isTrue();
}
@Test
- @EnableFlags({FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE})
- @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_TRANSITIONS)
- public void isTrue_dwFlagOn_overrideOn_featureFlagOff_returnsFalse() {
+ @EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
+ public void isTrue_dwFlagOn_overrideOn_featureFlagOff() throws Exception {
+ mLocalFlagValue = false;
setOverride(OVERRIDE_ON_SETTING);
- // When toggle override matches its default state (dw flag), don't override flags
- assertThat(ENABLE_DESKTOP_WINDOWING_TRANSITIONS.isTrue()).isFalse();
+ if (showDesktopExperienceDevOpts()) {
+ assertThat(mOverriddenLocalFlag.isTrue()).isTrue();
+ } else {
+ assertThat(mOverriddenLocalFlag.isTrue()).isFalse();
+ }
+ assertThat(mNotOverriddenLocalFlag.isTrue()).isFalse();
}
@Test
- @EnableFlags({
- FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION,
- FLAG_ENABLE_DESKTOP_WINDOWING_MODE,
- FLAG_ENABLE_DESKTOP_WINDOWING_TRANSITIONS
- })
- public void isTrue_dwFlagOn_overrideOff_featureFlagOn_returnsTrue() {
+ @EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
+ public void isTrue_dwFlagOn_overrideOff_featureFlagOn() throws Exception {
+ mLocalFlagValue = true;
setOverride(OVERRIDE_OFF_SETTING);
- // Follow override if they exist, and is not equal to default toggle state (dw flag)
- assertThat(ENABLE_DESKTOP_WINDOWING_TRANSITIONS.isTrue()).isTrue();
+ if (showDesktopWindowingDevOpts()) {
+ // Follow override if they exist, and is not equal to default toggle state (dw flag)
+ assertThat(mOverriddenLocalFlag.isTrue()).isFalse();
+ } else {
+ assertThat(mOverriddenLocalFlag.isTrue()).isTrue();
+ }
+ assertThat(mNotOverriddenLocalFlag.isTrue()).isTrue();
}
@Test
- @EnableFlags({FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE})
- @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_TRANSITIONS)
- public void isTrue_dwFlagOn_overrideOff_featureFlagOff_returnsFalse() {
+ @EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
+ public void isTrue_dwFlagOn_overrideOff_featureFlagOff_returnsFalse() throws Exception {
+ mLocalFlagValue = false;
setOverride(OVERRIDE_OFF_SETTING);
// Follow override if they exist, and is not equal to default toggle state (dw flag)
- assertThat(ENABLE_DESKTOP_WINDOWING_TRANSITIONS.isTrue()).isFalse();
+ assertThat(mOverriddenLocalFlag.isTrue()).isFalse();
+ assertThat(mNotOverriddenLocalFlag.isTrue()).isFalse();
}
@Test
- @EnableFlags({
- FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION,
- FLAG_ENABLE_DESKTOP_WINDOWING_TRANSITIONS
- })
@DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
- public void isTrue_dwFlagOff_overrideUnset_featureFlagOn_returnsTrue() {
+ public void isTrue_dwFlagOff_overrideUnset_featureFlagOn_returnsTrue() throws Exception {
+ mLocalFlagValue = true;
setOverride(OVERRIDE_UNSET_SETTING);
// For unset overrides, follow flag
- assertThat(ENABLE_DESKTOP_WINDOWING_TRANSITIONS.isTrue()).isTrue();
+ assertThat(mOverriddenLocalFlag.isTrue()).isTrue();
+ assertThat(mNotOverriddenLocalFlag.isTrue()).isTrue();
}
@Test
- @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION)
- @DisableFlags({
- FLAG_ENABLE_DESKTOP_WINDOWING_MODE,
- FLAG_ENABLE_DESKTOP_WINDOWING_TRANSITIONS
- })
- public void isTrue_dwFlagOff_overrideUnset_featureFlagOff_returnsFalse() {
+ @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
+ public void isTrue_dwFlagOff_overrideUnset_featureFlagOff_returnsFalse() throws Exception {
+ mLocalFlagValue = false;
setOverride(OVERRIDE_UNSET_SETTING);
// For unset overrides, follow flag
- assertThat(ENABLE_DESKTOP_WINDOWING_TRANSITIONS.isTrue()).isFalse();
+ assertThat(mOverriddenLocalFlag.isTrue()).isFalse();
+ assertThat(mNotOverriddenLocalFlag.isTrue()).isFalse();
}
@Test
- @EnableFlags({
- FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION,
- FLAG_ENABLE_DESKTOP_WINDOWING_TRANSITIONS
- })
@DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
- public void isTrue_dwFlagOff_overrideOn_featureFlagOn_returnsTrue() {
+ public void isTrue_dwFlagOff_overrideOn_featureFlagOn_returnsTrue() throws Exception {
+ mLocalFlagValue = true;
setOverride(OVERRIDE_ON_SETTING);
// Follow override if they exist, and is not equal to default toggle state (dw flag)
- assertThat(ENABLE_DESKTOP_WINDOWING_TRANSITIONS.isTrue()).isTrue();
+ assertThat(mOverriddenLocalFlag.isTrue()).isTrue();
+ assertThat(mNotOverriddenLocalFlag.isTrue()).isTrue();
}
@Test
- @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION)
- @DisableFlags({
- FLAG_ENABLE_DESKTOP_WINDOWING_MODE,
- FLAG_ENABLE_DESKTOP_WINDOWING_TRANSITIONS
- })
- public void isTrue_dwFlagOff_overrideOn_featureFlagOff_returnFalse() {
+ @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
+ public void isTrue_dwFlagOff_overrideOn_featureFlagOff() throws Exception {
+ mLocalFlagValue = false;
setOverride(OVERRIDE_ON_SETTING);
- // Follow override if they exist, and is not equal to default toggle state (dw flag)
- assertThat(ENABLE_DESKTOP_WINDOWING_TRANSITIONS.isTrue()).isFalse();
+ if (showAnyDevOpts()) {
+ assertThat(mOverriddenLocalFlag.isTrue()).isTrue();
+ } else {
+ // Follow override if they exist, and is not equal to default toggle state (dw flag)
+ assertThat(mOverriddenLocalFlag.isTrue()).isFalse();
+ }
+ assertThat(mNotOverriddenLocalFlag.isTrue()).isFalse();
}
@Test
- @EnableFlags({
- FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION,
- FLAG_ENABLE_DESKTOP_WINDOWING_TRANSITIONS
- })
@DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
- public void isTrue_dwFlagOff_overrideOff_featureFlagOn_returnsTrue() {
+ public void isTrue_dwFlagOff_overrideOff_featureFlagOn_returnsTrue() throws Exception {
+ mLocalFlagValue = true;
setOverride(OVERRIDE_OFF_SETTING);
// When toggle override matches its default state (dw flag), don't override flags
- assertThat(ENABLE_DESKTOP_WINDOWING_TRANSITIONS.isTrue()).isTrue();
+ assertThat(mOverriddenLocalFlag.isTrue()).isTrue();
+ assertThat(mNotOverriddenLocalFlag.isTrue()).isTrue();
}
@Test
- @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION)
- @DisableFlags({
- FLAG_ENABLE_DESKTOP_WINDOWING_MODE,
- FLAG_ENABLE_DESKTOP_WINDOWING_TRANSITIONS
- })
- public void isTrue_dwFlagOff_overrideOff_featureFlagOff_returnsFalse() {
+ @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
+ public void isTrue_dwFlagOff_overrideOff_featureFlagOff_returnsFalse() throws Exception {
+ mLocalFlagValue = false;
setOverride(OVERRIDE_OFF_SETTING);
- // When toggle override matches its default state (dw flag), don't override flags
- assertThat(ENABLE_DESKTOP_WINDOWING_TRANSITIONS.isTrue()).isFalse();
+ assertThat(mOverriddenLocalFlag.isTrue()).isFalse();
+ assertThat(mNotOverriddenLocalFlag.isTrue()).isFalse();
}
@Test
@@ -365,7 +379,9 @@ public class DesktopModeFlagsTest {
assertThat(OVERRIDE_UNSET.getSetting()).isEqualTo(-1);
}
- private void setOverride(Integer setting) {
+ private void setOverride(Integer setting) throws Exception {
+ setSysProp(setting);
+
ContentResolver contentResolver = mContext.getContentResolver();
String key = Settings.Global.DEVELOPMENT_OVERRIDE_DESKTOP_MODE_FEATURES;
@@ -376,11 +392,35 @@ public class DesktopModeFlagsTest {
}
}
+ private void setSysProp(Integer value) throws Exception {
+ if (value == null) {
+ resetSysProp();
+ } else {
+ mUiDevice.executeShellCommand(
+ "setprop " + DesktopModeFlags.SYSTEM_PROPERTY_NAME + " " + value);
+ }
+ }
+
+ private void resetSysProp() throws Exception {
+ mUiDevice.executeShellCommand("setprop " + DesktopModeFlags.SYSTEM_PROPERTY_NAME + " ''");
+ }
+
private void resetCache() throws Exception {
Field cachedToggleOverride = DesktopModeFlags.class.getDeclaredField(
"sCachedToggleOverride");
cachedToggleOverride.setAccessible(true);
cachedToggleOverride.set(null, null);
- setOverride(OVERRIDE_UNSET_SETTING);
+ }
+
+ private boolean showDesktopWindowingDevOpts() {
+ return Flags.showDesktopWindowingDevOption() && !Flags.showDesktopExperienceDevOption();
+ }
+
+ private boolean showDesktopExperienceDevOpts() {
+ return Flags.showDesktopExperienceDevOption();
+ }
+
+ private boolean showAnyDevOpts() {
+ return Flags.showDesktopWindowingDevOption() || Flags.showDesktopExperienceDevOption();
}
}
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatus.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatus.java
index 2fed1380b635..1ee71ca78815 100644
--- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatus.java
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatus.java
@@ -218,6 +218,13 @@ public class DesktopModeStatus {
return isDeviceEligibleForDesktopMode(context) && Flags.showDesktopWindowingDevOption();
}
+ /**
+ * Return {@code true} if desktop mode dev option should be shown on current device
+ */
+ public static boolean canShowDesktopExperienceDevOption(@NonNull Context context) {
+ return Flags.showDesktopExperienceDevOption();
+ }
+
/** Returns if desktop mode dev option should be enabled if there is no user override. */
public static boolean shouldDevOptionBeEnabledByDefault() {
return Flags.enableDesktopWindowingMode();
@@ -290,7 +297,7 @@ public class DesktopModeStatus {
/**
* Return {@code true} if desktop mode is unrestricted and is supported in the device.
*/
- private static boolean isDeviceEligibleForDesktopMode(@NonNull Context context) {
+ public static boolean isDeviceEligibleForDesktopMode(@NonNull Context context) {
return !enforceDeviceRestrictions() || isDesktopModeSupported(context);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
index 4e7f87c48a86..f1f49eda75b6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
@@ -633,8 +633,6 @@ public class BubbleStackView extends FrameLayout
mMagneticTarget,
mIndividualBubbleMagnetListener);
- hideCurrentInputMethod();
-
// Save the magnetized individual bubble so we can dispatch touch events to it.
mMagnetizedObject = mExpandedAnimationController.getMagnetizedBubbleDraggingOut();
} else {
@@ -671,6 +669,10 @@ public class BubbleStackView extends FrameLayout
return;
}
+ if (mPositioner.isImeVisible()) {
+ hideCurrentInputMethod();
+ }
+
// Show the dismiss target, if we haven't already.
mDismissView.show();
diff --git a/media/java/android/media/flags/projection.aconfig b/media/java/android/media/flags/projection.aconfig
index fa1349c61c4c..6d4f0b4f47d5 100644
--- a/media/java/android/media/flags/projection.aconfig
+++ b/media/java/android/media/flags/projection.aconfig
@@ -29,3 +29,13 @@ flag {
is_exported: true
}
+flag {
+ namespace: "media_projection"
+ name: "show_stop_dialog_post_call_end"
+ description: "Shows a stop dialog for MediaProjection sessions that started during call and remain active after a call ends"
+ bug: "390343524"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+ is_exported: true
+}
diff --git a/media/java/android/media/projection/IMediaProjectionWatcherCallback.aidl b/media/java/android/media/projection/IMediaProjectionWatcherCallback.aidl
index e46d34e81483..3baf4d7efd65 100644
--- a/media/java/android/media/projection/IMediaProjectionWatcherCallback.aidl
+++ b/media/java/android/media/projection/IMediaProjectionWatcherCallback.aidl
@@ -18,6 +18,7 @@ package android.media.projection;
import android.media.projection.MediaProjectionInfo;
import android.view.ContentRecordingSession;
+import android.media.projection.MediaProjectionEvent;
/** {@hide} */
oneway interface IMediaProjectionWatcherCallback {
@@ -35,4 +36,19 @@ oneway interface IMediaProjectionWatcherCallback {
in MediaProjectionInfo info,
in @nullable ContentRecordingSession session
);
+
+ /**
+ * Called when a specific {@link MediaProjectionEvent} occurs during the media projection session.
+ *
+ * @param event contains the event type, which describes the nature/context of the event.
+ * @param info optional {@link MediaProjectionInfo} containing details about the media
+ projection host.
+ * @param session the recording session for the current media projection. Can be
+ * {@code null} when the recording will stop.
+ */
+ void onMediaProjectionEvent(
+ in MediaProjectionEvent event,
+ in @nullable MediaProjectionInfo info,
+ in @nullable ContentRecordingSession session
+ );
}
diff --git a/media/java/android/media/projection/MediaProjectionEvent.aidl b/media/java/android/media/projection/MediaProjectionEvent.aidl
new file mode 100644
index 000000000000..34359900ce81
--- /dev/null
+++ b/media/java/android/media/projection/MediaProjectionEvent.aidl
@@ -0,0 +1,3 @@
+package android.media.projection;
+
+parcelable MediaProjectionEvent; \ No newline at end of file
diff --git a/media/java/android/media/projection/MediaProjectionEvent.java b/media/java/android/media/projection/MediaProjectionEvent.java
new file mode 100644
index 000000000000..6922560c8abe
--- /dev/null
+++ b/media/java/android/media/projection/MediaProjectionEvent.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.projection;
+
+import android.annotation.IntDef;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/** @hide */
+public final class MediaProjectionEvent implements Parcelable {
+
+ /**
+ * Represents various media projection events.
+ */
+ @IntDef({PROJECTION_STARTED_DURING_CALL_AND_ACTIVE_POST_CALL})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface EventType {}
+
+ /** Event type for when a call ends but the session is still active. */
+ public static final int PROJECTION_STARTED_DURING_CALL_AND_ACTIVE_POST_CALL = 0;
+
+ private final @EventType int mEventType;
+ private final long mTimestampMillis;
+
+ public MediaProjectionEvent(@EventType int eventType, long timestampMillis) {
+ mEventType = eventType;
+ mTimestampMillis = timestampMillis;
+ }
+
+ private MediaProjectionEvent(Parcel in) {
+ mEventType = in.readInt();
+ mTimestampMillis = in.readLong();
+ }
+
+ public @EventType int getEventType() {
+ return mEventType;
+ }
+
+ public long getTimestampMillis() {
+ return mTimestampMillis;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o instanceof MediaProjectionEvent other) {
+ return mEventType == other.mEventType && mTimestampMillis == other.mTimestampMillis;
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mEventType, mTimestampMillis);
+ }
+
+ @Override
+ public String toString() {
+ return "MediaProjectionEvent{mEventType=" + mEventType + ", mTimestampMillis="
+ + mTimestampMillis + "}";
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(mEventType);
+ out.writeLong(mTimestampMillis);
+ }
+
+ public static final Parcelable.Creator<MediaProjectionEvent> CREATOR =
+ new Parcelable.Creator<>() {
+ @Override
+ public MediaProjectionEvent createFromParcel(Parcel in) {
+ return new MediaProjectionEvent(in);
+ }
+
+ @Override
+ public MediaProjectionEvent[] newArray(int size) {
+ return new MediaProjectionEvent[size];
+ }
+ };
+}
diff --git a/media/java/android/media/projection/MediaProjectionManager.java b/media/java/android/media/projection/MediaProjectionManager.java
index 9cc2cca441a4..9036bf385d96 100644
--- a/media/java/android/media/projection/MediaProjectionManager.java
+++ b/media/java/android/media/projection/MediaProjectionManager.java
@@ -363,6 +363,19 @@ public final class MediaProjectionManager {
@Nullable ContentRecordingSession session
) {
}
+
+ /**
+ * Called when a specific {@link MediaProjectionEvent} occurs during the media projection
+ * session.
+ *
+ * @param event the media projection event details.
+ * @param info optional details about the media projection host.
+ * @param session optional associated recording session details.
+ */
+ public void onMediaProjectionEvent(
+ final MediaProjectionEvent event,
+ @Nullable MediaProjectionInfo info,
+ @Nullable final ContentRecordingSession session) {}
}
/** @hide */
@@ -405,5 +418,13 @@ public final class MediaProjectionManager {
) {
mHandler.post(() -> mCallback.onRecordingSessionSet(info, session));
}
+
+ @Override
+ public void onMediaProjectionEvent(
+ final MediaProjectionEvent event,
+ @Nullable MediaProjectionInfo info,
+ @Nullable final ContentRecordingSession session) {
+ mHandler.post(() -> mCallback.onMediaProjectionEvent(event, info, session));
+ }
}
}
diff --git a/media/java/android/media/quality/MediaQualityManager.java b/media/java/android/media/quality/MediaQualityManager.java
index b7269256a449..0d6d32a22dae 100644
--- a/media/java/android/media/quality/MediaQualityManager.java
+++ b/media/java/android/media/quality/MediaQualityManager.java
@@ -51,7 +51,6 @@ import java.util.function.Consumer;
@FlaggedApi(Flags.FLAG_MEDIA_QUALITY_FW)
@SystemService(Context.MEDIA_QUALITY_SERVICE)
public final class MediaQualityManager {
- // TODO: unhide the APIs for api review
private static final String TAG = "MediaQualityManager";
private final IMediaQualityManager mService;
@@ -123,7 +122,6 @@ public final class MediaQualityManager {
public void onPictureProfileAdded(String profileId, PictureProfile profile) {
synchronized (mPpLock) {
for (PictureProfileCallbackRecord record : mPpCallbackRecords) {
- // TODO: filter callback record
record.postPictureProfileAdded(profileId, profile);
}
}
@@ -132,7 +130,6 @@ public final class MediaQualityManager {
public void onPictureProfileUpdated(String profileId, PictureProfile profile) {
synchronized (mPpLock) {
for (PictureProfileCallbackRecord record : mPpCallbackRecords) {
- // TODO: filter callback record
record.postPictureProfileUpdated(profileId, profile);
}
}
@@ -141,7 +138,6 @@ public final class MediaQualityManager {
public void onPictureProfileRemoved(String profileId, PictureProfile profile) {
synchronized (mPpLock) {
for (PictureProfileCallbackRecord record : mPpCallbackRecords) {
- // TODO: filter callback record
record.postPictureProfileRemoved(profileId, profile);
}
}
@@ -151,7 +147,6 @@ public final class MediaQualityManager {
String profileId, List<ParameterCapability> caps) {
synchronized (mPpLock) {
for (PictureProfileCallbackRecord record : mPpCallbackRecords) {
- // TODO: filter callback record
record.postParameterCapabilitiesChanged(profileId, caps);
}
}
@@ -160,7 +155,6 @@ public final class MediaQualityManager {
public void onError(String profileId, int err) {
synchronized (mPpLock) {
for (PictureProfileCallbackRecord record : mPpCallbackRecords) {
- // TODO: filter callback record
record.postError(profileId, err);
}
}
@@ -171,7 +165,6 @@ public final class MediaQualityManager {
public void onSoundProfileAdded(String profileId, SoundProfile profile) {
synchronized (mSpLock) {
for (SoundProfileCallbackRecord record : mSpCallbackRecords) {
- // TODO: filter callback record
record.postSoundProfileAdded(profileId, profile);
}
}
@@ -180,7 +173,6 @@ public final class MediaQualityManager {
public void onSoundProfileUpdated(String profileId, SoundProfile profile) {
synchronized (mSpLock) {
for (SoundProfileCallbackRecord record : mSpCallbackRecords) {
- // TODO: filter callback record
record.postSoundProfileUpdated(profileId, profile);
}
}
@@ -189,7 +181,6 @@ public final class MediaQualityManager {
public void onSoundProfileRemoved(String profileId, SoundProfile profile) {
synchronized (mSpLock) {
for (SoundProfileCallbackRecord record : mSpCallbackRecords) {
- // TODO: filter callback record
record.postSoundProfileRemoved(profileId, profile);
}
}
@@ -199,7 +190,6 @@ public final class MediaQualityManager {
String profileId, List<ParameterCapability> caps) {
synchronized (mSpLock) {
for (SoundProfileCallbackRecord record : mSpCallbackRecords) {
- // TODO: filter callback record
record.postParameterCapabilitiesChanged(profileId, caps);
}
}
@@ -208,7 +198,6 @@ public final class MediaQualityManager {
public void onError(String profileId, int err) {
synchronized (mSpLock) {
for (SoundProfileCallbackRecord record : mSpCallbackRecords) {
- // TODO: filter callback record
record.postError(profileId, err);
}
}
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 9aad5d5f8367..246aa7158cab 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -178,6 +178,7 @@ public class SettingsBackupTest {
Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT,
Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS,
Settings.Global.DEVELOPMENT_OVERRIDE_DESKTOP_MODE_FEATURES,
+ Settings.Global.DEVELOPMENT_OVERRIDE_DESKTOP_EXPERIENCE_FEATURES,
Settings.Global.DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES,
Settings.Global.DEVELOPMENT_FORCE_RTL,
Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW,
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/aconfig/Android.bp b/packages/SystemUI/accessibility/accessibilitymenu/aconfig/Android.bp
index 0ff856e0b91e..1d74774c7c11 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/aconfig/Android.bp
+++ b/packages/SystemUI/accessibility/accessibilitymenu/aconfig/Android.bp
@@ -5,7 +5,7 @@ package {
aconfig_declarations {
name: "com_android_a11y_menu_flags",
package: "com.android.systemui.accessibility.accessibilitymenu",
- container: "system",
+ container: "system_ext",
srcs: [
"accessibility.aconfig",
],
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/aconfig/accessibility.aconfig b/packages/SystemUI/accessibility/accessibilitymenu/aconfig/accessibility.aconfig
index 6d790114803a..bdf6d4242e68 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/aconfig/accessibility.aconfig
+++ b/packages/SystemUI/accessibility/accessibilitymenu/aconfig/accessibility.aconfig
@@ -1,5 +1,5 @@
package: "com.android.systemui.accessibility.accessibilitymenu"
-container: "system"
+container: "system_ext"
# NOTE: Keep alphabetized to help limit merge conflicts from multiple simultaneous editors.
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ResponsiveLazyHorizontalGrid.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ResponsiveLazyHorizontalGrid.kt
index c7930549abe8..44c375d6ac5e 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ResponsiveLazyHorizontalGrid.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ResponsiveLazyHorizontalGrid.kt
@@ -272,9 +272,8 @@ private fun calculateNumCellsWidth(width: Dp) =
}
private fun calculateNumCellsHeight(height: Dp) =
- // See https://developer.android.com/develop/ui/views/layout/use-window-size-classes
when {
- height >= 900.dp -> 3
+ height >= 1000.dp -> 3
height >= 480.dp -> 2
else -> 1
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardFingerprintListenModel.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardFingerprintListenModel.kt
index 953cf88feccb..943cbe87c8c2 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardFingerprintListenModel.kt
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardFingerprintListenModel.kt
@@ -49,6 +49,7 @@ data class KeyguardFingerprintListenModel(
var systemUser: Boolean = false,
var udfps: Boolean = false,
var userDoesNotHaveTrust: Boolean = false,
+ var communalShowing: Boolean = false,
) : KeyguardListenModel() {
/** List of [String] to be used as a [Row] with [DumpsysTableLogger]. */
@@ -81,6 +82,7 @@ data class KeyguardFingerprintListenModel(
systemUser.toString(),
udfps.toString(),
userDoesNotHaveTrust.toString(),
+ communalShowing.toString(),
)
}
@@ -122,6 +124,7 @@ data class KeyguardFingerprintListenModel(
systemUser = model.systemUser
udfps = model.udfps
userDoesNotHaveTrust = model.userDoesNotHaveTrust
+ communalShowing = model.communalShowing
}
}
@@ -170,6 +173,7 @@ data class KeyguardFingerprintListenModel(
"systemUser",
"underDisplayFingerprint",
"userDoesNotHaveTrust",
+ "communalShowing",
)
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 101fcdfce7e5..c266a5b47cff 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -43,6 +43,7 @@ import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STR
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN;
+import static com.android.systemui.Flags.glanceableHubV2;
import static com.android.systemui.Flags.simPinBouncerReset;
import static com.android.systemui.Flags.simPinUseSlotId;
import static com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_OPENED;
@@ -128,6 +129,7 @@ import com.android.systemui.biometrics.AuthController;
import com.android.systemui.biometrics.FingerprintInteractiveToAuthProvider;
import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor;
import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
@@ -294,6 +296,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
private final Provider<JavaAdapter> mJavaAdapter;
private final Provider<SceneInteractor> mSceneInteractor;
private final Provider<AlternateBouncerInteractor> mAlternateBouncerInteractor;
+ private final CommunalSceneInteractor mCommunalSceneInteractor;
private final AuthController mAuthController;
private final UiEventLogger mUiEventLogger;
private final Set<String> mAllowFingerprintOnOccludingActivitiesFromPackage;
@@ -404,6 +407,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
protected int mFingerprintRunningState = BIOMETRIC_STATE_STOPPED;
private boolean mFingerprintDetectRunning;
private boolean mIsDreaming;
+ private boolean mCommunalShowing;
private int mActiveMobileDataSubscription = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
private final FingerprintInteractiveToAuthProvider mFingerprintInteractiveToAuthProvider;
@@ -2205,7 +2209,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
IActivityTaskManager activityTaskManagerService,
Provider<AlternateBouncerInteractor> alternateBouncerInteractor,
Provider<JavaAdapter> javaAdapter,
- Provider<SceneInteractor> sceneInteractor) {
+ Provider<SceneInteractor> sceneInteractor,
+ CommunalSceneInteractor communalSceneInteractor) {
mContext = context;
mSubscriptionManager = subscriptionManager;
mUserTracker = userTracker;
@@ -2254,6 +2259,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
mAlternateBouncerInteractor = alternateBouncerInteractor;
mJavaAdapter = javaAdapter;
mSceneInteractor = sceneInteractor;
+ mCommunalSceneInteractor = communalSceneInteractor;
mHandler = new Handler(mainLooper) {
@Override
@@ -2535,6 +2541,13 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
);
}
+ if (glanceableHubV2()) {
+ mJavaAdapter.get().alwaysCollectFlow(
+ mCommunalSceneInteractor.isCommunalVisible(),
+ this::onCommunalShowingChanged
+ );
+ }
+
// start() can be invoked in the middle of user switching, so check for this state and issue
// the call manually as that important event was missed.
if (mUserTracker.isUserSwitching()) {
@@ -2837,6 +2850,15 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
}
/**
+ * Sets whether the communal hub is showing.
+ */
+ @VisibleForTesting
+ void onCommunalShowingChanged(boolean showing) {
+ mCommunalShowing = showing;
+ updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE);
+ }
+
+ /**
* Whether the alternate bouncer is showing.
*/
public void setAlternateBouncerShowing(boolean showing) {
@@ -2998,11 +3020,14 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
final boolean strongerAuthRequired = !isUnlockingWithFingerprintAllowed();
final boolean shouldListenBouncerState =
!strongerAuthRequired || !isPrimaryBouncerShowingOrWillBeShowing();
+ final boolean isUdfpsAuthRequiredOnCommunal =
+ !mCommunalShowing || isAlternateBouncerShowing();
final boolean shouldListenUdfpsState = !isUdfps
|| (!userCanSkipBouncer
&& !strongerAuthRequired
- && userDoesNotHaveTrust);
+ && userDoesNotHaveTrust
+ && (!glanceableHubV2() || isUdfpsAuthRequiredOnCommunal));
boolean shouldListen = shouldListenKeyguardState && shouldListenUserState
@@ -3033,7 +3058,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
mSwitchingUser,
mIsSystemUser,
isUdfps,
- userDoesNotHaveTrust));
+ userDoesNotHaveTrust,
+ mCommunalShowing));
return shouldListen;
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 2645811fa4ad..312d2ffd74e4 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -38,6 +38,7 @@ import static com.android.keyguard.KeyguardUpdateMonitor.BIOMETRIC_STATE_CANCELL
import static com.android.keyguard.KeyguardUpdateMonitor.BIOMETRIC_STATE_STOPPED;
import static com.android.keyguard.KeyguardUpdateMonitor.DEFAULT_CANCEL_SIGNAL_TIMEOUT;
import static com.android.keyguard.KeyguardUpdateMonitor.HAL_POWER_PRESS_TIMEOUT;
+import static com.android.systemui.Flags.FLAG_GLANCEABLE_HUB_V2;
import static com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_OPENED;
import static com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_UNKNOWN;
@@ -139,6 +140,7 @@ import com.android.systemui.biometrics.AuthController;
import com.android.systemui.biometrics.FingerprintInteractiveToAuthProvider;
import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor;
import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor;
import com.android.systemui.deviceentry.data.repository.FaceWakeUpTriggersConfig;
import com.android.systemui.deviceentry.data.repository.FaceWakeUpTriggersConfigImpl;
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor;
@@ -180,6 +182,9 @@ import org.mockito.MockitoAnnotations;
import org.mockito.MockitoSession;
import org.mockito.quality.Strictness;
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
+import platform.test.runner.parameterized.Parameters;
+
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -189,9 +194,6 @@ import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
-import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
-import platform.test.runner.parameterized.Parameters;
-
@SmallTest
@RunWith(ParameterizedAndroidJunit4.class)
@TestableLooper.RunWithLooper
@@ -304,6 +306,8 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
private JavaAdapter mJavaAdapter;
@Mock
private SceneInteractor mSceneInteractor;
+ @Mock
+ private CommunalSceneInteractor mCommunalSceneInteractor;
@Captor
private ArgumentCaptor<FaceAuthenticationListener> mFaceAuthenticationListener;
@@ -1084,6 +1088,49 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
}
@Test
+ @EnableFlags(FLAG_GLANCEABLE_HUB_V2)
+ public void udfpsStopsListeningWhenCommunalShowing() {
+ // GIVEN keyguard showing
+ mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON);
+ mKeyguardUpdateMonitor.setKeyguardShowing(true, false);
+
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFingerprint(true)).isTrue();
+
+ // WHEN communal is shown
+ mKeyguardUpdateMonitor.onCommunalShowingChanged(true);
+
+ // THEN shouldn't listen for fingerprint
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFingerprint(true)).isFalse();
+
+ // WHEN alternate bouncer shows on top of communal, we should listen for fingerprint
+ mKeyguardUpdateMonitor.setAlternateBouncerVisibility(true);
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFingerprint(true)).isTrue();
+
+ // WHEN communal is hidden
+ mKeyguardUpdateMonitor.onCommunalShowingChanged(false);
+ mKeyguardUpdateMonitor.setAlternateBouncerVisibility(false);
+
+ // THEN listen for fingerprint
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFingerprint(true)).isTrue();
+ }
+
+ @Test
+ @EnableFlags(FLAG_GLANCEABLE_HUB_V2)
+ public void sfpsNotAffectedByCommunalShowing() {
+ // GIVEN keyguard showing
+ mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON);
+ mKeyguardUpdateMonitor.setKeyguardShowing(true, false);
+
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFingerprint(false)).isTrue();
+
+ // WHEN communal is shown
+ mKeyguardUpdateMonitor.onCommunalShowingChanged(true);
+
+ // THEN we should still listen for fingerprint if not UDFPS
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFingerprint(false)).isTrue();
+ }
+
+ @Test
public void testFingerprintPowerPressed_restartsFingerprintListeningStateWithDelay() {
mKeyguardUpdateMonitor.mFingerprintAuthenticationCallback
.onAuthenticationError(FingerprintManager.BIOMETRIC_ERROR_POWER_PRESSED, "");
@@ -2669,7 +2716,8 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
mTaskStackChangeListeners, mSelectedUserInteractor, mActivityTaskManager,
() -> mAlternateBouncerInteractor,
() -> mJavaAdapter,
- () -> mSceneInteractor);
+ () -> mSceneInteractor,
+ mCommunalSceneInteractor);
setAlternateBouncerVisibility(false);
setPrimaryBouncerVisibility(false);
setStrongAuthTracker(KeyguardUpdateMonitorTest.this.mStrongAuthTracker);
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index c0dbfa546a94..89f0d0edbf2b 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -710,7 +710,9 @@ public class HdmiControlService extends SystemService {
// Register ContentObserver to monitor the settings change.
registerContentObserver();
}
- mMhlController.setOption(OPTION_MHL_SERVICE_CONTROL, ENABLED);
+ if (mMhlController != null) {
+ mMhlController.setOption(OPTION_MHL_SERVICE_CONTROL, ENABLED);
+ }
}
@VisibleForTesting
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 c428f39fd9d0..34a6cb951d46 100644
--- a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
+++ b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
@@ -58,6 +58,7 @@ import android.media.projection.IMediaProjection;
import android.media.projection.IMediaProjectionCallback;
import android.media.projection.IMediaProjectionManager;
import android.media.projection.IMediaProjectionWatcherCallback;
+import android.media.projection.MediaProjectionEvent;
import android.media.projection.MediaProjectionInfo;
import android.media.projection.MediaProjectionManager;
import android.media.projection.ReviewGrantedConsentResult;
@@ -80,6 +81,7 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
+import com.android.media.projection.flags.Flags;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.Watchdog;
@@ -177,9 +179,31 @@ public final class MediaProjectionManagerService extends SystemService
private void maybeStopMediaProjection(int reason) {
synchronized (mLock) {
- if (!mMediaProjectionStopController.isExemptFromStopping(mProjectionGrant, reason)) {
- Slog.d(TAG, "Content Recording: Stopping MediaProjection due to "
- + MediaProjectionStopController.stopReasonToString(reason));
+ if (mMediaProjectionStopController.isExemptFromStopping(mProjectionGrant, reason)) {
+ return;
+ }
+
+ if (Flags.showStopDialogPostCallEnd()
+ && mMediaProjectionStopController.isStopReasonCallEnd(reason)) {
+ MediaProjectionEvent event =
+ new MediaProjectionEvent(
+ MediaProjectionEvent
+ .PROJECTION_STARTED_DURING_CALL_AND_ACTIVE_POST_CALL,
+ System.currentTimeMillis());
+ Slog.d(
+ TAG,
+ "Scheduling event: "
+ + event.getEventType()
+ + " for reason: "
+ + MediaProjectionStopController.stopReasonToString(reason));
+
+ // Post the PROJECTION_STARTED_DURING_CALL_AND_ACTIVE_POST_CALL event with a delay.
+ mHandler.postDelayed(() -> dispatchEvent(event), 500);
+ } else {
+ Slog.d(
+ TAG,
+ "Stopping MediaProjection due to reason: "
+ + MediaProjectionStopController.stopReasonToString(reason));
mProjectionGrant.stop(StopReason.STOP_DEVICE_LOCKED);
}
}
@@ -388,6 +412,24 @@ public final class MediaProjectionManagerService extends SystemService
mCallbackDelegate.dispatchSession(projectionInfo, session);
}
+ private void dispatchEvent(@NonNull MediaProjectionEvent event) {
+ if (!Flags.showStopDialogPostCallEnd()) {
+ Slog.d(
+ TAG,
+ "Event dispatch skipped. Reason: Flag showStopDialogPostCallEnd "
+ + "is disabled. Event details: "
+ + event);
+ return;
+ }
+ MediaProjectionInfo projectionInfo;
+ ContentRecordingSession session;
+ synchronized (mLock) {
+ projectionInfo = mProjectionGrant != null ? mProjectionGrant.getProjectionInfo() : null;
+ session = mProjectionGrant != null ? mProjectionGrant.mSession : null;
+ }
+ mCallbackDelegate.dispatchEvent(event, projectionInfo, session);
+ }
+
/**
* Returns {@code true} when updating the current mirroring session on WM succeeded, and
* {@code false} otherwise.
@@ -1467,6 +1509,25 @@ public final class MediaProjectionManagerService extends SystemService
}
}
+ private void dispatchEvent(
+ @NonNull MediaProjectionEvent event,
+ @Nullable MediaProjectionInfo info,
+ @Nullable ContentRecordingSession session) {
+ if (!Flags.showStopDialogPostCallEnd()) {
+ Slog.d(
+ TAG,
+ "Event dispatch skipped. Reason: Flag showStopDialogPostCallEnd "
+ + "is disabled. Event details: "
+ + event);
+ return;
+ }
+ synchronized (mLock) {
+ for (IMediaProjectionWatcherCallback callback : mWatcherCallbacks.values()) {
+ mHandler.post(new WatcherEventCallback(callback, event, info, session));
+ }
+ }
+ }
+
public void dispatchSession(
@NonNull MediaProjectionInfo projectionInfo,
@Nullable ContentRecordingSession session) {
@@ -1593,6 +1654,41 @@ public final class MediaProjectionManagerService extends SystemService
}
}
+ private static final class WatcherEventCallback implements Runnable {
+ private final IMediaProjectionWatcherCallback mCallback;
+ private final MediaProjectionEvent mEvent;
+ private final MediaProjectionInfo mProjectionInfo;
+ private final ContentRecordingSession mSession;
+
+ WatcherEventCallback(
+ @NonNull IMediaProjectionWatcherCallback callback,
+ @NonNull MediaProjectionEvent event,
+ @Nullable MediaProjectionInfo projectionInfo,
+ @Nullable ContentRecordingSession session) {
+ mCallback = callback;
+ mEvent = event;
+ mProjectionInfo = projectionInfo;
+ mSession = session;
+ }
+
+ @Override
+ public void run() {
+ if (!Flags.showStopDialogPostCallEnd()) {
+ Slog.d(
+ TAG,
+ "Not running WatcherEventCallback. Reason: Flag "
+ + "showStopDialogPostCallEnd is disabled. "
+ );
+ return;
+ }
+ try {
+ mCallback.onMediaProjectionEvent(mEvent, mProjectionInfo, mSession);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to notify MediaProjectionEvent change", e);
+ }
+ }
+ }
+
private static final class WatcherSessionCallback implements Runnable {
private final IMediaProjectionWatcherCallback mCallback;
private final MediaProjectionInfo mProjectionInfo;
diff --git a/services/core/java/com/android/server/media/projection/MediaProjectionStopController.java b/services/core/java/com/android/server/media/projection/MediaProjectionStopController.java
index c018e6bc1dc7..2e0bb4f88485 100644
--- a/services/core/java/com/android/server/media/projection/MediaProjectionStopController.java
+++ b/services/core/java/com/android/server/media/projection/MediaProjectionStopController.java
@@ -95,6 +95,11 @@ public class MediaProjectionStopController {
}
}
+ /** Checks if the given stop reason corresponds to a call ending. */
+ public boolean isStopReasonCallEnd(int stopReason) {
+ return stopReason == STOP_REASON_CALL_END;
+ }
+
/**
* Checks whether the given projection grant is exempt from stopping restrictions.
*/
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 57ccab027c29..a2c53e56b9c9 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -2922,7 +2922,16 @@ public class UserManagerService extends IUserManager.Stub {
* switchable.
*/
public @UserManager.UserSwitchabilityResult int getUserSwitchability(int userId) {
- checkManageOrInteractPermissionIfCallerInOtherProfileGroup(userId, "getUserSwitchability");
+ if (Flags.getUserSwitchabilityPermission()) {
+ if (!hasManageUsersOrPermission(android.Manifest.permission.INTERACT_ACROSS_USERS)) {
+ throw new SecurityException(
+ "You need MANAGE_USERS or INTERACT_ACROSS_USERS permission to "
+ + "getUserSwitchability");
+ }
+ } else {
+ checkManageOrInteractPermissionIfCallerInOtherProfileGroup(userId,
+ "getUserSwitchability");
+ }
final TimingsTraceAndSlog t = new TimingsTraceAndSlog();
t.traceBegin("getUserSwitchability-" + userId);
diff --git a/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java b/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java
index 441d3eaf2348..142d919da455 100644
--- a/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java
+++ b/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java
@@ -24,6 +24,8 @@ import android.util.Log;
import android.view.KeyEvent;
import android.view.ViewConfiguration;
+import com.android.hardware.input.Flags;
+
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -355,6 +357,19 @@ public final class SingleKeyGestureDetector {
}
if (event.getKeyCode() == mActiveRule.mKeyCode) {
+ if (Flags.abortSlowMultiPress()
+ && (event.getEventTime() - mLastDownTime
+ >= mActiveRule.getLongPressTimeoutMs())) {
+ // In this case, we are either on a first long press (but long press behavior is not
+ // supported for this rule), or, on a non-first press that is at least as long as
+ // the long-press duration. Thus, we will cancel the multipress gesture.
+ if (DEBUG) {
+ Log.d(TAG, "The duration of the press is too slow. Resetting.");
+ }
+ reset();
+ return false;
+ }
+
// key-up action should always be triggered if not processed by long press.
MessageObject object = new MessageObject(mActiveRule, mActiveRule.mKeyCode,
mKeyPressCounter, event);
diff --git a/services/core/java/com/android/server/wm/ScreenRecordingCallbackController.java b/services/core/java/com/android/server/wm/ScreenRecordingCallbackController.java
index efc68aac0323..00e1c01bbadb 100644
--- a/services/core/java/com/android/server/wm/ScreenRecordingCallbackController.java
+++ b/services/core/java/com/android/server/wm/ScreenRecordingCallbackController.java
@@ -22,6 +22,7 @@ import static com.android.internal.protolog.WmProtoLogGroups.WM_ERROR;
import android.media.projection.IMediaProjectionManager;
import android.media.projection.IMediaProjectionWatcherCallback;
+import android.media.projection.MediaProjectionEvent;
import android.media.projection.MediaProjectionInfo;
import android.os.Binder;
import android.os.IBinder;
@@ -84,6 +85,12 @@ public class ScreenRecordingCallbackController {
public void onRecordingSessionSet(MediaProjectionInfo mediaProjectionInfo,
ContentRecordingSession contentRecordingSession) {
}
+
+ @Override
+ public void onMediaProjectionEvent(
+ MediaProjectionEvent event,
+ MediaProjectionInfo mediaProjectionInfo,
+ ContentRecordingSession session) {}
}
ScreenRecordingCallbackController(WindowManagerService wms) {
diff --git a/services/core/java/com/android/server/wm/WallpaperWindowToken.java b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
index 0ecd0251ca94..3b79c54f1c73 100644
--- a/services/core/java/com/android/server/wm/WallpaperWindowToken.java
+++ b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
@@ -89,8 +89,9 @@ class WallpaperWindowToken extends WindowToken {
// Similar to Task.prepareSurfaces, outside of transitions we need to apply visibility
// changes directly. In transitions the transition player will take care of applying the
// visibility change.
- if (!mTransitionController.inTransition(this)) {
- getSyncTransaction().setVisibility(mSurfaceControl, isVisible());
+ if (!mTransitionController.isCollecting(this)
+ && !mTransitionController.isPlayingTarget(this)) {
+ getPendingTransaction().setVisibility(mSurfaceControl, isVisible());
}
}
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 5de0e9b6ed93..3a1d652f82d4 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -309,6 +309,7 @@ import android.window.ActivityWindowInfo;
import android.window.AddToSurfaceSyncGroupResult;
import android.window.ClientWindowFrames;
import android.window.ConfigurationChangeSetting;
+import android.window.DesktopModeFlags;
import android.window.IGlobalDragListener;
import android.window.IScreenRecordingCallback;
import android.window.ISurfaceSyncGroupCompletedListener;
@@ -820,6 +821,8 @@ public class WindowManagerService extends IWindowManager.Stub
DEVELOPMENT_WM_DISPLAY_SETTINGS_PATH);
private final Uri mMaximumObscuringOpacityForTouchUri = Settings.Global.getUriFor(
Settings.Global.MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH);
+ private final Uri mDevelopmentOverrideDesktopExperienceUri = Settings.Global.getUriFor(
+ Settings.Global.DEVELOPMENT_OVERRIDE_DESKTOP_EXPERIENCE_FEATURES);
public SettingsObserver() {
super(new Handler());
@@ -847,6 +850,8 @@ public class WindowManagerService extends IWindowManager.Stub
UserHandle.USER_ALL);
resolver.registerContentObserver(mMaximumObscuringOpacityForTouchUri, false, this,
UserHandle.USER_ALL);
+ resolver.registerContentObserver(mDevelopmentOverrideDesktopExperienceUri, false, this,
+ UserHandle.USER_ALL);
}
@Override
@@ -890,6 +895,11 @@ public class WindowManagerService extends IWindowManager.Stub
return;
}
+ if (mDevelopmentOverrideDesktopExperienceUri.equals(uri)) {
+ updateDevelopmentOverrideDesktopExperience();
+ return;
+ }
+
@UpdateAnimationScaleMode
final int mode;
if (mWindowAnimationScaleUri.equals(uri)) {
@@ -956,6 +966,16 @@ public class WindowManagerService extends IWindowManager.Stub
mAtmService.mForceResizableActivities = forceResizable;
}
+ void updateDevelopmentOverrideDesktopExperience() {
+ ContentResolver resolver = mContext.getContentResolver();
+ final int overrideDesktopMode = Settings.Global.getInt(resolver,
+ Settings.Global.DEVELOPMENT_OVERRIDE_DESKTOP_EXPERIENCE_FEATURES,
+ DesktopModeFlags.ToggleOverride.OVERRIDE_UNSET.getSetting());
+
+ SystemProperties.set(DesktopModeFlags.SYSTEM_PROPERTY_NAME,
+ Integer.toString(overrideDesktopMode));
+ }
+
void updateDevEnableNonResizableMultiWindow() {
ContentResolver resolver = mContext.getContentResolver();
final boolean devEnableNonResizableMultiWindow = Settings.Global.getInt(resolver,
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
index affcfc14034e..379079a0018c 100644
--- a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionStopControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionStopControllerTest.java
@@ -300,6 +300,22 @@ public class MediaProjectionStopControllerTest {
}
@Test
+ public void isStopReasonCallEnd_stopReasonCallEnd_returnsTrue() {
+ boolean result =
+ mStopController.isStopReasonCallEnd(
+ MediaProjectionStopController.STOP_REASON_CALL_END);
+ assertThat(result).isTrue();
+ }
+
+ @Test
+ public void isStopReasonCallEnd_stopReasonKeyguard_returnsFalse() {
+ boolean result =
+ mStopController.isStopReasonCallEnd(
+ MediaProjectionStopController.STOP_REASON_KEYGUARD);
+ assertThat(result).isFalse();
+ }
+
+ @Test
@EnableFlags(
android.companion.virtualdevice.flags.Flags.FLAG_MEDIA_PROJECTION_KEYGUARD_RESTRICTIONS)
public void testKeyguardLockedStateChanged_unlocked() {
diff --git a/services/tests/wmtests/src/com/android/server/policy/SingleKeyGestureTests.java b/services/tests/wmtests/src/com/android/server/policy/SingleKeyGestureTests.java
index ff8b6d3c1962..08eb1451bd6f 100644
--- a/services/tests/wmtests/src/com/android/server/policy/SingleKeyGestureTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/SingleKeyGestureTests.java
@@ -23,6 +23,8 @@ import static android.view.KeyEvent.KEYCODE_POWER;
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+import static com.android.hardware.input.Flags.FLAG_ABORT_SLOW_MULTI_PRESS;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
@@ -35,9 +37,13 @@ import android.os.HandlerThread;
import android.os.Looper;
import android.os.Process;
import android.os.SystemClock;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.view.KeyEvent;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import java.util.concurrent.BlockingQueue;
@@ -52,6 +58,8 @@ import java.util.concurrent.TimeUnit;
* atest WmTests:SingleKeyGestureTests
*/
public class SingleKeyGestureTests {
+ @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
private SingleKeyGestureDetector mDetector;
private int mMaxMultiPressCount = 3;
@@ -260,6 +268,44 @@ public class SingleKeyGestureTests {
}
@Test
+ @EnableFlags(FLAG_ABORT_SLOW_MULTI_PRESS)
+ public void testMultipress_noLongPressBehavior_longPressCancelsMultiPress()
+ throws InterruptedException {
+ mLongPressOnPowerBehavior = false;
+
+ pressKey(KEYCODE_POWER, 0 /* pressTime */);
+ pressKey(KEYCODE_POWER, mLongPressTime /* pressTime */);
+
+ assertFalse(mMultiPressed.await(mWaitTimeout, TimeUnit.MILLISECONDS));
+ }
+
+ @Test
+ @EnableFlags(FLAG_ABORT_SLOW_MULTI_PRESS)
+ public void testMultipress_noVeryLongPressBehavior_veryLongPressCancelsMultiPress()
+ throws InterruptedException {
+ mLongPressOnPowerBehavior = false;
+ mVeryLongPressOnPowerBehavior = false;
+
+ pressKey(KEYCODE_POWER, 0 /* pressTime */);
+ pressKey(KEYCODE_POWER, mVeryLongPressTime /* pressTime */);
+
+ assertFalse(mMultiPressed.await(mWaitTimeout, TimeUnit.MILLISECONDS));
+ }
+
+ @Test
+ @DisableFlags(FLAG_ABORT_SLOW_MULTI_PRESS)
+ public void testMultipress_flagDisabled_noLongPressBehavior_longPressDoesNotCancelMultiPress()
+ throws InterruptedException {
+ mLongPressOnPowerBehavior = false;
+ mExpectedMultiPressCount = 2;
+
+ pressKey(KEYCODE_POWER, 0 /* pressTime */);
+ pressKey(KEYCODE_POWER, mLongPressTime /* pressTime */);
+
+ assertTrue(mMultiPressed.await(mWaitTimeout, TimeUnit.MILLISECONDS));
+ }
+
+ @Test
public void testMultiPress() throws InterruptedException {
// Double presses.
mExpectedMultiPressCount = 2;
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index d3f98d1a1d70..0b3d720bf52a 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -3219,7 +3219,6 @@ public class CarrierConfigManager {
* The roaming indicator will be shown if this is {@code true} and will not be shown if this is
* {@code false}.
*/
- @FlaggedApi(Flags.FLAG_HIDE_ROAMING_ICON)
public static final String KEY_SHOW_ROAMING_INDICATOR_BOOL = "show_roaming_indicator_bool";
/**